diff --git a/README.md b/README.md index ad87d60f85..6256647bd3 100644 --- a/README.md +++ b/README.md @@ -146,7 +146,7 @@ Other values are ignored. If you wish to use use a third-party network provider ### Configuring OVNKubernetes -OVNKubernetes supports the following configuration options, all of which are optional and once set at cluster creation, they can't be changed except for `gatewayConfig` and `IPsec` which can be changed at runtime: +OVNKubernetes supports the following configuration options, all of which are optional and once set at cluster creation, they can't be changed except for `gatewayConfig`, `IPsec` and `reachabilityTotalTimeoutSeconds` which can be changed at runtime: * `MTU`: The MTU to use for the geneve overlay. The default is the MTU of the node that the cluster-network-operator is first run on, minus 100 bytes for geneve overhead. If the nodes in your cluster don't all have the same MTU then you may need to set this explicitly. * `genevePort`: The UDP port to use for the Geneve overlay. The default is 6081. * `hybridOverlayConfig`: hybrid linux/windows cluster (see below). diff --git a/bindata/network/ovn-kubernetes/common/008-script-lib.yaml b/bindata/network/ovn-kubernetes/common/008-script-lib.yaml index 8276c773d5..b4c8b1840f 100644 --- a/bindata/network/ovn-kubernetes/common/008-script-lib.yaml +++ b/bindata/network/ovn-kubernetes/common/008-script-lib.yaml @@ -569,6 +569,11 @@ data: echo "I$(date "+%m%d %H:%M:%S.%N") - starting ovnkube-node" + ovn_eip_reachability_timeout_opt= + {{- if .ReachabilityTotalTimeoutSeconds }} + ovn_eip_reachability_timeout_opt="--egressip-reachability-total-timeout {{.ReachabilityTotalTimeoutSeconds}}" + {{ end }} + init_ovnkube_controller="--init-ovnkube-controller ${K8S_NODE}" gateway_interface="br-ex" OVN_NODE_MODE=${OVN_NODE_MODE:-full} @@ -746,5 +751,6 @@ data: ${ovn_v4_masquerade_subnet_opt} \ ${ovn_v6_masquerade_subnet_opt} \ ${ovn_v4_transit_switch_subnet_opt} \ - ${ovn_v6_transit_switch_subnet_opt} + ${ovn_v6_transit_switch_subnet_opt} \ + ${ovn_eip_reachability_timeout_opt} } diff --git a/bindata/network/ovn-kubernetes/managed/ovnkube-control-plane.yaml b/bindata/network/ovn-kubernetes/managed/ovnkube-control-plane.yaml index 55817fe7ce..2bf64ccc55 100644 --- a/bindata/network/ovn-kubernetes/managed/ovnkube-control-plane.yaml +++ b/bindata/network/ovn-kubernetes/managed/ovnkube-control-plane.yaml @@ -156,6 +156,11 @@ spec: fi done + ovn_eip_reachability_timeout_opt= + {{- if .ReachabilityTotalTimeoutSeconds }} + ovn_eip_reachability_timeout_opt="--egressip-reachability-total-timeout {{.ReachabilityTotalTimeoutSeconds}}" + {{ end }} + ovn_v4_join_subnet_opt= if [[ "{{.V4JoinSubnet}}" != "" ]]; then ovn_v4_join_subnet_opt="--gateway-v4-join-subnet {{.V4JoinSubnet}}" @@ -222,7 +227,8 @@ spec: ${ovn_v6_masquerade_subnet_opt} \ ${persistent_ips_enabled_flag} \ ${route_advertisements_enable_flag} \ - ${evpn_enable_flag} + ${evpn_enable_flag} \ + ${ovn_eip_reachability_timeout_opt} volumeMounts: - mountPath: /run/ovnkube-config/ name: ovnkube-config diff --git a/bindata/network/ovn-kubernetes/self-hosted/004-config.yaml b/bindata/network/ovn-kubernetes/self-hosted/004-config.yaml index 6c1944f7b8..23e3b1c589 100644 --- a/bindata/network/ovn-kubernetes/self-hosted/004-config.yaml +++ b/bindata/network/ovn-kubernetes/self-hosted/004-config.yaml @@ -40,9 +40,6 @@ data: enable-egress-firewall=true enable-egress-qos=true enable-egress-service=true - {{- if .ReachabilityTotalTimeoutSeconds }} - egressip-reachability-total-timeout={{.ReachabilityTotalTimeoutSeconds}} - {{- end }} {{- if .ReachabilityNodePort }} egressip-node-healthcheck-port={{.ReachabilityNodePort}} {{- end }} diff --git a/bindata/network/ovn-kubernetes/self-hosted/ovnkube-control-plane.yaml b/bindata/network/ovn-kubernetes/self-hosted/ovnkube-control-plane.yaml index 3521ed08ac..2f69ef19ef 100644 --- a/bindata/network/ovn-kubernetes/self-hosted/ovnkube-control-plane.yaml +++ b/bindata/network/ovn-kubernetes/self-hosted/ovnkube-control-plane.yaml @@ -102,6 +102,11 @@ spec: source "/env/_master" set +o allexport fi + + ovn_eip_reachability_timeout_opt= + {{- if .ReachabilityTotalTimeoutSeconds }} + ovn_eip_reachability_timeout_opt="--egressip-reachability-total-timeout {{.ReachabilityTotalTimeoutSeconds}}" + {{ end }} ovn_v4_join_subnet_opt= if [[ "{{.V4JoinSubnet}}" != "" ]]; then @@ -176,7 +181,8 @@ spec: ${persistent_ips_enabled_flag} \ ${gateway_mode_flags} \ ${route_advertisements_enable_flag} \ - ${evpn_enable_flag} + ${evpn_enable_flag} \ + ${ovn_eip_reachability_timeout_opt} volumeMounts: - mountPath: /run/ovnkube-config/ name: ovnkube-config diff --git a/pkg/network/ovn_kubernetes_test.go b/pkg/network/ovn_kubernetes_test.go index e091eff928..91c6a5eb0a 100644 --- a/pkg/network/ovn_kubernetes_test.go +++ b/pkg/network/ovn_kubernetes_test.go @@ -405,7 +405,6 @@ enable-egress-ip=true enable-egress-firewall=true enable-egress-qos=true enable-egress-service=true -egressip-reachability-total-timeout=3 egressip-node-healthcheck-port=9107 enable-multi-network=true enable-network-segmentation=true @@ -467,7 +466,6 @@ enable-egress-ip=true enable-egress-firewall=true enable-egress-qos=true enable-egress-service=true -egressip-reachability-total-timeout=0 egressip-node-healthcheck-port=9107 enable-multi-network=true enable-network-segmentation=true @@ -4001,6 +3999,151 @@ func TestRenderOVNKubernetesEnablePersistentIPs(t *testing.T) { g.Expect(objs).To(ContainElement(HaveKubernetesID("CustomResourceDefinition", "", "ipamclaims.k8s.cni.cncf.io"))) } +// TestRenderOVNKubernetesReachability tests egress IP reachability timeout rendering +func TestRenderOVNKubernetesReachability(t *testing.T) { + g := NewGomegaWithT(t) + + testCases := []struct { + name string + reachabilityTimeout *uint32 + expectKubernetesFeatureReachability bool + expectErr bool + }{ + { + name: "No reachability timeout (nil)", + reachabilityTimeout: nil, + expectKubernetesFeatureReachability: false, + expectErr: false, + }, + { + name: "Reachability timeout set to 0", + reachabilityTimeout: ptrToUint32(0), + expectKubernetesFeatureReachability: true, + expectErr: false, + }, + { + name: "Reachability timeout changed to 10", + reachabilityTimeout: ptrToUint32(10), + expectKubernetesFeatureReachability: true, + expectErr: false, + }, + { + name: "Reachability timeout unchanged to 10", + reachabilityTimeout: ptrToUint32(10), + expectKubernetesFeatureReachability: true, + expectErr: false, + }, + { + name: "Reachability timeout changed to 5", + reachabilityTimeout: ptrToUint32(5), + expectKubernetesFeatureReachability: true, + expectErr: false, + }, + { + name: "Reachability timeout disabled", + reachabilityTimeout: nil, + expectKubernetesFeatureReachability: false, + expectErr: false, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + crd := OVNKubernetesConfig.DeepCopy() + config := &crd.Spec + config.DefaultNetwork.OVNKubernetesConfig.EgressIPConfig.ReachabilityTotalTimeoutSeconds = tc.reachabilityTimeout + + errs := validateOVNKubernetes(config) + g.Expect(errs).To(HaveLen(0)) + fillDefaults(config, nil) + + // at the same time we have an upgrade + t.Setenv("RELEASE_VERSION", "2.0.0") + + bootstrapResult := fakeBootstrapResult() + bootstrapResult.OVN = bootstrap.OVNBootstrapResult{ + ControlPlaneReplicaCount: 3, + OVNKubernetesConfig: &bootstrap.OVNConfigBoostrapResult{ + DpuHostModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU_HOST, + DpuModeLabel: OVN_NODE_SELECTOR_DEFAULT_DPU, + SmartNicModeLabel: OVN_NODE_SELECTOR_DEFAULT_SMART_NIC, + MgmtPortResourceName: "", + HyperShiftConfig: &bootstrap.OVNHyperShiftBootstrapResult{ + Enabled: false, + }, + }, + } + + featureGatesCNO := getDefaultFeatureGates() + fakeClient := cnofake.NewFakeClient() + // Set is as Hypershift hosted control plane. + bootstrapResult.Infra = bootstrap.InfraStatus{} + bootstrapResult.Infra.HostedControlPlane = &hypershift.HostedControlPlane{} + objs, _, err := renderOVNKubernetes(config, bootstrapResult, manifestDirOvn, fakeClient, featureGatesCNO) + if tc.expectErr { + g.Expect(err).To(HaveOccurred()) + return + } + g.Expect(err).NotTo(HaveOccurred()) + + var configMap *uns.Unstructured + var scriptCP string + var scriptNode string + for _, obj := range objs { + // Gets script that starts ovnkube-control-plane pod, ovnkube-cluster-manager container + if obj.GetKind() == "Deployment" && obj.GetName() == "ovnkube-control-plane" && obj.GetNamespace() == "openshift-ovn-kubernetes" { + containers, found, err := uns.NestedSlice(obj.Object, "spec", "template", "spec", "containers") + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(found).To(BeTrue()) + for _, c := range containers { + cm := c.(map[string]interface{}) + if name, ok := cm["name"]; ok && name == "ovnkube-cluster-manager" { + command, found, err := uns.NestedSlice(cm, "command") + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(found).To(BeTrue()) + g.Expect(len(command)).To(BeNumerically(">", 2)) // Command options are set in the 3rd slice + scriptCP = command[2].(string) + break + } + } + } + + // Gets script that starts ovnkube-node pod, ovnkube-controller container + if obj.GetKind() == "ConfigMap" && obj.GetName() == "ovnkube-script-lib" { + configMap = obj + configMapData, found, err := uns.NestedStringMap(configMap.Object, "data") + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(found).To(BeTrue(), "ovnkube-script-lib ConfigMap should have data field") + scriptNode = configMapData["ovnkube-lib.sh"] + } + } + g.Expect(scriptCP).NotTo(BeEmpty()) + g.Expect(scriptNode).NotTo(BeEmpty()) + + if tc.expectKubernetesFeatureReachability { + g.Expect(scriptCP).To( + ContainSubstring(fmt.Sprintf("--egressip-reachability-total-timeout %d", *tc.reachabilityTimeout)), + "ovnkube-control-plane pod template should contain the configured reachability timeout value", + ) + g.Expect(scriptNode).To( + ContainSubstring(fmt.Sprintf("--egressip-reachability-total-timeout %d", *tc.reachabilityTimeout)), + "ovnkube-node pod template should contain the configured reachability timeout value", + ) + + } else { + g.Expect(scriptCP).NotTo( + ContainSubstring("--egressip-reachability-total-timeout"), + "ovnkube-control-plane pod template should not contain the configured reachability timeout value", + ) + g.Expect(scriptNode).NotTo( + ContainSubstring("--egressip-reachability-total-timeout"), + "ovnkube-node pod template should not contain the configured reachability timeout value", + ) + } + }) + } +} + type fakeClientReader struct { configMap *v1.ConfigMap }