diff --git a/api/cisco/nx/v1alpha1/bordergateway_types.go b/api/cisco/nx/v1alpha1/bordergateway_types.go
index c712be57..7b4fe386 100644
--- a/api/cisco/nx/v1alpha1/bordergateway_types.go
+++ b/api/cisco/nx/v1alpha1/bordergateway_types.go
@@ -164,6 +164,7 @@ type BorderGatewayStatus struct {
// +kubebuilder:printcolumn:name="Admin State",type=string,JSONPath=`.spec.adminState`
// +kubebuilder:printcolumn:name="Source Interface",type=string,JSONPath=`.spec.sourceInterfaceRef.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"
// BorderGateway is the Schema for the bordergateways API
diff --git a/api/cisco/nx/v1alpha1/system_types.go b/api/cisco/nx/v1alpha1/system_types.go
index 98d86596..f43716ff 100644
--- a/api/cisco/nx/v1alpha1/system_types.go
+++ b/api/cisco/nx/v1alpha1/system_types.go
@@ -60,6 +60,7 @@ type SystemStatus struct {
// +kubebuilder:resource:shortName=nxsystem
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.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"
// System is the Schema for the systems API
diff --git a/api/cisco/nx/v1alpha1/vpcdomain_types.go b/api/cisco/nx/v1alpha1/vpcdomain_types.go
index 47fc96f0..8f2ecc8f 100644
--- a/api/cisco/nx/v1alpha1/vpcdomain_types.go
+++ b/api/cisco/nx/v1alpha1/vpcdomain_types.go
@@ -257,6 +257,7 @@ const (
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Configured",type=string,JSONPath=`.status.conditions[?(@.type=="Configured")].status`,priority=1
// +kubebuilder:printcolumn:name="Operational",type=string,JSONPath=`.status.conditions[?(@.type=="Operational")].status`,priority=1
+// +kubebuilder:printcolumn:name="Paused",type=string,JSONPath=`.status.conditions[?(@.type=="Paused")].status`,priority=1
// +kubebuilder:printcolumn:name="Peer Status",type=string,JSONPath=`.status.peerStatus`,priority=1
// +kubebuilder:printcolumn:name="Role",type=string,JSONPath=`.status.role`,priority=1
// +kubebuilder:printcolumn:name="Peer Link Interface",type="string",JSONPath=".status.peerLinkIf",priority=1
diff --git a/api/core/v1alpha1/acl_types.go b/api/core/v1alpha1/acl_types.go
index 4a160ead..99c9a575 100644
--- a/api/core/v1alpha1/acl_types.go
+++ b/api/core/v1alpha1/acl_types.go
@@ -121,6 +121,7 @@ type AccessControlListStatus struct {
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Entries",type=string,JSONPath=`.status.entriesSummary`,priority=1
// +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"
// AccessControlList is the Schema for the accesscontrollists API
diff --git a/api/core/v1alpha1/banner_types.go b/api/core/v1alpha1/banner_types.go
index 26b40bac..3e591b69 100644
--- a/api/core/v1alpha1/banner_types.go
+++ b/api/core/v1alpha1/banner_types.go
@@ -66,6 +66,7 @@ type BannerStatus struct {
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.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"
// Banner is the Schema for the banners API
diff --git a/api/core/v1alpha1/bgp_peer_types.go b/api/core/v1alpha1/bgp_peer_types.go
index 28a83780..c51b082a 100644
--- a/api/core/v1alpha1/bgp_peer_types.go
+++ b/api/core/v1alpha1/bgp_peer_types.go
@@ -223,6 +223,7 @@ const (
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Configured",type=string,JSONPath=`.status.conditions[?(@.type=="Configured")].status`,priority=1
// +kubebuilder:printcolumn:name="Operational",type=string,JSONPath=`.status.conditions[?(@.type=="Operational")].status`,priority=1
+// +kubebuilder:printcolumn:name="Paused",type=string,JSONPath=`.status.conditions[?(@.type=="Paused")].status`,priority=1
// +kubebuilder:printcolumn:name="Session State",type=string,JSONPath=`.status.sessionState`,priority=1
// +kubebuilder:printcolumn:name="Last Established",type="date",JSONPath=`.status.lastEstablishedTime`,priority=1
// +kubebuilder:printcolumn:name="Advertised Prefixes",type=string,JSONPath=`.status.advertisedPrefixesSummary`,priority=1
diff --git a/api/core/v1alpha1/bgp_types.go b/api/core/v1alpha1/bgp_types.go
index 1c52b708..db1f94bb 100644
--- a/api/core/v1alpha1/bgp_types.go
+++ b/api/core/v1alpha1/bgp_types.go
@@ -154,6 +154,7 @@ type BGPStatus struct {
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Admin State",type=string,JSONPath=`.spec.adminState`
// +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"
// BGP is the Schema for the bgp API
diff --git a/api/core/v1alpha1/certificate_types.go b/api/core/v1alpha1/certificate_types.go
index c46f0295..758c00e3 100644
--- a/api/core/v1alpha1/certificate_types.go
+++ b/api/core/v1alpha1/certificate_types.go
@@ -56,6 +56,7 @@ type CertificateStatus struct {
// +kubebuilder:printcolumn:name="Certificate",type=string,JSONPath=`.spec.id`
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.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"
// Certificate is the Schema for the certificates API
diff --git a/api/core/v1alpha1/device_types.go b/api/core/v1alpha1/device_types.go
index 0b82c96c..3d2907c0 100644
--- a/api/core/v1alpha1/device_types.go
+++ b/api/core/v1alpha1/device_types.go
@@ -155,9 +155,9 @@ type DeviceStatus struct {
// +optional
Ports []DevicePort `json:"ports,omitempty"`
- // PostSummary shows a summary of the port configured, grouped by type, e.g. "1/4 (10g), 3/64 (100g)".
+ // PortSummary shows a summary of the port configured, grouped by type, e.g. "1/4 (10g), 3/64 (100g)".
// +optional
- PostSummary string `json:"portSummary,omitempty"`
+ PortSummary string `json:"portSummary,omitempty"`
// The conditions are a list of status objects that describe the state of the Device.
//+listType=map
@@ -276,9 +276,9 @@ const (
// +kubebuilder:printcolumn:name="SerialNumber",type=string,JSONPath=".status.serialNumber",priority=1
// +kubebuilder:printcolumn:name="FirmwareVersion",type=string,JSONPath=".status.firmwareVersion",priority=1
// +kubebuilder:printcolumn:name="Ports",type=string,JSONPath=".status.portSummary",priority=1
-// +kubebuilder:printcolumn:name="Paused",type=boolean,JSONPath=`.spec.paused`,priority=1
// +kubebuilder:printcolumn:name="Phase",type="string",JSONPath=".status.phase"
// +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"
// Device is the Schema for the devices API.
diff --git a/api/core/v1alpha1/dns_types.go b/api/core/v1alpha1/dns_types.go
index de54e565..b2d84b7e 100644
--- a/api/core/v1alpha1/dns_types.go
+++ b/api/core/v1alpha1/dns_types.go
@@ -82,6 +82,7 @@ type DNSStatus struct {
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Admin State",type=string,JSONPath=`.spec.adminState`
// +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"
// DNS is the Schema for the dns API
diff --git a/api/core/v1alpha1/evpninstance_types.go b/api/core/v1alpha1/evpninstance_types.go
index e5b92f1d..2141b695 100644
--- a/api/core/v1alpha1/evpninstance_types.go
+++ b/api/core/v1alpha1/evpninstance_types.go
@@ -118,6 +118,7 @@ type EVPNInstanceStatus struct {
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
// +kubebuilder:printcolumn:name="Route Distinguisher",type=string,JSONPath=`.spec.routeDistinguisher`,priority=1
// +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"
// EVPNInstance is the Schema for the evpninstances API
diff --git a/api/core/v1alpha1/groupversion_info.go b/api/core/v1alpha1/groupversion_info.go
index 9a86d27b..f5100391 100644
--- a/api/core/v1alpha1/groupversion_info.go
+++ b/api/core/v1alpha1/groupversion_info.go
@@ -108,6 +108,11 @@ const (
// be calculated by the controller based on child conditions, if present.
ReadyCondition = "Ready"
+ // PausedCondition indicates whether reconciliation is paused for an object.
+ // This condition is set to True when the parent Device has spec.paused=true
+ // or the object has the networking.metal.ironcore.dev/paused annotation.
+ PausedCondition = "Paused"
+
// ConfiguredCondition indicates whether the resource has been successfully configured.
// This condition indicates whether the desired configuration has been applied to the device
// (i.e., all necessary API calls succeeded).
@@ -128,6 +133,12 @@ const (
// NotReadyReason indicates that the resource is not ready for use.
NotReadyReason = "NotReady"
+ // PausedReason indicates that reconciliation is paused.
+ PausedReason = "Paused"
+
+ // NotPausedReason indicates that reconciliation is not paused.
+ NotPausedReason = "NotPaused"
+
// UnreachableReason indicates that the controller cannot reach the device.
UnreachableReason = "Unreachable"
diff --git a/api/core/v1alpha1/interface_types.go b/api/core/v1alpha1/interface_types.go
index f5c0727f..1f7c8ba0 100644
--- a/api/core/v1alpha1/interface_types.go
+++ b/api/core/v1alpha1/interface_types.go
@@ -344,6 +344,7 @@ type InterfaceStatus struct {
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Configured",type=string,JSONPath=`.status.conditions[?(@.type=="Configured")].status`,priority=1
// +kubebuilder:printcolumn:name="Operational",type=string,JSONPath=`.status.conditions[?(@.type=="Operational")].status`,priority=1
+// +kubebuilder:printcolumn:name="Paused",type=string,JSONPath=`.status.conditions[?(@.type=="Paused")].status`,priority=1
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// Interface is the Schema for the interfaces API.
diff --git a/api/core/v1alpha1/isis_types.go b/api/core/v1alpha1/isis_types.go
index bf624cde..41e3e370 100644
--- a/api/core/v1alpha1/isis_types.go
+++ b/api/core/v1alpha1/isis_types.go
@@ -111,6 +111,7 @@ type ISISStatus struct {
// +kubebuilder:printcolumn:name="NET",type=string,JSONPath=`.spec.networkEntityTitle`
// +kubebuilder:printcolumn:name="Level",type=string,JSONPath=`.spec.type`,priority=1
// +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"
// ISIS is the Schema for the isis API
diff --git a/api/core/v1alpha1/lldp_types.go b/api/core/v1alpha1/lldp_types.go
index f7da56b8..865f415d 100644
--- a/api/core/v1alpha1/lldp_types.go
+++ b/api/core/v1alpha1/lldp_types.go
@@ -72,6 +72,7 @@ type LLDPStatus struct {
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Configured",type=string,JSONPath=`.status.conditions[?(@.type=="Configured")].status`,priority=1
// +kubebuilder:printcolumn:name="Operational",type=string,JSONPath=`.status.conditions[?(@.type=="Operational")].status`,priority=1
+// +kubebuilder:printcolumn:name="Paused",type=string,JSONPath=`.status.conditions[?(@.type=="Paused")].status`,priority=1
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// LLDP is the Schema for the lldps API
diff --git a/api/core/v1alpha1/managementaccess_types.go b/api/core/v1alpha1/managementaccess_types.go
index dad88000..93bfbb46 100644
--- a/api/core/v1alpha1/managementaccess_types.go
+++ b/api/core/v1alpha1/managementaccess_types.go
@@ -139,6 +139,7 @@ type ManagementAccessStatus struct {
// +kubebuilder:printcolumn:name="gRPC",type=boolean,JSONPath=`.spec.grpc.enabled`
// +kubebuilder:printcolumn:name="gRPC Port",type=integer,JSONPath=`.spec.grpc.port`,priority=1
// +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"
// ManagementAccess is the Schema for the managementaccesses API
diff --git a/api/core/v1alpha1/ntp_types.go b/api/core/v1alpha1/ntp_types.go
index defdd7be..a72c6cd5 100644
--- a/api/core/v1alpha1/ntp_types.go
+++ b/api/core/v1alpha1/ntp_types.go
@@ -79,6 +79,7 @@ type NTPStatus struct {
// +kubebuilder:printcolumn:name="Admin State",type=string,JSONPath=`.spec.adminState`
// +kubebuilder:printcolumn:name="Source Interface",type=string,JSONPath=`.spec.sourceInterfaceName`
// +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"
// NTP is the Schema for the ntp API
diff --git a/api/core/v1alpha1/nve_types.go b/api/core/v1alpha1/nve_types.go
index ad68f170..7b761a69 100644
--- a/api/core/v1alpha1/nve_types.go
+++ b/api/core/v1alpha1/nve_types.go
@@ -133,6 +133,7 @@ type NetworkVirtualizationEdgeStatus struct {
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Configured",type=string,JSONPath=`.status.conditions[?(@.type=="Configured")].status`,priority=1
// +kubebuilder:printcolumn:name="Operational",type=string,JSONPath=`.status.conditions[?(@.type=="Operational")].status`,priority=1
+// +kubebuilder:printcolumn:name="Paused",type=string,JSONPath=`.status.conditions[?(@.type=="Paused")].status`,priority=1
// +kubebuilder:printcolumn:name="Source Interface",type=string,JSONPath=`.status.sourceInterfaceName`
// +kubebuilder:printcolumn:name="Anycast Interface",type=string,JSONPath=`.status.anycastSourceInterfaceName`
// +kubebuilder:printcolumn:name="HostReachability",type=string,JSONPath=`.status.hostReachability`
diff --git a/api/core/v1alpha1/ospf_types.go b/api/core/v1alpha1/ospf_types.go
index 50f34452..74cb2f3f 100644
--- a/api/core/v1alpha1/ospf_types.go
+++ b/api/core/v1alpha1/ospf_types.go
@@ -185,6 +185,7 @@ const (
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Configured",type=string,JSONPath=`.status.conditions[?(@.type=="Configured")].status`,priority=1
// +kubebuilder:printcolumn:name="Operational",type=string,JSONPath=`.status.conditions[?(@.type=="Operational")].status`,priority=1
+// +kubebuilder:printcolumn:name="Paused",type=string,JSONPath=`.status.conditions[?(@.type=="Paused")].status`,priority=1
// +kubebuilder:printcolumn:name="Adjacencies",type=string,JSONPath=`.status.adjacencySummary`,priority=1
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
diff --git a/api/core/v1alpha1/pim_types.go b/api/core/v1alpha1/pim_types.go
index d4b89ecb..cbec07bc 100644
--- a/api/core/v1alpha1/pim_types.go
+++ b/api/core/v1alpha1/pim_types.go
@@ -95,6 +95,7 @@ type PIMStatus struct {
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Admin State",type=string,JSONPath=`.spec.adminState`
// +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"
// PIM is the Schema for the pim API
diff --git a/api/core/v1alpha1/prefixset_types.go b/api/core/v1alpha1/prefixset_types.go
index 09721ec3..c0cfa18c 100644
--- a/api/core/v1alpha1/prefixset_types.go
+++ b/api/core/v1alpha1/prefixset_types.go
@@ -95,6 +95,7 @@ type PrefixSetStatus struct {
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Entries",type=string,JSONPath=`.status.entriesSummary`,priority=1
// +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"
// PrefixSet is the Schema for the prefixsets API
diff --git a/api/core/v1alpha1/routingpolicy_types.go b/api/core/v1alpha1/routingpolicy_types.go
index 4583854e..8fd4c5fa 100644
--- a/api/core/v1alpha1/routingpolicy_types.go
+++ b/api/core/v1alpha1/routingpolicy_types.go
@@ -151,6 +151,7 @@ type RoutingPolicyStatus struct {
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Statements",type=string,JSONPath=`.status.statementsSummary`,priority=1
// +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"
// RoutingPolicy is the Schema for the routingpolicies API
diff --git a/api/core/v1alpha1/snmp_types.go b/api/core/v1alpha1/snmp_types.go
index b8af0ea9..57b00587 100644
--- a/api/core/v1alpha1/snmp_types.go
+++ b/api/core/v1alpha1/snmp_types.go
@@ -133,6 +133,7 @@ type SNMPStatus struct {
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Source Interface",type=string,JSONPath=`.spec.sourceInterfaceName`
// +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"
// SNMP is the Schema for the snmp API
diff --git a/api/core/v1alpha1/syslog_types.go b/api/core/v1alpha1/syslog_types.go
index f489035f..680246c1 100644
--- a/api/core/v1alpha1/syslog_types.go
+++ b/api/core/v1alpha1/syslog_types.go
@@ -109,6 +109,7 @@ type SyslogStatus struct {
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Servers",type=string,JSONPath=`.status.serversSummary`,priority=1
// +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"
// Syslog is the Schema for the syslogs API
diff --git a/api/core/v1alpha1/user_types.go b/api/core/v1alpha1/user_types.go
index e5d00424..63a811b8 100644
--- a/api/core/v1alpha1/user_types.go
+++ b/api/core/v1alpha1/user_types.go
@@ -89,6 +89,7 @@ type UserStatus struct {
// +kubebuilder:printcolumn:name="Username",type=string,JSONPath=`.spec.username`
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.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"
// User is the Schema for the users API
diff --git a/api/core/v1alpha1/vlan_types.go b/api/core/v1alpha1/vlan_types.go
index 30f4f66c..cb2fc1d8 100644
--- a/api/core/v1alpha1/vlan_types.go
+++ b/api/core/v1alpha1/vlan_types.go
@@ -76,6 +76,7 @@ type VLANStatus struct {
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:printcolumn:name="Configured",type=string,JSONPath=`.status.conditions[?(@.type=="Configured")].status`,priority=1
// +kubebuilder:printcolumn:name="Operational",type=string,JSONPath=`.status.conditions[?(@.type=="Operational")].status`,priority=1
+// +kubebuilder:printcolumn:name="Paused",type=string,JSONPath=`.status.conditions[?(@.type=="Paused")].status`,priority=1
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// VLAN is the Schema for the vlans API
diff --git a/api/core/v1alpha1/vrf_types.go b/api/core/v1alpha1/vrf_types.go
index c312df3f..0bd7f437 100644
--- a/api/core/v1alpha1/vrf_types.go
+++ b/api/core/v1alpha1/vrf_types.go
@@ -119,6 +119,7 @@ type VRFStatus struct {
// +kubebuilder:printcolumn:name="Device",type=string,JSONPath=`.spec.deviceRef.name`
// +kubebuilder:printcolumn:name="Route Distinguisher",type=string,JSONPath=`.spec.routeDistinguisher`,priority=1
// +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"
// VRF is the Schema for the vrfs API
diff --git a/charts/network-operator/templates/crd/accesscontrollists.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/accesscontrollists.networking.metal.ironcore.dev.yaml
index 672cac92..4063fb5d 100644
--- a/charts/network-operator/templates/crd/accesscontrollists.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/accesscontrollists.networking.metal.ironcore.dev.yaml
@@ -33,6 +33,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/banners.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/banners.networking.metal.ironcore.dev.yaml
index 2b4fb2ae..613d6490 100644
--- a/charts/network-operator/templates/crd/banners.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/banners.networking.metal.ironcore.dev.yaml
@@ -27,6 +27,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/bgp.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/bgp.networking.metal.ironcore.dev.yaml
index 1d3e7206..bf50a828 100644
--- a/charts/network-operator/templates/crd/bgp.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/bgp.networking.metal.ironcore.dev.yaml
@@ -33,6 +33,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/bgppeers.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/bgppeers.networking.metal.ironcore.dev.yaml
index f4782199..335f511d 100644
--- a/charts/network-operator/templates/crd/bgppeers.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/bgppeers.networking.metal.ironcore.dev.yaml
@@ -44,6 +44,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .status.sessionState
name: Session State
priority: 1
diff --git a/charts/network-operator/templates/crd/bordergateways.nx.cisco.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/bordergateways.nx.cisco.networking.metal.ironcore.dev.yaml
index c17d0aef..6b789fcd 100644
--- a/charts/network-operator/templates/crd/bordergateways.nx.cisco.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/bordergateways.nx.cisco.networking.metal.ironcore.dev.yaml
@@ -35,6 +35,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/certificates.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/certificates.networking.metal.ironcore.dev.yaml
index ba6aa4e7..dcbf61f6 100644
--- a/charts/network-operator/templates/crd/certificates.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/certificates.networking.metal.ironcore.dev.yaml
@@ -30,6 +30,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/devices.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/devices.networking.metal.ironcore.dev.yaml
index af8e1204..e0132342 100644
--- a/charts/network-operator/templates/crd/devices.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/devices.networking.metal.ironcore.dev.yaml
@@ -43,16 +43,16 @@ spec:
name: Ports
priority: 1
type: string
- - jsonPath: .spec.paused
- name: Paused
- priority: 1
- type: boolean
- jsonPath: .status.phase
name: Phase
type: string
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
@@ -385,7 +385,7 @@ spec:
- Provisioned
type: string
portSummary:
- description: PostSummary shows a summary of the port configured, grouped
+ description: PortSummary shows a summary of the port configured, grouped
by type, e.g. "1/4 (10g), 3/64 (100g)".
type: string
ports:
diff --git a/charts/network-operator/templates/crd/dns.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/dns.networking.metal.ironcore.dev.yaml
index 1c04c44a..8d5bea94 100644
--- a/charts/network-operator/templates/crd/dns.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/dns.networking.metal.ironcore.dev.yaml
@@ -30,6 +30,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/evpninstances.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/evpninstances.networking.metal.ironcore.dev.yaml
index 0fe79be3..e773a1a3 100644
--- a/charts/network-operator/templates/crd/evpninstances.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/evpninstances.networking.metal.ironcore.dev.yaml
@@ -37,6 +37,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/interfaces.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/interfaces.networking.metal.ironcore.dev.yaml
index d329d1c1..dc8a28fd 100644
--- a/charts/network-operator/templates/crd/interfaces.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/interfaces.networking.metal.ironcore.dev.yaml
@@ -51,6 +51,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/isis.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/isis.networking.metal.ironcore.dev.yaml
index edfe6431..0f796aa6 100644
--- a/charts/network-operator/templates/crd/isis.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/isis.networking.metal.ironcore.dev.yaml
@@ -37,6 +37,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/lldps.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/lldps.networking.metal.ironcore.dev.yaml
index 044b2cd5..f7f97023 100644
--- a/charts/network-operator/templates/crd/lldps.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/lldps.networking.metal.ironcore.dev.yaml
@@ -35,6 +35,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/managementaccesses.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/managementaccesses.networking.metal.ironcore.dev.yaml
index 2eb61c00..dafbc712 100644
--- a/charts/network-operator/templates/crd/managementaccesses.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/managementaccesses.networking.metal.ironcore.dev.yaml
@@ -37,6 +37,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/networkvirtualizationedges.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/networkvirtualizationedges.networking.metal.ironcore.dev.yaml
index 24758fc0..786fb8c1 100644
--- a/charts/network-operator/templates/crd/networkvirtualizationedges.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/networkvirtualizationedges.networking.metal.ironcore.dev.yaml
@@ -38,6 +38,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .status.sourceInterfaceName
name: Source Interface
type: string
diff --git a/charts/network-operator/templates/crd/ntp.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/ntp.networking.metal.ironcore.dev.yaml
index 1f58092d..ad14d789 100644
--- a/charts/network-operator/templates/crd/ntp.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/ntp.networking.metal.ironcore.dev.yaml
@@ -30,6 +30,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/ospf.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/ospf.networking.metal.ironcore.dev.yaml
index e7461082..209fa6e7 100644
--- a/charts/network-operator/templates/crd/ospf.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/ospf.networking.metal.ironcore.dev.yaml
@@ -41,6 +41,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .status.adjacencySummary
name: Adjacencies
priority: 1
diff --git a/charts/network-operator/templates/crd/pim.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/pim.networking.metal.ironcore.dev.yaml
index cf79d63c..5e8f944f 100644
--- a/charts/network-operator/templates/crd/pim.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/pim.networking.metal.ironcore.dev.yaml
@@ -27,6 +27,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/prefixsets.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/prefixsets.networking.metal.ironcore.dev.yaml
index 5e8e4419..437571e4 100644
--- a/charts/network-operator/templates/crd/prefixsets.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/prefixsets.networking.metal.ironcore.dev.yaml
@@ -31,6 +31,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/routingpolicies.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/routingpolicies.networking.metal.ironcore.dev.yaml
index c072d158..ef7e2609 100644
--- a/charts/network-operator/templates/crd/routingpolicies.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/routingpolicies.networking.metal.ironcore.dev.yaml
@@ -33,6 +33,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/snmp.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/snmp.networking.metal.ironcore.dev.yaml
index 879ffd7c..30783899 100644
--- a/charts/network-operator/templates/crd/snmp.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/snmp.networking.metal.ironcore.dev.yaml
@@ -27,6 +27,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/syslogs.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/syslogs.networking.metal.ironcore.dev.yaml
index 8afe1c2b..828b5fce 100644
--- a/charts/network-operator/templates/crd/syslogs.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/syslogs.networking.metal.ironcore.dev.yaml
@@ -28,6 +28,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/systems.nx.cisco.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/systems.nx.cisco.networking.metal.ironcore.dev.yaml
index 24bfefa2..720f807c 100644
--- a/charts/network-operator/templates/crd/systems.nx.cisco.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/systems.nx.cisco.networking.metal.ironcore.dev.yaml
@@ -26,6 +26,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/users.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/users.networking.metal.ironcore.dev.yaml
index 6f9abc7f..37efd089 100644
--- a/charts/network-operator/templates/crd/users.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/users.networking.metal.ironcore.dev.yaml
@@ -27,6 +27,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/vlans.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/vlans.networking.metal.ironcore.dev.yaml
index 9bf403a3..b9071700 100644
--- a/charts/network-operator/templates/crd/vlans.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/vlans.networking.metal.ironcore.dev.yaml
@@ -42,6 +42,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/charts/network-operator/templates/crd/vpcdomains.nx.cisco.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/vpcdomains.nx.cisco.networking.metal.ironcore.dev.yaml
index ea67ac16..c5395b25 100644
--- a/charts/network-operator/templates/crd/vpcdomains.nx.cisco.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/vpcdomains.nx.cisco.networking.metal.ironcore.dev.yaml
@@ -40,6 +40,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .status.peerStatus
name: Peer Status
priority: 1
diff --git a/charts/network-operator/templates/crd/vrfs.networking.metal.ironcore.dev.yaml b/charts/network-operator/templates/crd/vrfs.networking.metal.ironcore.dev.yaml
index a46114c3..0aa3a1c5 100644
--- a/charts/network-operator/templates/crd/vrfs.networking.metal.ironcore.dev.yaml
+++ b/charts/network-operator/templates/crd/vrfs.networking.metal.ironcore.dev.yaml
@@ -31,6 +31,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_accesscontrollists.yaml b/config/crd/bases/networking.metal.ironcore.dev_accesscontrollists.yaml
index bc2f2645..5729c4aa 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_accesscontrollists.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_accesscontrollists.yaml
@@ -30,6 +30,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_banners.yaml b/config/crd/bases/networking.metal.ironcore.dev_banners.yaml
index eab0b691..a6bfea19 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_banners.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_banners.yaml
@@ -24,6 +24,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_bgp.yaml b/config/crd/bases/networking.metal.ironcore.dev_bgp.yaml
index 69870194..95faa75e 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_bgp.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_bgp.yaml
@@ -30,6 +30,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_bgppeers.yaml b/config/crd/bases/networking.metal.ironcore.dev_bgppeers.yaml
index 6fc9be75..22e3ffd8 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_bgppeers.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_bgppeers.yaml
@@ -41,6 +41,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .status.sessionState
name: Session State
priority: 1
diff --git a/config/crd/bases/networking.metal.ironcore.dev_certificates.yaml b/config/crd/bases/networking.metal.ironcore.dev_certificates.yaml
index d6614907..8d397448 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_certificates.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_certificates.yaml
@@ -27,6 +27,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_devices.yaml b/config/crd/bases/networking.metal.ironcore.dev_devices.yaml
index 62b3b46e..556e1674 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_devices.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_devices.yaml
@@ -40,16 +40,16 @@ spec:
name: Ports
priority: 1
type: string
- - jsonPath: .spec.paused
- name: Paused
- priority: 1
- type: boolean
- jsonPath: .status.phase
name: Phase
type: string
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
@@ -382,7 +382,7 @@ spec:
- Provisioned
type: string
portSummary:
- description: PostSummary shows a summary of the port configured, grouped
+ description: PortSummary shows a summary of the port configured, grouped
by type, e.g. "1/4 (10g), 3/64 (100g)".
type: string
ports:
diff --git a/config/crd/bases/networking.metal.ironcore.dev_dns.yaml b/config/crd/bases/networking.metal.ironcore.dev_dns.yaml
index 086b6d54..f683b39e 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_dns.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_dns.yaml
@@ -27,6 +27,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_evpninstances.yaml b/config/crd/bases/networking.metal.ironcore.dev_evpninstances.yaml
index 4a89e2f0..8ea5092b 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_evpninstances.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_evpninstances.yaml
@@ -34,6 +34,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_interfaces.yaml b/config/crd/bases/networking.metal.ironcore.dev_interfaces.yaml
index dc496169..4a95d6c6 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_interfaces.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_interfaces.yaml
@@ -48,6 +48,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_isis.yaml b/config/crd/bases/networking.metal.ironcore.dev_isis.yaml
index 025d1587..8fb69c40 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_isis.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_isis.yaml
@@ -34,6 +34,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_lldps.yaml b/config/crd/bases/networking.metal.ironcore.dev_lldps.yaml
index 53368a01..267ebb72 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_lldps.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_lldps.yaml
@@ -32,6 +32,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_managementaccesses.yaml b/config/crd/bases/networking.metal.ironcore.dev_managementaccesses.yaml
index 8c44f2cb..2e794954 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_managementaccesses.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_managementaccesses.yaml
@@ -34,6 +34,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_networkvirtualizationedges.yaml b/config/crd/bases/networking.metal.ironcore.dev_networkvirtualizationedges.yaml
index 3699e34f..ba093f8d 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_networkvirtualizationedges.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_networkvirtualizationedges.yaml
@@ -35,6 +35,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .status.sourceInterfaceName
name: Source Interface
type: string
diff --git a/config/crd/bases/networking.metal.ironcore.dev_ntp.yaml b/config/crd/bases/networking.metal.ironcore.dev_ntp.yaml
index 5c9d21e3..acba2571 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_ntp.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_ntp.yaml
@@ -27,6 +27,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_ospf.yaml b/config/crd/bases/networking.metal.ironcore.dev_ospf.yaml
index fdb8cfe8..98ca1fa0 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_ospf.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_ospf.yaml
@@ -38,6 +38,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .status.adjacencySummary
name: Adjacencies
priority: 1
diff --git a/config/crd/bases/networking.metal.ironcore.dev_pim.yaml b/config/crd/bases/networking.metal.ironcore.dev_pim.yaml
index 2a04e539..f2da9362 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_pim.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_pim.yaml
@@ -24,6 +24,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_prefixsets.yaml b/config/crd/bases/networking.metal.ironcore.dev_prefixsets.yaml
index d13cd77f..148942c7 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_prefixsets.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_prefixsets.yaml
@@ -28,6 +28,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_routingpolicies.yaml b/config/crd/bases/networking.metal.ironcore.dev_routingpolicies.yaml
index 608bc64c..288f3df3 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_routingpolicies.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_routingpolicies.yaml
@@ -30,6 +30,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_snmp.yaml b/config/crd/bases/networking.metal.ironcore.dev_snmp.yaml
index c0adc2f2..b8c5f402 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_snmp.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_snmp.yaml
@@ -24,6 +24,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_syslogs.yaml b/config/crd/bases/networking.metal.ironcore.dev_syslogs.yaml
index 7b00364b..ae12d918 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_syslogs.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_syslogs.yaml
@@ -25,6 +25,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_users.yaml b/config/crd/bases/networking.metal.ironcore.dev_users.yaml
index 837680c1..90f0e4a9 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_users.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_users.yaml
@@ -24,6 +24,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_vlans.yaml b/config/crd/bases/networking.metal.ironcore.dev_vlans.yaml
index acbfd095..eeaebaa2 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_vlans.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_vlans.yaml
@@ -39,6 +39,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/networking.metal.ironcore.dev_vrfs.yaml b/config/crd/bases/networking.metal.ironcore.dev_vrfs.yaml
index 21bdcb5f..1f4c62c8 100644
--- a/config/crd/bases/networking.metal.ironcore.dev_vrfs.yaml
+++ b/config/crd/bases/networking.metal.ironcore.dev_vrfs.yaml
@@ -28,6 +28,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_bordergateways.yaml b/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_bordergateways.yaml
index f03a5f2e..9d33376a 100644
--- a/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_bordergateways.yaml
+++ b/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_bordergateways.yaml
@@ -32,6 +32,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_systems.yaml b/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_systems.yaml
index 6afb22e2..b7265af0 100644
--- a/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_systems.yaml
+++ b/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_systems.yaml
@@ -23,6 +23,10 @@ spec:
- jsonPath: .status.conditions[?(@.type=="Ready")].status
name: Ready
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
diff --git a/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_vpcdomains.yaml b/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_vpcdomains.yaml
index ad3dd0d7..371cee81 100644
--- a/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_vpcdomains.yaml
+++ b/config/crd/bases/nx.cisco.networking.metal.ironcore.dev_vpcdomains.yaml
@@ -37,6 +37,10 @@ spec:
name: Operational
priority: 1
type: string
+ - jsonPath: .status.conditions[?(@.type=="Paused")].status
+ name: Paused
+ priority: 1
+ type: string
- jsonPath: .status.peerStatus
name: Peer Status
priority: 1
diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts
index 3ba467ec..d4660e6b 100644
--- a/docs/.vitepress/config.mts
+++ b/docs/.vitepress/config.mts
@@ -33,6 +33,7 @@ export default withMermaid({
text: 'Documentation',
items: [
{ text: 'Overview', link: '/overview' },
+ { text: 'Concepts', link: '/concepts/' },
{ text: 'Tutorials', link: '/tutorials/' },
{ text: 'API References', link: '/api-reference/' },
],
@@ -70,6 +71,13 @@ export default withMermaid({
text: 'Overview',
items: [{ text: 'Index', link: '/overview/' }],
},
+ {
+ text: 'Concepts',
+ items: [
+ { text: 'Index', link: '/concepts/' },
+ { text: 'Pausing Reconciliation', link: '/concepts/pausing' },
+ ],
+ },
{
text: 'Tutorials',
items: [
diff --git a/docs/api-reference/index.md b/docs/api-reference/index.md
index 06e61d0d..5b827127 100644
--- a/docs/api-reference/index.md
+++ b/docs/api-reference/index.md
@@ -920,7 +920,7 @@ _Appears in:_
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
-| `paused` _boolean_ | Paused can be used to prevent controllers from processing the Device and its associated objects. | | Optional: \{\}
|
+| `paused` _boolean_ | Paused can be used to prevent controllers from processing the Device and its associated objects. | false | Optional: \{\}
|
| `endpoint` _[Endpoint](#endpoint)_ | Endpoint contains the connection information for the device. | | Required: \{\}
|
| `provisioning` _[Provisioning](#provisioning)_ | Provisioning is an optional configuration for the device provisioning process.
It can be used to provide initial configuration templates or scripts that are applied during the device provisioning. | | Optional: \{\}
|
@@ -945,7 +945,7 @@ _Appears in:_
| `firmwareVersion` _string_ | FirmwareVersion is the firmware version running on the Device. | | Optional: \{\}
|
| `provisioning` _[ProvisioningInfo](#provisioninginfo) array_ | Provisioning is the list of provisioning attempts for the Device. | | Optional: \{\}
|
| `ports` _[DevicePort](#deviceport) array_ | Ports is the list of ports on the Device. | | Optional: \{\}
|
-| `portSummary` _string_ | PostSummary shows a summary of the port configured, grouped by type, e.g. "1/4 (10g), 3/64 (100g)". | | Optional: \{\}
|
+| `portSummary` _string_ | PortSummary shows a summary of the port configured, grouped by type, e.g. "1/4 (10g), 3/64 (100g)". | | Optional: \{\}
|
| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#condition-v1-meta) array_ | The conditions are a list of status objects that describe the state of the Device. | | Optional: \{\}
|
diff --git a/docs/concepts/index.md b/docs/concepts/index.md
new file mode 100644
index 00000000..05b3eb2f
--- /dev/null
+++ b/docs/concepts/index.md
@@ -0,0 +1,5 @@
+# Concepts
+
+This section covers the core concepts behind the Network Operator.
+
+- [Pausing Reconciliation](./pausing.md) — Temporarily prevent controllers from reconciling resources.
diff --git a/docs/concepts/pausing.md b/docs/concepts/pausing.md
new file mode 100644
index 00000000..064b6660
--- /dev/null
+++ b/docs/concepts/pausing.md
@@ -0,0 +1,103 @@
+# Pausing Reconciliation
+
+Network operators may need to temporarily prevent controllers from reconciling
+resources — for example during maintenance windows or manual debugging sessions.
+
+## Pausing a Device
+
+Setting `spec.paused: true` on a Device pauses reconciliation of the Device
+**and all of its child resources** (Interfaces, VRFs, VLANs, BGP, etc.).
+
+```yaml
+apiVersion: networking.metal.ironcore.dev/v1alpha1
+kind: Device
+metadata:
+ name: leaf-01
+spec:
+ paused: true
+ endpoint:
+ address: 10.0.0.1
+```
+
+## Pausing Individual Resources
+
+The `networking.metal.ironcore.dev/paused` annotation can be applied to any
+resource to pause its reconciliation independently of the parent Device.
+
+```yaml
+apiVersion: networking.metal.ironcore.dev/v1alpha1
+kind: VRF
+metadata:
+ name: vrf-prod
+ annotations:
+ networking.metal.ironcore.dev/paused: "true"
+spec:
+ deviceRef:
+ name: leaf-01
+ name: prod
+```
+
+::: tip
+You can quickly pause and unpause a resource using `kubectl annotate`:
+```bash
+# Pause
+kubectl annotate vrf vrf-prod networking.metal.ironcore.dev/paused=true
+
+# Unpause
+kubectl annotate vrf vrf-prod networking.metal.ironcore.dev/paused-
+```
+:::
+
+## Paused Condition
+
+Every resource reflects its pause state in `.status.conditions` with a `Paused`
+condition. The `Paused` column is visible with `-o wide`:
+
+```
+$ kubectl get vrfs -o wide
+NAME VRF DEVICE READY PAUSED AGE
+vrf-prod prod leaf-01 Unknown True 5m
+```
+
+The condition message indicates the reason:
+
+```yaml
+conditions:
+ - type: Paused
+ status: "True"
+ reason: Paused
+ message: "Device spec.paused is set to true"
+```
+
+## Effect on the Ready Condition
+
+When a resource is paused, the `Ready` condition is set to `Unknown` with reason `Paused`.
+
+```yaml
+conditions:
+ - type: Ready
+ status: "Unknown"
+ reason: Paused
+ message: "Reconciliation is paused"
+ - type: Paused
+ status: "True"
+ reason: Paused
+ message: "Device spec.paused is set to true"
+```
+
+The `kubectl get` output reflects this:
+
+```
+$ kubectl get vrfs -o wide
+NAME VRF DEVICE READY PAUSED AGE
+vrf-prod prod leaf-01 Unknown True 5m
+```
+
+Once the resource is unpaused, the controller runs a full reconcile and
+immediately sets `Ready` back to `True` or `False` based on the observed state.
+
+::: info Why Unknown and not False?
+`False` would imply the resource is broken. `Unknown` is the honest signal:
+the operator has stopped actively verifying the resource, so its current
+state is simply not known.
+:::
diff --git a/internal/annotations/annotations.go b/internal/annotations/annotations.go
deleted file mode 100644
index 0393d26f..00000000
--- a/internal/annotations/annotations.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors
-// SPDX-License-Identifier: Apache-2.0
-
-// Package annotations implements annotation helper functions.
-package annotations
-
-import (
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
- "github.com/ironcore-dev/network-operator/api/core/v1alpha1"
-)
-
-// IsPaused returns true if the Device is paused or the object has the [v1alpha1.PausedAnnotation].
-func IsPaused(device *v1alpha1.Device, obj metav1.Object) bool {
- return device.Spec.Paused || HasPaused(obj)
-}
-
-// HasPaused returns true if the object has the [v1alpha1.PausedAnnotation].
-func HasPaused(obj metav1.Object) bool {
- return Has(obj, v1alpha1.PausedAnnotation)
-}
-
-// Has returns true if the object has the specified annotation.
-func Has(obj metav1.Object, annotation string) bool {
- _, ok := obj.GetAnnotations()[annotation]
- return ok
-}
diff --git a/internal/conditions/conditions.go b/internal/conditions/conditions.go
index ab3f56bd..c9bced73 100644
--- a/internal/conditions/conditions.go
+++ b/internal/conditions/conditions.go
@@ -30,6 +30,17 @@ type Setter interface {
SetConditions([]metav1.Condition)
}
+// Get finds and returns the condition with the given type from the target object.
+// Returns nil if the condition is not found.
+func Get(target Getter, conditionType string) *metav1.Condition {
+ return meta.FindStatusCondition(target.GetConditions(), conditionType)
+}
+
+// GetTopLevelCondition finds and returns the top level "Ready" condition.
+func GetTopLevelCondition(target Getter) *metav1.Condition {
+ return Get(target, v1alpha1.ReadyCondition)
+}
+
// Set adds or updates a condition on the target object.
// It returns true if the condition was changed, false otherwise.
func Set(target Setter, condition metav1.Condition) (changed bool) {
@@ -73,7 +84,7 @@ func IsReady(target Getter) bool {
// IsConfigured looks at the [v1alpha1.ConfiguredCondition] condition type and returns true
// if that condition is set to true and the observed generation matches the object's generation.
func IsConfigured(target Getter) bool {
- condition := meta.FindStatusCondition(target.GetConditions(), v1alpha1.ConfiguredCondition)
+ condition := Get(target, v1alpha1.ConfiguredCondition)
if condition == nil {
return false
}
@@ -83,11 +94,6 @@ func IsConfigured(target Getter) bool {
return condition.Status == metav1.ConditionTrue
}
-// GetTopLevelCondition finds and returns the top level condition (Ready Condition).
-func GetTopLevelCondition(target Getter) *metav1.Condition {
- return meta.FindStatusCondition(target.GetConditions(), v1alpha1.ReadyCondition)
-}
-
// InitializeConditions updates all conditions to Unknown if not set.
func InitializeConditions(target Setter, types ...string) (changed bool) {
conditions := target.GetConditions()
@@ -116,7 +122,7 @@ func RecomputeReady(target Setter) (changed bool) {
conditions := target.GetConditions()
for _, condition := range conditions {
- if condition.Type != v1alpha1.ReadyCondition && condition.Status != metav1.ConditionTrue {
+ if condition.Type != v1alpha1.ReadyCondition && condition.Type != v1alpha1.PausedCondition && condition.Status != metav1.ConditionTrue {
status = metav1.ConditionFalse
reason = v1alpha1.NotReadyReason
message = "One or more conditions are not ready"
diff --git a/internal/controller/cisco/nx/bordergateway_controller.go b/internal/controller/cisco/nx/bordergateway_controller.go
index 9da39c73..51458a45 100644
--- a/internal/controller/cisco/nx/bordergateway_controller.go
+++ b/internal/controller/cisco/nx/bordergateway_controller.go
@@ -29,9 +29,9 @@ import (
nxv1alpha1 "github.com/ironcore-dev/network-operator/api/cisco/nx/v1alpha1"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/provider/cisco/nxos"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
@@ -104,9 +104,8 @@ func (r *BorderGatewayReconciler) Reconcile(ctx context.Context, req ctrl.Reques
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "cisco-nx-border-gateway-controller"); err != nil {
diff --git a/internal/controller/cisco/nx/bordergateway_controller_test.go b/internal/controller/cisco/nx/bordergateway_controller_test.go
index 106b2b7b..bc6e27b9 100644
--- a/internal/controller/cisco/nx/bordergateway_controller_test.go
+++ b/internal/controller/cisco/nx/bordergateway_controller_test.go
@@ -124,9 +124,11 @@ var _ = Describe("BorderGateway Controller", func() {
Eventually(func(g Gomega) {
resource := &nxv1alpha1.BorderGateway{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/cisco/nx/system_controller.go b/internal/controller/cisco/nx/system_controller.go
index f82dbf14..27129133 100644
--- a/internal/controller/cisco/nx/system_controller.go
+++ b/internal/controller/cisco/nx/system_controller.go
@@ -27,9 +27,9 @@ import (
nxv1alpha1 "github.com/ironcore-dev/network-operator/api/cisco/nx/v1alpha1"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -101,9 +101,8 @@ func (r *SystemReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "cisco-nx-system-controller"); err != nil {
diff --git a/internal/controller/cisco/nx/system_controller_test.go b/internal/controller/cisco/nx/system_controller_test.go
index ede427ca..1553a5c6 100644
--- a/internal/controller/cisco/nx/system_controller_test.go
+++ b/internal/controller/cisco/nx/system_controller_test.go
@@ -103,9 +103,11 @@ var _ = Describe("System Controller", func() {
Eventually(func(g Gomega) {
resource := &nxv1alpha1.System{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/cisco/nx/vpcdomain_controller.go b/internal/controller/cisco/nx/vpcdomain_controller.go
index 5252a732..115d282e 100644
--- a/internal/controller/cisco/nx/vpcdomain_controller.go
+++ b/internal/controller/cisco/nx/vpcdomain_controller.go
@@ -28,8 +28,8 @@ import (
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
@@ -114,9 +114,8 @@ func (r *VPCDomainReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "cisco-nx-vpcdomain-controller"); err != nil {
diff --git a/internal/controller/cisco/nx/vpcdomain_controller_test.go b/internal/controller/cisco/nx/vpcdomain_controller_test.go
index 3a13a1e2..7fd2243c 100644
--- a/internal/controller/cisco/nx/vpcdomain_controller_test.go
+++ b/internal/controller/cisco/nx/vpcdomain_controller_test.go
@@ -170,13 +170,15 @@ var _ = Describe("VPCDomain Controller", func() {
Eventually(func(g Gomega) {
resource := &nxv1.VPCDomain{}
g.Expect(k8sClient.Get(ctx, vpcdomainKey, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(corev1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(corev1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(corev1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(corev1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
@@ -339,12 +341,14 @@ var _ = Describe("VPCDomain Controller", func() {
Eventually(func(g Gomega) {
resource := &nxv1.VPCDomain{}
g.Expect(k8sClient.Get(ctx, vpcdomainKey, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(2))
+ g.Expect(resource.Status.Conditions).To(HaveLen(3))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(corev1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(corev1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(corev1.WaitingForDependenciesReason))
+ g.Expect(resource.Status.Conditions[2].Type).To(Equal(corev1.PausedCondition))
+ g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -379,12 +383,14 @@ var _ = Describe("VPCDomain Controller", func() {
Eventually(func(g Gomega) {
resource := &nxv1.VPCDomain{}
g.Expect(k8sClient.Get(ctx, vpcdomainKey, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(2))
+ g.Expect(resource.Status.Conditions).To(HaveLen(3))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(corev1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(corev1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(corev1.InvalidInterfaceTypeReason))
+ g.Expect(resource.Status.Conditions[2].Type).To(Equal(corev1.PausedCondition))
+ g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -419,12 +425,14 @@ var _ = Describe("VPCDomain Controller", func() {
Eventually(func(g Gomega) {
resource := &nxv1.VPCDomain{}
g.Expect(k8sClient.Get(ctx, vpcdomainKey, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(2))
+ g.Expect(resource.Status.Conditions).To(HaveLen(3))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(corev1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(corev1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(corev1.CrossDeviceReferenceReason))
+ g.Expect(resource.Status.Conditions[2].Type).To(Equal(corev1.PausedCondition))
+ g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -459,12 +467,14 @@ var _ = Describe("VPCDomain Controller", func() {
Eventually(func(g Gomega) {
resource := &nxv1.VPCDomain{}
g.Expect(k8sClient.Get(ctx, vpcdomainKey, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(2))
+ g.Expect(resource.Status.Conditions).To(HaveLen(3))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(corev1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(corev1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(corev1.WaitingForDependenciesReason))
+ g.Expect(resource.Status.Conditions[2].Type).To(Equal(corev1.PausedCondition))
+ g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -499,12 +509,14 @@ var _ = Describe("VPCDomain Controller", func() {
Eventually(func(g Gomega) {
resource := &nxv1.VPCDomain{}
g.Expect(k8sClient.Get(ctx, vpcdomainKey, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(2))
+ g.Expect(resource.Status.Conditions).To(HaveLen(3))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(corev1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(corev1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(corev1.CrossDeviceReferenceReason))
+ g.Expect(resource.Status.Conditions[2].Type).To(Equal(corev1.PausedCondition))
+ g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
})
diff --git a/internal/controller/core/acl_controller.go b/internal/controller/core/acl_controller.go
index 397842f3..47d1b31d 100644
--- a/internal/controller/core/acl_controller.go
+++ b/internal/controller/core/acl_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -103,9 +103,8 @@ func (r *AccessControlListReconciler) Reconcile(ctx context.Context, req ctrl.Re
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "acl-controller"); err != nil {
diff --git a/internal/controller/core/acl_controller_test.go b/internal/controller/core/acl_controller_test.go
index 77b47d46..dda2bd84 100644
--- a/internal/controller/core/acl_controller_test.go
+++ b/internal/controller/core/acl_controller_test.go
@@ -120,9 +120,11 @@ var _ = Describe("AccessControlList Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.AccessControlList{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/banner_controller.go b/internal/controller/core/banner_controller.go
index 1a3cc77b..1ccd246a 100644
--- a/internal/controller/core/banner_controller.go
+++ b/internal/controller/core/banner_controller.go
@@ -30,10 +30,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/clientutil"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -107,9 +107,8 @@ func (r *BannerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "banner-controller"); err != nil {
diff --git a/internal/controller/core/banner_controller_test.go b/internal/controller/core/banner_controller_test.go
index 93b4cf30..0a944ce9 100644
--- a/internal/controller/core/banner_controller_test.go
+++ b/internal/controller/core/banner_controller_test.go
@@ -104,9 +104,11 @@ var _ = Describe("Banner Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Banner{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
@@ -163,9 +165,11 @@ var _ = Describe("Banner Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Banner{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/bgp_controller.go b/internal/controller/core/bgp_controller.go
index 70d137d2..24fe0f1b 100644
--- a/internal/controller/core/bgp_controller.go
+++ b/internal/controller/core/bgp_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -107,9 +107,8 @@ func (r *BGPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "bgp-controller"); err != nil {
diff --git a/internal/controller/core/bgp_controller_test.go b/internal/controller/core/bgp_controller_test.go
index 82fa5cdd..2cf5534d 100644
--- a/internal/controller/core/bgp_controller_test.go
+++ b/internal/controller/core/bgp_controller_test.go
@@ -102,9 +102,11 @@ var _ = Describe("BGP Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.BGP{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/bgp_peer_controller.go b/internal/controller/core/bgp_peer_controller.go
index b23ce666..65714394 100644
--- a/internal/controller/core/bgp_peer_controller.go
+++ b/internal/controller/core/bgp_peer_controller.go
@@ -32,9 +32,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -110,9 +110,8 @@ func (r *BGPPeerReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "bgppeer-controller"); err != nil {
diff --git a/internal/controller/core/bgp_peer_controller_test.go b/internal/controller/core/bgp_peer_controller_test.go
index ac01e3c4..36cae963 100644
--- a/internal/controller/core/bgp_peer_controller_test.go
+++ b/internal/controller/core/bgp_peer_controller_test.go
@@ -102,13 +102,15 @@ var _ = Describe("BGPPeer Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.BGPPeer{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Verifying the BGP peer is configured in the provider")
@@ -154,13 +156,15 @@ var _ = Describe("BGPPeer Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.BGPPeer{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Verifying the BGP peer is configured in the provider")
@@ -191,7 +195,7 @@ var _ = Describe("BGPPeer Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.BGPPeer{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -199,6 +203,8 @@ var _ = Describe("BGPPeer Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.InterfaceNotFoundReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -239,7 +245,7 @@ var _ = Describe("BGPPeer Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.BGPPeer{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -247,6 +253,8 @@ var _ = Describe("BGPPeer Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.CrossDeviceReferenceReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
})
diff --git a/internal/controller/core/certificate_controller.go b/internal/controller/core/certificate_controller.go
index c5ae21cf..12c2e657 100644
--- a/internal/controller/core/certificate_controller.go
+++ b/internal/controller/core/certificate_controller.go
@@ -30,10 +30,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/clientutil"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -106,9 +106,8 @@ func (r *CertificateReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "certificate-controller"); err != nil {
diff --git a/internal/controller/core/certificate_controller_test.go b/internal/controller/core/certificate_controller_test.go
index abd8f648..8d345b18 100644
--- a/internal/controller/core/certificate_controller_test.go
+++ b/internal/controller/core/certificate_controller_test.go
@@ -129,9 +129,11 @@ var _ = Describe("Certificate Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Certificate{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/device_controller.go b/internal/controller/core/device_controller.go
index 853717f5..1f3c0b7b 100644
--- a/internal/controller/core/device_controller.go
+++ b/internal/controller/core/device_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
)
@@ -94,9 +94,8 @@ func (r *DeviceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c
return ctrl.Result{}, err
}
- if annotations.IsPaused(obj, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, obj, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
conn, err := deviceutil.GetDeviceConnection(ctx, r, obj)
@@ -300,7 +299,7 @@ func (r *DeviceReconciler) reconcile(ctx context.Context, device *v1alpha1.Devic
slices.Sort(device.Status.Ports[i].SupportedSpeedsGbps)
}
- device.Status.PostSummary = PortSummary(device.Status.Ports)
+ device.Status.PortSummary = PortSummary(device.Status.Ports)
info, err := prov.GetDeviceInfo(ctx)
if err != nil {
@@ -323,9 +322,6 @@ func (r *DeviceReconciler) reconcile(ctx context.Context, device *v1alpha1.Devic
}
func (r *DeviceReconciler) reconcileMaintenance(ctx context.Context, obj *v1alpha1.Device, prov provider.DeviceProvider, conn *deviceutil.Connection) error {
- if obj.Annotations == nil {
- return nil
- }
action, ok := obj.Annotations[v1alpha1.DeviceMaintenanceAnnotation]
if !ok {
return nil
diff --git a/internal/controller/core/device_controller_test.go b/internal/controller/core/device_controller_test.go
index b12071e3..46a47658 100644
--- a/internal/controller/core/device_controller_test.go
+++ b/internal/controller/core/device_controller_test.go
@@ -80,10 +80,12 @@ var _ = Describe("Device Controller", func() {
resource := &v1alpha1.Device{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
g.Expect(resource.Status.Phase).To(Equal(v1alpha1.DevicePhaseRunning))
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[0].Reason).To(Equal(v1alpha1.ReadyReason))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Creating the custom resource for the Kind Interface")
@@ -109,9 +111,11 @@ var _ = Describe("Device Controller", func() {
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
g.Expect(resource.Status.Phase).To(Equal(v1alpha1.DevicePhaseRunning))
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Manufacturer).To(Equal("Manufacturer"))
g.Expect(resource.Status.Model).To(Equal("Model"))
@@ -125,7 +129,7 @@ var _ = Describe("Device Controller", func() {
g.Expect(resource.Status.Ports[0].Transceiver).To(Equal("QSFP-DD"))
g.Expect(resource.Status.Ports[0].InterfaceRef).ToNot(BeNil())
g.Expect(resource.Status.Ports[0].InterfaceRef.Name).To(Equal(name))
- g.Expect(resource.Status.PostSummary).To(Equal("1/8 (10g)"))
+ g.Expect(resource.Status.PortSummary).To(Equal("1/8 (10g)"))
}).Should(Succeed())
By("Cleanup the specific resource instance Interface")
@@ -168,10 +172,12 @@ var _ = Describe("Device Controller", func() {
resource := &v1alpha1.Device{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
g.Expect(resource.Status.Phase).To(Equal(v1alpha1.DevicePhaseProvisioning))
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[0].Reason).To(Equal(v1alpha1.ProvisioningReason))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -219,10 +225,12 @@ var _ = Describe("Device Controller", func() {
resource := &v1alpha1.Device{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
g.Expect(resource.Status.Phase).To(Equal(v1alpha1.DevicePhaseRunning))
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[0].Reason).To(Equal(v1alpha1.ReadyReason))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -259,8 +267,10 @@ var _ = Describe("Device Controller", func() {
resource := &v1alpha1.Device{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
g.Expect(resource.Status.Phase).To(Equal(v1alpha1.DevicePhaseProvisioning))
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Setting the device to Running phase")
@@ -273,8 +283,10 @@ var _ = Describe("Device Controller", func() {
resource := &v1alpha1.Device{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
g.Expect(resource.Status.Phase).To(Equal(v1alpha1.DevicePhaseRunning))
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Adding the reset-phase annotation to the device")
diff --git a/internal/controller/core/dns_controller.go b/internal/controller/core/dns_controller.go
index 6367e70d..c84ffaf3 100644
--- a/internal/controller/core/dns_controller.go
+++ b/internal/controller/core/dns_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -103,9 +103,8 @@ func (r *DNSReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "dns-controller"); err != nil {
diff --git a/internal/controller/core/dns_controller_test.go b/internal/controller/core/dns_controller_test.go
index e3e93e16..13fa2450 100644
--- a/internal/controller/core/dns_controller_test.go
+++ b/internal/controller/core/dns_controller_test.go
@@ -106,9 +106,11 @@ var _ = Describe("DNS Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.DNS{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/evpninstance_controller.go b/internal/controller/core/evpninstance_controller.go
index 83a5b0ee..d2191a76 100644
--- a/internal/controller/core/evpninstance_controller.go
+++ b/internal/controller/core/evpninstance_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -105,9 +105,8 @@ func (r *EVPNInstanceReconciler) Reconcile(ctx context.Context, req ctrl.Request
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "evpn-instance-controller"); err != nil {
diff --git a/internal/controller/core/evpninstance_controller_test.go b/internal/controller/core/evpninstance_controller_test.go
index 5adb7515..0b3ca6eb 100644
--- a/internal/controller/core/evpninstance_controller_test.go
+++ b/internal/controller/core/evpninstance_controller_test.go
@@ -128,9 +128,11 @@ var _ = Describe("EVPNInstance Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.EVPNInstance{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Verifying the VLAN is labeled with L2VNI label")
@@ -182,10 +184,12 @@ var _ = Describe("EVPNInstance Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.EVPNInstance{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[0].Reason).To(Equal(v1alpha1.VLANNotFoundReason))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -232,10 +236,12 @@ var _ = Describe("EVPNInstance Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.EVPNInstance{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[0].Reason).To(Equal(v1alpha1.CrossDeviceReferenceReason))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
})
diff --git a/internal/controller/core/interface_controller.go b/internal/controller/core/interface_controller.go
index 22ebedd9..d47932f3 100644
--- a/internal/controller/core/interface_controller.go
+++ b/internal/controller/core/interface_controller.go
@@ -31,9 +31,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -112,9 +112,8 @@ func (r *InterfaceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "interface-controller"); err != nil {
diff --git a/internal/controller/core/interface_controller_test.go b/internal/controller/core/interface_controller_test.go
index b09383fd..d1cfac08 100644
--- a/internal/controller/core/interface_controller_test.go
+++ b/internal/controller/core/interface_controller_test.go
@@ -124,13 +124,15 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Verifying the Interface is configured in the provider")
@@ -182,13 +184,15 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -232,7 +236,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -240,6 +244,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.CrossDeviceReferenceReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -268,7 +274,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -276,6 +282,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.InterfaceNotFoundReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -341,13 +349,15 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Verifying member interfaces are properly linked")
@@ -399,7 +409,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -407,6 +417,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.InterfaceNotFoundReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -453,7 +465,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -461,6 +473,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.CrossDeviceReferenceReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -512,7 +526,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -520,6 +534,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.MemberInterfaceAlreadyInUseReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -566,7 +582,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -574,6 +590,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.InvalidInterfaceTypeReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -620,7 +638,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -628,6 +646,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.InvalidInterfaceTypeReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -672,13 +692,15 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Verifying the VLAN status is updated with RoutedBy reference")
@@ -719,7 +741,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -727,6 +749,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.VLANNotFoundReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -769,7 +793,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -777,6 +801,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.CrossDeviceReferenceReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -819,13 +845,15 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Verifying the Interface has the VRF label")
@@ -865,7 +893,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -873,6 +901,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.VRFNotFoundReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -914,7 +944,7 @@ var _ = Describe("Interface Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Interface{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
@@ -922,6 +952,8 @@ var _ = Describe("Interface Controller", func() {
g.Expect(resource.Status.Conditions[1].Reason).To(Equal(v1alpha1.CrossDeviceReferenceReason))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionUnknown))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
})
diff --git a/internal/controller/core/isis_controller.go b/internal/controller/core/isis_controller.go
index 2be7bef1..8b0df54e 100644
--- a/internal/controller/core/isis_controller.go
+++ b/internal/controller/core/isis_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -107,9 +107,8 @@ func (r *ISISReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "isis-controller"); err != nil {
diff --git a/internal/controller/core/isis_controller_test.go b/internal/controller/core/isis_controller_test.go
index 9ece97d1..3331cf3e 100644
--- a/internal/controller/core/isis_controller_test.go
+++ b/internal/controller/core/isis_controller_test.go
@@ -107,9 +107,11 @@ var _ = Describe("ISIS Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.ISIS{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/lldp_controller.go b/internal/controller/core/lldp_controller.go
index df25315c..56424786 100644
--- a/internal/controller/core/lldp_controller.go
+++ b/internal/controller/core/lldp_controller.go
@@ -31,9 +31,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -104,9 +104,8 @@ func (r *LLDPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
// Prevent concurrent reconciliations of resources targeting the same device
diff --git a/internal/controller/core/lldp_controller_test.go b/internal/controller/core/lldp_controller_test.go
index 68b2a40f..cf630b1d 100644
--- a/internal/controller/core/lldp_controller_test.go
+++ b/internal/controller/core/lldp_controller_test.go
@@ -111,7 +111,7 @@ var _ = Describe("LLDP Controller", func() {
Eventually(func(g Gomega) {
lldp = &v1alpha1.LLDP{}
g.Expect(k8sClient.Get(ctx, resourceKey, lldp)).To(Succeed())
- g.Expect(lldp.Status.Conditions).To(HaveLen(3))
+ g.Expect(lldp.Status.Conditions).To(HaveLen(4))
cond := meta.FindStatusCondition(lldp.Status.Conditions, v1alpha1.ReadyCondition)
g.Expect(cond).ToNot(BeNil())
@@ -160,7 +160,7 @@ var _ = Describe("LLDP Controller", func() {
Eventually(func(g Gomega) {
lldp = &v1alpha1.LLDP{}
g.Expect(k8sClient.Get(ctx, resourceKey, lldp)).To(Succeed())
- g.Expect(lldp.Status.Conditions).To(HaveLen(3))
+ g.Expect(lldp.Status.Conditions).To(HaveLen(4))
cond := meta.FindStatusCondition(lldp.Status.Conditions, v1alpha1.ReadyCondition)
g.Expect(cond).ToNot(BeNil())
diff --git a/internal/controller/core/managementaccess_controller.go b/internal/controller/core/managementaccess_controller.go
index 78097eba..c9924be7 100644
--- a/internal/controller/core/managementaccess_controller.go
+++ b/internal/controller/core/managementaccess_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -103,9 +103,8 @@ func (r *ManagementAccessReconciler) Reconcile(ctx context.Context, req ctrl.Req
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "managementaccess-controller"); err != nil {
diff --git a/internal/controller/core/managementaccess_controller_test.go b/internal/controller/core/managementaccess_controller_test.go
index ab0d7cb7..bc52d747 100644
--- a/internal/controller/core/managementaccess_controller_test.go
+++ b/internal/controller/core/managementaccess_controller_test.go
@@ -104,9 +104,11 @@ var _ = Describe("ManagementAccess Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.ManagementAccess{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/ntp_controller.go b/internal/controller/core/ntp_controller.go
index ad2d29ff..d0a43417 100644
--- a/internal/controller/core/ntp_controller.go
+++ b/internal/controller/core/ntp_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -103,9 +103,8 @@ func (r *NTPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "ntp-controller"); err != nil {
diff --git a/internal/controller/core/ntp_controller_test.go b/internal/controller/core/ntp_controller_test.go
index 4b21987a..c51dab2b 100644
--- a/internal/controller/core/ntp_controller_test.go
+++ b/internal/controller/core/ntp_controller_test.go
@@ -107,9 +107,11 @@ var _ = Describe("NTP Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.NTP{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/nve_controller.go b/internal/controller/core/nve_controller.go
index 42f02e28..e2f55f3d 100644
--- a/internal/controller/core/nve_controller.go
+++ b/internal/controller/core/nve_controller.go
@@ -31,9 +31,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -104,9 +104,8 @@ func (r *NetworkVirtualizationEdgeReconciler) Reconcile(ctx context.Context, req
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "nve-controller"); err != nil {
diff --git a/internal/controller/core/nve_controller_test.go b/internal/controller/core/nve_controller_test.go
index cf77197b..0ce53e62 100644
--- a/internal/controller/core/nve_controller_test.go
+++ b/internal/controller/core/nve_controller_test.go
@@ -135,13 +135,15 @@ var _ = Describe("NVE Controller", func() {
By("Updating the resource status")
Eventually(func(g Gomega) {
g.Expect(k8sClient.Get(ctx, nveKey, nve)).To(Succeed())
- g.Expect(nve.Status.Conditions).To(HaveLen(3))
+ g.Expect(nve.Status.Conditions).To(HaveLen(4))
g.Expect(nve.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(nve.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(nve.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(nve.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(nve.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(nve.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(nve.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(nve.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the NVE is created in the provider")
@@ -173,13 +175,15 @@ var _ = Describe("NVE Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.NetworkVirtualizationEdge{}
g.Expect(k8sClient.Get(ctx, nveKey, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
})
diff --git a/internal/controller/core/ospf_controller.go b/internal/controller/core/ospf_controller.go
index 54ef5398..86e70c6b 100644
--- a/internal/controller/core/ospf_controller.go
+++ b/internal/controller/core/ospf_controller.go
@@ -32,9 +32,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -110,9 +110,8 @@ func (r *OSPFReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "ospf-controller"); err != nil {
diff --git a/internal/controller/core/ospf_controller_test.go b/internal/controller/core/ospf_controller_test.go
index 423e33a4..18399169 100644
--- a/internal/controller/core/ospf_controller_test.go
+++ b/internal/controller/core/ospf_controller_test.go
@@ -102,13 +102,15 @@ var _ = Describe("OSPF Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.OSPF{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/pim_controller.go b/internal/controller/core/pim_controller.go
index 141b08f6..b444b288 100644
--- a/internal/controller/core/pim_controller.go
+++ b/internal/controller/core/pim_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -107,9 +107,8 @@ func (r *PIMReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "pim-controller"); err != nil {
diff --git a/internal/controller/core/pim_controller_test.go b/internal/controller/core/pim_controller_test.go
index f419f959..7db0d762 100644
--- a/internal/controller/core/pim_controller_test.go
+++ b/internal/controller/core/pim_controller_test.go
@@ -100,9 +100,11 @@ var _ = Describe("PIM Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.PIM{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/prefixset_controller.go b/internal/controller/core/prefixset_controller.go
index 23341a68..9ee8299c 100644
--- a/internal/controller/core/prefixset_controller.go
+++ b/internal/controller/core/prefixset_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -103,9 +103,8 @@ func (r *PrefixSetReconciler) Reconcile(ctx context.Context, req ctrl.Request) (
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "prefixset-controller"); err != nil {
diff --git a/internal/controller/core/prefixset_controller_test.go b/internal/controller/core/prefixset_controller_test.go
index 1dc946c6..5560d2af 100644
--- a/internal/controller/core/prefixset_controller_test.go
+++ b/internal/controller/core/prefixset_controller_test.go
@@ -111,9 +111,11 @@ var _ = Describe("PrefixSet Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.PrefixSet{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/routingpolicy_controller.go b/internal/controller/core/routingpolicy_controller.go
index 49c2028d..6faa037a 100644
--- a/internal/controller/core/routingpolicy_controller.go
+++ b/internal/controller/core/routingpolicy_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -103,9 +103,8 @@ func (r *RoutingPolicyReconciler) Reconcile(ctx context.Context, req ctrl.Reques
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "routingpolicy-controller"); err != nil {
diff --git a/internal/controller/core/routingpolicy_controller_test.go b/internal/controller/core/routingpolicy_controller_test.go
index a4d25465..11f387b5 100644
--- a/internal/controller/core/routingpolicy_controller_test.go
+++ b/internal/controller/core/routingpolicy_controller_test.go
@@ -115,9 +115,11 @@ var _ = Describe("RoutingPolicy Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.RoutingPolicy{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
@@ -184,9 +186,11 @@ var _ = Describe("RoutingPolicy Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.RoutingPolicy{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Verifying the RoutingPolicy is configured in the provider")
@@ -226,10 +230,12 @@ var _ = Describe("RoutingPolicy Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.RoutingPolicy{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[0].Reason).To(Equal(v1alpha1.PrefixSetNotFoundReason))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
@@ -283,10 +289,12 @@ var _ = Describe("RoutingPolicy Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.RoutingPolicy{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionFalse))
g.Expect(resource.Status.Conditions[0].Reason).To(Equal(v1alpha1.CrossDeviceReferenceReason))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
})
})
diff --git a/internal/controller/core/snmp_controller.go b/internal/controller/core/snmp_controller.go
index 4583de4b..f01fbf3f 100644
--- a/internal/controller/core/snmp_controller.go
+++ b/internal/controller/core/snmp_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -103,9 +103,8 @@ func (r *SNMPReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "snmp-controller"); err != nil {
diff --git a/internal/controller/core/snmp_controller_test.go b/internal/controller/core/snmp_controller_test.go
index b29ccfdb..8a32f1f4 100644
--- a/internal/controller/core/snmp_controller_test.go
+++ b/internal/controller/core/snmp_controller_test.go
@@ -118,9 +118,11 @@ var _ = Describe("SNMP Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.SNMP{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/syslog_controller.go b/internal/controller/core/syslog_controller.go
index 5ce5460a..44baaead 100644
--- a/internal/controller/core/syslog_controller.go
+++ b/internal/controller/core/syslog_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -103,9 +103,8 @@ func (r *SyslogReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ c
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "syslog-controller"); err != nil {
diff --git a/internal/controller/core/syslog_controller_test.go b/internal/controller/core/syslog_controller_test.go
index 700fabbf..fc16816b 100644
--- a/internal/controller/core/syslog_controller_test.go
+++ b/internal/controller/core/syslog_controller_test.go
@@ -113,9 +113,11 @@ var _ = Describe("Syslog Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.Syslog{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/user_controller.go b/internal/controller/core/user_controller.go
index f963eb35..81295fd3 100644
--- a/internal/controller/core/user_controller.go
+++ b/internal/controller/core/user_controller.go
@@ -30,10 +30,10 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/clientutil"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -106,9 +106,8 @@ func (r *UserReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "user-controller"); err != nil {
diff --git a/internal/controller/core/user_controller_test.go b/internal/controller/core/user_controller_test.go
index 20d6bee6..308660ea 100644
--- a/internal/controller/core/user_controller_test.go
+++ b/internal/controller/core/user_controller_test.go
@@ -123,9 +123,11 @@ var _ = Describe("User Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.User{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(1))
+ g.Expect(resource.Status.Conditions).To(HaveLen(2))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/vlan_controller.go b/internal/controller/core/vlan_controller.go
index 091a7b71..9fbbec75 100644
--- a/internal/controller/core/vlan_controller.go
+++ b/internal/controller/core/vlan_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -107,9 +107,8 @@ func (r *VLANReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctr
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "vlan-controller"); err != nil {
diff --git a/internal/controller/core/vlan_controller_test.go b/internal/controller/core/vlan_controller_test.go
index b635265d..51a078a4 100644
--- a/internal/controller/core/vlan_controller_test.go
+++ b/internal/controller/core/vlan_controller_test.go
@@ -103,13 +103,15 @@ var _ = Describe("VLAN Controller", func() {
Eventually(func(g Gomega) {
resource := &v1alpha1.VLAN{}
g.Expect(k8sClient.Get(ctx, key, resource)).To(Succeed())
- g.Expect(resource.Status.Conditions).To(HaveLen(3))
+ g.Expect(resource.Status.Conditions).To(HaveLen(4))
g.Expect(resource.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(resource.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[1].Type).To(Equal(v1alpha1.ConfiguredCondition))
g.Expect(resource.Status.Conditions[1].Status).To(Equal(metav1.ConditionTrue))
g.Expect(resource.Status.Conditions[2].Type).To(Equal(v1alpha1.OperationalCondition))
g.Expect(resource.Status.Conditions[2].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(resource.Status.Conditions[3].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(resource.Status.Conditions[3].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the resource is created in the provider")
diff --git a/internal/controller/core/vrf_controller.go b/internal/controller/core/vrf_controller.go
index 2914f63a..8d763d96 100644
--- a/internal/controller/core/vrf_controller.go
+++ b/internal/controller/core/vrf_controller.go
@@ -29,9 +29,9 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"github.com/ironcore-dev/network-operator/api/core/v1alpha1"
- "github.com/ironcore-dev/network-operator/internal/annotations"
"github.com/ironcore-dev/network-operator/internal/conditions"
"github.com/ironcore-dev/network-operator/internal/deviceutil"
+ "github.com/ironcore-dev/network-operator/internal/paused"
"github.com/ironcore-dev/network-operator/internal/provider"
"github.com/ironcore-dev/network-operator/internal/resourcelock"
)
@@ -109,9 +109,8 @@ func (r *VRFReconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl
return ctrl.Result{}, err
}
- if annotations.IsPaused(device, obj) {
- log.Info("Reconciliation is paused for this object")
- return ctrl.Result{}, nil
+ if isPaused, requeue, err := paused.EnsureCondition(ctx, r.Client, device, obj); isPaused || requeue || err != nil {
+ return ctrl.Result{Requeue: requeue}, err
}
if err := r.Locker.AcquireLock(ctx, device.Name, "vrf-controller"); err != nil {
diff --git a/internal/controller/core/vrf_controller_test.go b/internal/controller/core/vrf_controller_test.go
index 4e208779..58ecb862 100644
--- a/internal/controller/core/vrf_controller_test.go
+++ b/internal/controller/core/vrf_controller_test.go
@@ -115,9 +115,11 @@ var _ = Describe("VRF Controller", func() {
By("Updating the resource status")
Eventually(func(g Gomega) {
g.Expect(k8sClient.Get(ctx, key, vrf)).To(Succeed())
- g.Expect(vrf.Status.Conditions).To(HaveLen(1))
+ g.Expect(vrf.Status.Conditions).To(HaveLen(2))
g.Expect(vrf.Status.Conditions[0].Type).To(Equal(v1alpha1.ReadyCondition))
g.Expect(vrf.Status.Conditions[0].Status).To(Equal(metav1.ConditionTrue))
+ g.Expect(vrf.Status.Conditions[1].Type).To(Equal(v1alpha1.PausedCondition))
+ g.Expect(vrf.Status.Conditions[1].Status).To(Equal(metav1.ConditionFalse))
}).Should(Succeed())
By("Ensuring the VRF is created in the provider")
diff --git a/internal/paused/paused.go b/internal/paused/paused.go
new file mode 100644
index 00000000..2954d429
--- /dev/null
+++ b/internal/paused/paused.go
@@ -0,0 +1,96 @@
+// SPDX-FileCopyrightText: 2025 SAP SE or an SAP affiliate company and IronCore contributors
+// SPDX-License-Identifier: Apache-2.0
+
+// Package paused implements helper functions for managing the Paused condition on API objects.
+package paused
+
+import (
+ "context"
+ "fmt"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ "github.com/ironcore-dev/network-operator/api/core/v1alpha1"
+ "github.com/ironcore-dev/network-operator/internal/conditions"
+)
+
+// Object combines [client.Object] with [conditions.Setter].
+type Object interface {
+ client.Object
+ conditions.Setter
+}
+
+// EnsureCondition computes and patches the "Paused" condition on the object.
+// It returns whether the object is paused, whether the caller should requeue,
+// and any error encountered while patching.
+func EnsureCondition(ctx context.Context, c client.Client, device *v1alpha1.Device, obj Object) (isPaused, requeue bool, err error) {
+ log := ctrl.LoggerFrom(ctx)
+
+ oldCondition := conditions.Get(obj, v1alpha1.PausedCondition)
+ newCondition := computeCondition(device, obj)
+
+ isPaused = newCondition.Status == metav1.ConditionTrue
+ statusChanged := oldCondition == nil || oldCondition.Status != newCondition.Status
+
+ switch {
+ case statusChanged && isPaused:
+ log.Info("Pausing reconciliation for this object", "reason", newCondition.Message)
+ case statusChanged && !isPaused:
+ log.Info("Unpausing reconciliation for this object")
+ case !statusChanged && isPaused:
+ log.V(4).Info("Reconciliation is paused for this object", "reason", newCondition.Message)
+ }
+
+ // Set Ready=Unknown while paused: the operator is no longer actively
+ // verifying the resource, so its state cannot be determined.
+ if isPaused {
+ conditions.Set(obj, metav1.Condition{
+ Type: v1alpha1.ReadyCondition,
+ Status: metav1.ConditionUnknown,
+ Reason: v1alpha1.PausedReason,
+ Message: "Reconciliation is paused",
+ })
+ }
+
+ // Only do a standalone status patch when pausing. When not paused,
+ // the condition is set in-memory and will be persisted by the normal
+ // reconciliation status update, avoiding an unnecessary extra reconcile.
+ orig := obj.DeepCopyObject().(client.Object)
+ if changed := conditions.Set(obj, newCondition); !changed || !isPaused {
+ return isPaused, false, nil
+ }
+
+ if err = c.Status().Patch(ctx, obj, client.MergeFrom(orig)); err != nil {
+ return isPaused, false, err
+ }
+
+ return isPaused, true, nil
+}
+
+// computeCondition builds the Paused condition based on [v1alpha1.Device.Spec.Paused]
+// and the presence of the [v1alpha1.PausedAnnotation] on the object.
+func computeCondition(device *v1alpha1.Device, obj Object) metav1.Condition {
+ condition := metav1.Condition{
+ Type: v1alpha1.PausedCondition,
+ Status: metav1.ConditionFalse,
+ Reason: v1alpha1.NotPausedReason,
+ ObservedGeneration: obj.GetGeneration(),
+ }
+
+ if device != nil && device.Spec.Paused {
+ condition.Status = metav1.ConditionTrue
+ condition.Reason = v1alpha1.PausedReason
+ condition.Message = "Device spec.paused is set to true"
+ return condition
+ }
+
+ if _, ok := obj.GetAnnotations()[v1alpha1.PausedAnnotation]; ok {
+ condition.Status = metav1.ConditionTrue
+ condition.Reason = v1alpha1.PausedReason
+ condition.Message = fmt.Sprintf("%s has the %s annotation", obj.GetObjectKind().GroupVersionKind().Kind, v1alpha1.PausedAnnotation)
+ }
+
+ return condition
+}