diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index eb523b3d6e..ad404d04c8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -483,14 +483,15 @@ jobs: - {"target": "network-segmentation", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv6", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones"} - {"target": "bgp", "ha": "noHA", "gateway-mode": "local", "ipfamily": "dualstack", "disable-snat-multiple-gws": "snatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "routeadvertisements": "advertise-default", "network-segmentation": "enable-network-segmentation", "dns-name-resolver": "enable-dns-name-resolver"} - {"target": "bgp", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "dualstack", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "routeadvertisements": "advertise-default", "network-segmentation": "enable-network-segmentation", "dns-name-resolver": "enable-dns-name-resolver"} + - {"target": "bgp-loose-isolation", "ha": "noHA", "gateway-mode": "shared", "ipfamily": "dualstack", "disable-snat-multiple-gws": "snatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "routeadvertisements": "advertise-default", "network-segmentation": "enable-network-segmentation", "advertised-udn-isolation-mode": "loose"} - {"target": "traffic-flow-test-only","ha": "noHA", "gateway-mode": "shared", "ipfamily": "ipv4", "disable-snat-multiple-gws": "noSnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "traffic-flow-tests": "1-24", "network-segmentation": "enable-network-segmentation"} - {"target": "tools", "ha": "noHA", "gateway-mode": "local", "ipfamily": "dualstack", "disable-snat-multiple-gws": "SnatGW", "second-bridge": "1br", "ic": "ic-single-node-zones", "network-segmentation": "enable-network-segmentation"} needs: [ build-pr ] env: JOB_NAME: "${{ matrix.target }}-${{ matrix.ha }}-${{ matrix.gateway-mode }}-${{ matrix.ipfamily }}-${{ matrix.disable-snat-multiple-gws }}-${{ matrix.second-bridge }}-${{ matrix.ic }}" OVN_HYBRID_OVERLAY_ENABLE: ${{ (matrix.target == 'control-plane' || matrix.target == 'control-plane-helm') && (matrix.ipfamily == 'ipv4' || matrix.ipfamily == 'dualstack' ) }} - OVN_MULTICAST_ENABLE: "${{ matrix.target == 'control-plane' || matrix.target == 'control-plane-helm' || matrix.target == 'network-segmentation' || matrix.target == 'bgp' }}" - OVN_EMPTY_LB_EVENTS: "${{ matrix.target == 'control-plane' || matrix.target == 'control-plane-helm' || matrix.target == 'bgp' }}" + OVN_MULTICAST_ENABLE: "${{ matrix.target == 'control-plane' || matrix.target == 'control-plane-helm' || matrix.target == 'network-segmentation' || matrix.target == 'bgp' || matrix.target == 'bgp-loose-isolation' }}" + OVN_EMPTY_LB_EVENTS: "${{ matrix.target == 'control-plane' || matrix.target == 'control-plane-helm' || matrix.target == 'bgp' || matrix.target == 'bgp-loose-isolation' }}" OVN_HA: "${{ matrix.ha == 'HA' }}" OVN_DISABLE_SNAT_MULTIPLE_GWS: "${{ matrix.disable-snat-multiple-gws == 'noSnatGW' }}" KIND_INSTALL_METALLB: "${{ matrix.target == 'control-plane' || matrix.target == 'control-plane-helm' || matrix.target == 'network-segmentation' }}" @@ -514,6 +515,7 @@ jobs: ENABLE_ROUTE_ADVERTISEMENTS: "${{ matrix.routeadvertisements != '' }}" ADVERTISE_DEFAULT_NETWORK: "${{ matrix.routeadvertisements == 'advertise-default' }}" ENABLE_PRE_CONF_UDN_ADDR: "${{ matrix.ic == 'ic-single-node-zones' && (matrix.target == 'network-segmentation' || matrix.network-segmentation == 'enable-network-segmentation') }}" + ADVERTISED_UDN_ISOLATION_MODE: "${{ matrix.advertised-udn-isolation-mode }}" steps: - name: Install VRF kernel module @@ -647,7 +649,7 @@ jobs: # set 3 hours for control-plane tests as these might take a while # give 10m extra to give ginkgo chance to timeout before github so that we # get its output - timeout-minutes: ${{ matrix.target == 'bgp' && 190 || matrix.target == 'control-plane' && 190 || matrix.target == 'control-plane-helm' && 190 || matrix.target == 'external-gateway' && 190 || 130 }} + timeout-minutes: ${{ matrix.target == 'bgp-loose-isolation' && 190 || matrix.target == 'bgp' && 190 || matrix.target == 'control-plane' && 190 || matrix.target == 'control-plane-helm' && 190 || matrix.target == 'external-gateway' && 190 || 130 }} run: | # used by e2e diagnostics package export OVN_IMAGE="ovn-daemonset-fedora:pr" @@ -671,7 +673,7 @@ jobs: fi elif [ "${{ matrix.target }}" == "network-segmentation" ]; then make -C test control-plane WHAT="Network Segmentation" - elif [ "${{ matrix.target }}" == "bgp" ]; then + elif [ "${{ matrix.target }}" == "bgp" ] || [ "${{ matrix.target }}" == "bgp-loose-isolation" ]; then make -C test control-plane elif [ "${{ matrix.target }}" == "tools" ]; then make -C go-controller build diff --git a/contrib/kind-common b/contrib/kind-common index ab427fc975..1587e5ff0e 100644 --- a/contrib/kind-common +++ b/contrib/kind-common @@ -758,22 +758,52 @@ deploy_bgp_external_server() { $OCI_BIN run --cap-add NET_ADMIN --user 0 -d --network bgpnet --rm --name bgpserver -p 8080:8080 registry.k8s.io/e2e-test-images/agnhost:2.45 netexec # let's make the bgp external server have its default route towards FRR router so that we don't need to add routes during tests back to the pods in the # cluster for return traffic - local bgp_network_frr_v4 bgp_network_frr_v6 + local bgp_network_frr_v4 bgp_network_frr_v6 kind_network_frr_v4 kind_network_frr_v6 bgp_network_frr_v4=$($OCI_BIN inspect -f '{{.NetworkSettings.Networks.bgpnet.IPAddress}}' frr) - echo "FRR kind network IPv4: ${bgp_network_frr_v4}" + echo "FRR bgp network IPv4: ${bgp_network_frr_v4}" $OCI_BIN exec bgpserver ip route replace default via "$bgp_network_frr_v4" if [ "$PLATFORM_IPV6_SUPPORT" == true ] ; then bgp_network_frr_v6=$($OCI_BIN inspect -f '{{.NetworkSettings.Networks.bgpnet.GlobalIPv6Address}}' frr) - echo "FRR kind network IPv6: ${bgp_network_frr_v6}" + echo "FRR bgp network IPv6: ${bgp_network_frr_v6}" $OCI_BIN exec bgpserver ip -6 route replace default via "$bgp_network_frr_v6" fi - # disable the default route to make sure the container only routes accross - # directly connected or learnt networks (doing this at the very end since - # docker changes the routing table when a new network is connected) - $OCI_BIN exec frr ip route delete default - $OCI_BIN exec frr ip route - $OCI_BIN exec frr ip -6 route delete default - $OCI_BIN exec frr ip -6 route + if [ "$ADVERTISED_UDN_ISOLATION_MODE" == "loose" ]; then + kind_network_frr_v4=$($OCI_BIN inspect -f '{{.NetworkSettings.Networks.kind.IPAddress}}' frr) + echo "FRR kind network IPv4: ${kind_network_frr_v4}" + # If UDN isolation is in loose disabled, we need to set the default gateway for the nodes in the cluster + # to the FRR router so that cross-UDN traffic can be routed back to the pods in the cluster in the loose mode. + echo "Setting default gateway for nodes in the cluster to FRR router IPv4: ${kind_network_frr_v4}" + set_nodes_default_gw "$kind_network_frr_v4" + if [ "$PLATFORM_IPV6_SUPPORT" == true ] ; then + kind_network_frr_v6=$($OCI_BIN inspect -f '{{.NetworkSettings.Networks.kind.GlobalIPv6Address}}' frr) + echo "FRR kind network IPv6: ${kind_network_frr_v6}" + set_nodes_default_gw "$kind_network_frr_v6" + fi + else + # disable the default route to make sure the container only routes accross + # directly connected or learnt networks (doing this at the very end since + # docker changes the routing table when a new network is connected) + $OCI_BIN exec frr ip route delete default + $OCI_BIN exec frr ip route + $OCI_BIN exec frr ip -6 route delete default + $OCI_BIN exec frr ip -6 route + fi +} + +set_nodes_default_gw() { + local gw="$1" + local ip_cmd="ip" + local route_cmd="route replace default via" + + # Check if $gw is IPv6 (contains ':') + if [[ "$gw" == *:* ]]; then + ip_cmd="ip -6" + fi + + KIND_NODES=$(kind_get_nodes) + for node in $KIND_NODES; do + $OCI_BIN exec "$node" $ip_cmd $route_cmd "$gw" + done } destroy_bgp() { diff --git a/contrib/kind.sh b/contrib/kind.sh index 90ab340cf0..cff76b68ef 100755 --- a/contrib/kind.sh +++ b/contrib/kind.sh @@ -53,6 +53,7 @@ usage() { echo " [-ic | --enable-interconnect]" echo " [-uae | --preconfigured-udn-addresses-enable]" echo " [-rae | --enable-route-advertisements]" + echo " [-rud | --routed-udn-isolation-disable]" echo " [-adv | --advertise-default-network]" echo " [-nqe | --network-qos-enable]" echo " [--isolated]" @@ -127,6 +128,7 @@ echo "-obs | --observability Enable OVN Observability fea echo "-uae | --preconfigured-udn-addresses-enable Enable connecting workloads with preconfigured network to user-defined networks" echo "-rae | --enable-route-advertisements Enable route advertisements" echo "-adv | --advertise-default-network Applies a RouteAdvertisements configuration to advertise the default network on all nodes" +echo "-rud | --routed-udn-isolation-disable Disable isolation across BGP-advertised UDNs (sets advertised-udn-isolation-mode=loose). DEFAULT: strict." echo "" } @@ -316,6 +318,8 @@ parse_args() { ;; -adv | --advertise-default-network) ADVERTISE_DEFAULT_NETWORK=true ;; + -rud | --routed-udn-isolation-disable) ADVERTISED_UDN_ISOLATION_MODE=loose + ;; -ce | --enable-central ) OVN_ENABLE_INTERCONNECT=false CENTRAL_ARG_PROVIDED=true ;; @@ -417,6 +421,7 @@ print_params() { echo "ENABLE_MULTI_NET = $ENABLE_MULTI_NET" echo "ENABLE_NETWORK_SEGMENTATION= $ENABLE_NETWORK_SEGMENTATION" echo "ENABLE_ROUTE_ADVERTISEMENTS= $ENABLE_ROUTE_ADVERTISEMENTS" + echo "ADVERTISED_UDN_ISOLATION_MODE= $ADVERTISED_UDN_ISOLATION_MODE" echo "ADVERTISE_DEFAULT_NETWORK = $ADVERTISE_DEFAULT_NETWORK" echo "ENABLE_PRE_CONF_UDN_ADDR = $ENABLE_PRE_CONF_UDN_ADDR" echo "OVN_ENABLE_INTERCONNECT = $OVN_ENABLE_INTERCONNECT" @@ -663,6 +668,7 @@ set_default_params() { echo "Preconfigured UDN addresses requires interconnect to be enabled (-ic)" exit 1 fi + ADVERTISED_UDN_ISOLATION_MODE=${ADVERTISED_UDN_ISOLATION_MODE:-strict} ADVERTISE_DEFAULT_NETWORK=${ADVERTISE_DEFAULT_NETWORK:-false} OVN_COMPACT_MODE=${OVN_COMPACT_MODE:-false} if [ "$OVN_COMPACT_MODE" == true ]; then @@ -916,6 +922,7 @@ create_ovn_kube_manifests() { --preconfigured-udn-addresses-enable="${ENABLE_PRE_CONF_UDN_ADDR}" \ --route-advertisements-enable="${ENABLE_ROUTE_ADVERTISEMENTS}" \ --advertise-default-network="${ADVERTISE_DEFAULT_NETWORK}" \ + --advertised-udn-isolation-mode="${ADVERTISED_UDN_ISOLATION_MODE}" \ --ovnkube-metrics-scale-enable="${OVN_METRICS_SCALE_ENABLE}" \ --compact-mode="${OVN_COMPACT_MODE}" \ --enable-interconnect="${OVN_ENABLE_INTERCONNECT}" \ diff --git a/dist/images/daemonset.sh b/dist/images/daemonset.sh index fa136be2f5..f45f473a66 100755 --- a/dist/images/daemonset.sh +++ b/dist/images/daemonset.sh @@ -74,6 +74,7 @@ OVN_NETWORK_SEGMENTATION_ENABLE= OVN_PRE_CONF_UDN_ADDR_ENABLE= OVN_ROUTE_ADVERTISEMENTS_ENABLE= OVN_ADVERTISE_DEFAULT_NETWORK= +OVN_ADVERTISED_UDN_ISOLATION_MODE= OVN_V4_JOIN_SUBNET="" OVN_V6_JOIN_SUBNET="" OVN_V4_MASQUERADE_SUBNET="" @@ -283,6 +284,9 @@ while [ "$1" != "" ]; do --advertise-default-network) OVN_ADVERTISE_DEFAULT_NETWORK=$VALUE ;; + --advertised-udn-isolation-mode) + OVN_ADVERTISED_UDN_ISOLATION_MODE=$VALUE + ;; --egress-service-enable) OVN_EGRESSSERVICE_ENABLE=$VALUE ;; @@ -478,6 +482,8 @@ ovn_route_advertisements_enable=${OVN_ROUTE_ADVERTISEMENTS_ENABLE} echo "ovn_route_advertisements_enable: ${ovn_route_advertisements_enable}" ovn_advertise_default_network=${OVN_ADVERTISE_DEFAULT_NETWORK} echo "ovn_advertise_default_network: ${ovn_advertise_default_network}" +ovn_advertised_udn_isolation_mode=${OVN_ADVERTISED_UDN_ISOLATION_MODE} +echo "ovn_advertised_udn_isolation_mode: ${ovn_advertised_udn_isolation_mode}" ovn_hybrid_overlay_net_cidr=${OVN_HYBRID_OVERLAY_NET_CIDR} echo "ovn_hybrid_overlay_net_cidr: ${ovn_hybrid_overlay_net_cidr}" ovn_disable_snat_multiple_gws=${OVN_DISABLE_SNAT_MULTIPLE_GWS} @@ -620,6 +626,7 @@ ovn_image=${ovnkube_image} \ ovn_network_segmentation_enable=${ovn_network_segmentation_enable} \ ovn_pre_conf_udn_addr_enable=${ovn_pre_conf_udn_addr_enable} \ ovn_route_advertisements_enable=${ovn_route_advertisements_enable} \ + ovn_advertised_udn_isolation_mode=${ovn_advertised_udn_isolation_mode} \ ovn_egress_service_enable=${ovn_egress_service_enable} \ ovn_ssl_en=${ovn_ssl_en} \ ovn_remote_probe_interval=${ovn_remote_probe_interval} \ @@ -674,6 +681,7 @@ ovn_image=${ovnkube_image} \ ovn_multi_network_enable=${ovn_multi_network_enable} \ ovn_network_segmentation_enable=${ovn_network_segmentation_enable} \ ovn_route_advertisements_enable=${ovn_route_advertisements_enable} \ + ovn_advertised_udn_isolation_mode=${ovn_advertised_udn_isolation_mode} \ ovn_egress_service_enable=${ovn_egress_service_enable} \ ovn_ssl_en=${ovn_ssl_en} \ ovn_remote_probe_interval=${ovn_remote_probe_interval} \ @@ -773,6 +781,7 @@ ovn_image=${ovnkube_image} \ ovn_multi_network_enable=${ovn_multi_network_enable} \ ovn_network_segmentation_enable=${ovn_network_segmentation_enable} \ ovn_route_advertisements_enable=${ovn_route_advertisements_enable} \ + ovn_advertised_udn_isolation_mode=${ovn_advertised_udn_isolation_mode} \ ovn_egress_service_enable=${ovn_egress_service_enable} \ ovn_ssl_en=${ovn_ssl_en} \ ovn_master_count=${ovn_master_count} \ @@ -823,6 +832,7 @@ ovn_image=${ovnkube_image} \ ovn_network_segmentation_enable=${ovn_network_segmentation_enable} \ ovn_pre_conf_udn_addr_enable=${ovn_pre_conf_udn_addr_enable} \ ovn_route_advertisements_enable=${ovn_route_advertisements_enable} \ + ovn_advertised_udn_isolation_mode=${ovn_advertised_udn_isolation_mode} \ ovn_egress_service_enable=${ovn_egress_service_enable} \ ovn_ssl_en=${ovn_ssl_en} \ ovn_master_count=${ovn_master_count} \ @@ -904,6 +914,7 @@ ovn_image=${ovnkube_image} \ ovn_network_segmentation_enable=${ovn_network_segmentation_enable} \ ovn_pre_conf_udn_addr_enable=${ovn_pre_conf_udn_addr_enable} \ ovn_route_advertisements_enable=${ovn_route_advertisements_enable} \ + ovn_advertised_udn_isolation_mode=${ovn_advertised_udn_isolation_mode} \ ovn_egress_service_enable=${ovn_egress_service_enable} \ ovn_ssl_en=${ovn_ssl_en} \ ovn_remote_probe_interval=${ovn_remote_probe_interval} \ @@ -972,6 +983,7 @@ ovn_image=${ovnkube_image} \ ovn_network_segmentation_enable=${ovn_network_segmentation_enable} \ ovn_pre_conf_udn_addr_enable=${ovn_pre_conf_udn_addr_enable} \ ovn_route_advertisements_enable=${ovn_route_advertisements_enable} \ + ovn_advertised_udn_isolation_mode=${ovn_advertised_udn_isolation_mode} \ ovn_ssl_en=${ovn_ssl_en} \ ovn_remote_probe_interval=${ovn_remote_probe_interval} \ ovn_monitor_all=${ovn_monitor_all} \ @@ -1070,12 +1082,14 @@ ovn_network_segmentation_enable=${ovn_network_segmentation_enable} \ ovn_pre_conf_udn_addr_enable=${ovn_pre_conf_udn_addr_enable} \ ovn_enable_dnsnameresolver=${ovn_enable_dnsnameresolver} \ ovn_route_advertisements_enable=${ovn_route_advertisements_enable} \ +ovn_advertised_udn_isolation_mode=${ovn_advertised_udn_isolation_mode} \ jinjanate ../templates/rbac-ovnkube-cluster-manager.yaml.j2 -o ${output_dir}/rbac-ovnkube-cluster-manager.yaml ovn_network_segmentation_enable=${ovn_network_segmentation_enable} \ ovn_enable_dnsnameresolver=${ovn_enable_dnsnameresolver} \ ovn_route_advertisements_enable=${ovn_route_advertisements_enable} \ ovn_pre_conf_udn_addr_enable=${ovn_pre_conf_udn_addr_enable} \ +ovn_advertised_udn_isolation_mode=${ovn_advertised_udn_isolation_mode} \ jinjanate ../templates/rbac-ovnkube-master.yaml.j2 -o ${output_dir}/rbac-ovnkube-master.yaml cp ../templates/rbac-ovnkube-identity.yaml.j2 ${output_dir}/rbac-ovnkube-identity.yaml diff --git a/dist/images/ovnkube.sh b/dist/images/ovnkube.sh index f00db99512..3b41e7103c 100755 --- a/dist/images/ovnkube.sh +++ b/dist/images/ovnkube.sh @@ -273,6 +273,8 @@ ovn_network_segmentation_enable=${OVN_NETWORK_SEGMENTATION_ENABLE:=false} ovn_pre_conf_udn_addr_enable=${OVN_PRE_CONF_UDN_ADDR_ENABLE:=false} #OVN_NROUTE_ADVERTISEMENTS_ENABLE - enable route advertisements for ovn-kubernetes ovn_route_advertisements_enable=${OVN_ROUTE_ADVERTISEMENTS_ENABLE:=false} +#OVN_ADVERTISED_UDN_ISOLATION_MODE - pod network isolation between advertised UDN networks. +ovn_advertised_udn_isolation_mode=${OVN_ADVERTISED_UDN_ISOLATION_MODE:=strict} ovn_acl_logging_rate_limit=${OVN_ACL_LOGGING_RATE_LIMIT:-"20"} ovn_netflow_targets=${OVN_NETFLOW_TARGETS:-} ovn_sflow_targets=${OVN_SFLOW_TARGETS:-} @@ -1253,6 +1255,11 @@ ovn-master() { fi echo "route_advertisements_enabled_flag=${route_advertisements_enabled_flag}" + advertised_udn_isolation_flag= + if [[ -n ${ovn_advertised_udn_isolation_mode} ]]; then + advertised_udn_isolation_flag="--advertised-udn-isolation-mode=${ovn_advertised_udn_isolation_mode}" + fi + egressservice_enabled_flag= if [[ ${ovn_egressservice_enable} == "true" ]]; then egressservice_enabled_flag="--enable-egress-service" @@ -1360,6 +1367,7 @@ ovn-master() { ${multi_network_enabled_flag} \ ${network_segmentation_enabled_flag} \ ${route_advertisements_enabled_flag} \ + ${advertised_udn_isolation_flag} \ ${ovn_acl_logging_rate_limit_flag} \ ${ovn_enable_svc_template_support_flag} \ ${ovn_observ_enable_flag} \ @@ -1562,6 +1570,12 @@ ovnkube-controller() { fi echo "route_advertisements_enabled_flag=${route_advertisements_enabled_flag}" + advertised_udn_isolation_flag= + if [[ -n ${ovn_advertised_udn_isolation_mode} ]]; then + advertised_udn_isolation_flag="--advertised-udn-isolation-mode=${ovn_advertised_udn_isolation_mode}" + fi + echo "advertised_udn_isolation_flag=${advertised_udn_isolation_flag}" + egressservice_enabled_flag= if [[ ${ovn_egressservice_enable} == "true" ]]; then egressservice_enabled_flag="--enable-egress-service" @@ -1678,6 +1692,7 @@ ovnkube-controller() { ${network_segmentation_enabled_flag} \ ${pre_conf_udn_addr_enable_flag} \ ${route_advertisements_enabled_flag} \ + ${advertised_udn_isolation_flag} \ ${ovn_acl_logging_rate_limit_flag} \ ${ovn_dbs} \ ${ovn_enable_svc_template_support_flag} \ @@ -1873,6 +1888,12 @@ ovnkube-controller-with-node() { fi echo "route_advertisements_enabled_flag=${route_advertisements_enabled_flag}" + advertised_udn_isolation_flag= + if [[ -n ${ovn_advertised_udn_isolation_mode} ]]; then + advertised_udn_isolation_flag="--advertised-udn-isolation-mode=${ovn_advertised_udn_isolation_mode}" + fi + echo "advertised_udn_isolation_flag=${advertised_udn_isolation_flag}" + egressservice_enabled_flag= if [[ ${ovn_egressservice_enable} == "true" ]]; then egressservice_enabled_flag="--enable-egress-service" @@ -2057,6 +2078,7 @@ ovnkube-controller-with-node() { ovnkube_metrics_scale_enable_flag="--metrics-enable-scale --metrics-enable-pprof" fi echo "ovnkube_metrics_scale_enable_flag: ${ovnkube_metrics_scale_enable_flag}" + ovnkube_local_cert_flags= if [[ ${ovn_enable_ovnkube_identity} == "true" ]]; then bootstrap_kubeconfig="/host-kubernetes/kubelet.conf" @@ -2134,6 +2156,7 @@ ovnkube-controller-with-node() { ${network_segmentation_enabled_flag} \ ${pre_conf_udn_addr_enable_flag} \ ${route_advertisements_enabled_flag} \ + ${advertised_udn_isolation_flag} \ ${netflow_targets} \ ${ofctrl_wait_before_clear} \ ${ovn_acl_logging_rate_limit_flag} \ @@ -2312,6 +2335,11 @@ ovn-cluster-manager() { fi echo "route_advertisements_enabled_flag=${route_advertisements_enabled_flag}" + advertised_udn_isolation_flag= + if [[ -n ${ovn_advertised_udn_isolation_mode} ]]; then + advertised_udn_isolation_flag="--advertised-udn-isolation-mode=${ovn_advertised_udn_isolation_mode}" + fi + persistent_ips_enabled_flag= if [[ ${ovn_enable_persistent_ips} == "true" ]]; then persistent_ips_enabled_flag="--enable-persistent-ips" @@ -2375,6 +2403,7 @@ ovn-cluster-manager() { ${network_segmentation_enabled_flag} \ ${pre_conf_udn_addr_enable_flag} \ ${route_advertisements_enabled_flag} \ + ${advertised_udn_isolation_flag} \ ${persistent_ips_enabled_flag} \ ${ovnkube_enable_interconnect_flag} \ ${ovnkube_enable_multi_external_gateway_flag} \ @@ -2561,6 +2590,11 @@ ovn-node() { route_advertisements_enabled_flag="--enable-route-advertisements" fi + advertised_udn_isolation_flag= + if [[ -n ${ovn_advertised_udn_isolation_mode} ]]; then + advertised_udn_isolation_flag="--advertised-udn-isolation-mode=${ovn_advertised_udn_isolation_mode}" + fi + netflow_targets= if [[ -n ${ovn_netflow_targets} ]]; then netflow_targets="--netflow-targets ${ovn_netflow_targets}" @@ -2793,6 +2827,7 @@ ovn-node() { ${network_segmentation_enabled_flag} \ ${pre_conf_udn_addr_enable_flag} \ ${route_advertisements_enabled_flag} \ + ${advertised_udn_isolation_flag} \ ${netflow_targets} \ ${ofctrl_wait_before_clear} \ ${ovn_dbs} \ diff --git a/dist/templates/ovnkube-control-plane.yaml.j2 b/dist/templates/ovnkube-control-plane.yaml.j2 index 538a74c131..ded7096f86 100644 --- a/dist/templates/ovnkube-control-plane.yaml.j2 +++ b/dist/templates/ovnkube-control-plane.yaml.j2 @@ -150,6 +150,8 @@ spec: value: "{{ ovn_pre_conf_udn_addr_enable }}" - name: OVN_ROUTE_ADVERTISEMENTS_ENABLE value: "{{ ovn_route_advertisements_enable }}" + - name: OVN_ADVERTISED_UDN_ISOLATION_MODE + value: "{{ ovn_advertised_udn_isolation_mode }}" - name: OVN_HYBRID_OVERLAY_NET_CIDR value: "{{ ovn_hybrid_overlay_net_cidr }}" - name: OVN_DISABLE_SNAT_MULTIPLE_GWS diff --git a/dist/templates/ovnkube-master.yaml.j2 b/dist/templates/ovnkube-master.yaml.j2 index 15e2727692..532216da7b 100644 --- a/dist/templates/ovnkube-master.yaml.j2 +++ b/dist/templates/ovnkube-master.yaml.j2 @@ -261,6 +261,8 @@ spec: value: "{{ ovn_network_segmentation_enable }}" - name: OVN_ROUTE_ADVERTISEMENTS_ENABLE value: "{{ ovn_route_advertisements_enable }}" + - name: OVN_ADVERTISED_UDN_ISOLATION_MODE + value: "{{ ovn_advertised_udn_isolation_mode }}" - name: OVN_EGRESSSERVICE_ENABLE value: "{{ ovn_egress_service_enable }}" - name: OVN_HYBRID_OVERLAY_NET_CIDR diff --git a/dist/templates/ovnkube-node.yaml.j2 b/dist/templates/ovnkube-node.yaml.j2 index 856fc1cea3..4a81df0582 100644 --- a/dist/templates/ovnkube-node.yaml.j2 +++ b/dist/templates/ovnkube-node.yaml.j2 @@ -242,6 +242,8 @@ spec: value: "{{ ovn_network_segmentation_enable }}" - name: OVN_ROUTE_ADVERTISEMENTS_ENABLE value: "{{ ovn_route_advertisements_enable }}" + - name: OVN_ADVERTISED_UDN_ISOLATION_MODE + value: "{{ ovn_advertised_udn_isolation_mode }}" - name: OVN_ENABLE_INTERCONNECT value: "{{ ovn_enable_interconnect }}" - name: OVN_ENABLE_MULTI_EXTERNAL_GATEWAY diff --git a/dist/templates/ovnkube-single-node-zone.yaml.j2 b/dist/templates/ovnkube-single-node-zone.yaml.j2 index dbeee53eac..d5d6e6dd21 100644 --- a/dist/templates/ovnkube-single-node-zone.yaml.j2 +++ b/dist/templates/ovnkube-single-node-zone.yaml.j2 @@ -303,6 +303,9 @@ spec: name: run-systemd subPath: private readOnly: true + - mountPath: /var/run/k8s.cni.cncf.io/devinfo/dp + name: host-devinfo-dp + readOnly: true resources: requests: @@ -441,6 +444,8 @@ spec: value: "{{ ovn_pre_conf_udn_addr_enable }}" - name: OVN_ROUTE_ADVERTISEMENTS_ENABLE value: "{{ ovn_route_advertisements_enable }}" + - name: OVN_ADVERTISED_UDN_ISOLATION_MODE + value: "{{ ovn_advertised_udn_isolation_mode }}" - name: OVNKUBE_NODE_MGMT_PORT_NETDEV value: "{{ ovnkube_node_mgmt_port_netdev }}" - name: OVN_EMPTY_LB_EVENTS @@ -639,6 +644,9 @@ spec: - name: run-systemd hostPath: path: /run/systemd + - name: host-devinfo-dp + hostPath: + path: /var/run/k8s.cni.cncf.io/devinfo/dp tolerations: - operator: "Exists" diff --git a/dist/templates/ovnkube-zone-controller.yaml.j2 b/dist/templates/ovnkube-zone-controller.yaml.j2 index 88bbb671c4..d51cb82843 100644 --- a/dist/templates/ovnkube-zone-controller.yaml.j2 +++ b/dist/templates/ovnkube-zone-controller.yaml.j2 @@ -349,6 +349,8 @@ spec: value: "{{ ovn_pre_conf_udn_addr_enable }}" - name: OVN_ROUTE_ADVERTISEMENTS_ENABLE value: "{{ ovn_route_advertisements_enable }}" + - name: OVN_ADVERTISED_UDN_ISOLATION_MODE + value: "{{ ovn_advertised_udn_isolation_mode }}" - name: OVN_HYBRID_OVERLAY_NET_CIDR value: "{{ ovn_hybrid_overlay_net_cidr }}" - name: OVN_DISABLE_SNAT_MULTIPLE_GWS diff --git a/docs/features/cluster-egress-controls/egress-ip.md b/docs/features/cluster-egress-controls/egress-ip.md index d4cbd30b8c..f5c02232f5 100644 --- a/docs/features/cluster-egress-controls/egress-ip.md +++ b/docs/features/cluster-egress-controls/egress-ip.md @@ -204,11 +204,11 @@ Due to the fact that ovn-controllers on different nodes apply the changes indepe there is a chance that some pod traffic will reach the egress node before it configures the SNAT rules. The following flows are added on breth0 to address this scenario: ```shell -# Commit connections from local pods so they are not affected by the drop rule below, this is required for ICNIv2 -priority=109,ip,in_port=2,nw_src= actions=ct(commit,zone=64000,exec(set_field:0x1->ct_mark)),output:1 +# Output traffic from local pods so they are not affected by the drop rule below, this is required for ICNIv2 and advertised UDNs +priority=104,ip,in_port=2,nw_src= actions=output:eth0 # Drop non SNATed egress traffic coming from non-local pods -priority=104,ip,in_port=2,nw_src= actions=drop +priority=103,ip,in_port=2,nw_src= actions=drop # Commit connections coming from IPs not in cluster network priority=100,ip,in_port=2 actions=ct(commit,zone=64000,exec(set_field:0x1->ct_mark)),output:1 diff --git a/go-controller/go.mod b/go-controller/go.mod index 4da1a7a717..9df6b1ccba 100644 --- a/go-controller/go.mod +++ b/go-controller/go.mod @@ -56,6 +56,7 @@ require ( google.golang.org/protobuf v1.36.5 gopkg.in/fsnotify/fsnotify.v1 v1.4.7 gopkg.in/gcfg.v1 v1.2.3 + gopkg.in/k8snetworkplumbingwg/multus-cni.v4 v4.0.2 gopkg.in/natefinch/lumberjack.v2 v2.2.1 k8s.io/api v0.33.3 k8s.io/apimachinery v0.33.3 @@ -146,6 +147,7 @@ require ( k8s.io/component-base v0.33.3 // indirect k8s.io/controller-manager v0.33.3 // indirect k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect + k8s.io/kubelet v0.22.8 // indirect kubevirt.io/containerized-data-importer-api v1.55.0 // indirect kubevirt.io/controller-lifecycle-operator-sdk/api v0.0.0-20220329064328-f3cc58c6ed90 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect diff --git a/go-controller/go.sum b/go-controller/go.sum index 463631a9df..9dfe0d36d4 100644 --- a/go-controller/go.sum +++ b/go-controller/go.sum @@ -26,6 +26,7 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -39,6 +40,8 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= @@ -77,16 +80,22 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/alexflint/go-filemutex v1.2.0 h1:1v0TJPDtlhgpW4nJ+GvxCLSlUDC3+gW0CQQvlmfDR/s= github.com/alexflint/go-filemutex v1.2.0/go.mod h1:mYyQSWvw9Tx2/H2n9qXPb52tTYfE0pZAWcBq5mK025c= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d h1:Byv0BzEl3/e6D5CLfI0j/7hiIEtvGVFPCZ7Ei2oq8iQ= github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/benbjohnson/clock v1.0.3/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= @@ -96,6 +105,7 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e h1:KCjb01YiNoRaJ5c+SbnPLWjVzU9vqRYHg3e5JcN50nM= github.com/bhendo/go-powershell v0.0.0-20190719160123-219e7fb4e41e/go.mod h1:f7vw6ObmmNcyFQLhZX9eUGBJGpnwTJFDvVjqZxIxHWY= github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -210,6 +220,7 @@ github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgU github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -226,6 +237,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lV github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= @@ -265,6 +277,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -274,6 +287,7 @@ github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= @@ -298,8 +312,10 @@ github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2 github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= @@ -457,15 +473,31 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -481,6 +513,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/native v1.0.0 h1:Ts/E8zCSEsG17dUqv7joXJFybuMLjQfWE04tsBODTxk= github.com/josharian/native v1.0.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -502,6 +535,7 @@ github.com/juju/testing v0.0.0-20200706033705-4c23f9c453cd/go.mod h1:hpGvhGHPVbN github.com/juju/utils v0.0.0-20180808125547-9dfc6dbfb02b/go.mod h1:6/KLg8Wz/y2KVGWEpkK9vMNGkOnu4k/cqs8Z1fKjTOk= github.com/juju/version v0.0.0-20161031051906-1f41e27e54f2/go.mod h1:kE8gK5X0CImdr7qpSKl3xB2PmpySSmfj7zVbkZFs81U= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k8snetworkplumbingwg/govdpa v0.1.5-0.20230926073613-07c1031aea47 h1:iSncnlC+rtlNOIpPa3fbqQMhpTscGJIlkiWaPl1VcS4= github.com/k8snetworkplumbingwg/govdpa v0.1.5-0.20230926073613-07c1031aea47/go.mod h1:SPaDIyUmwN03Bgn0u/mhoiE4o/+koeKh11VUsdsUX0U= github.com/k8snetworkplumbingwg/ipamclaims v0.5.1-alpha h1:RPXmPp07hm6l2/a3PSInne+6VnZeSapcFeegpDNklCs= @@ -540,6 +574,7 @@ github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjS github.com/lithammer/dedent v1.1.0 h1:VNzHMVCBNG1j0fh3OrsFRkVUwStdDArbgBWoPAffktY= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= @@ -548,6 +583,7 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= @@ -565,13 +601,20 @@ github.com/mdlayher/socket v0.2.1 h1:F2aaOwb53VsBE+ebRS9bLd7yPOfYUMC8lOODdCBDY6w github.com/mdlayher/socket v0.2.1/go.mod h1:QLlNPkFR88mRUNQIzRBMfXxwKal8H7u1h3bL1CV+f0E= github.com/metallb/frr-k8s v0.0.15 h1:6M3UGhovX1EFoaSGjrRD7djUAx3w2I+g81FH8OVtHkM= github.com/metallb/frr-k8s v0.0.15/go.mod h1:TjrGoAf+v00hYGlI8jUdyDxY5udMAOs2GWwrvLWnA4E= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/miekg/dns v1.1.31 h1:sJFOl9BgwbYAWOGEwr61FU28pqsBNdpRBnhGXtO06Oo= github.com/miekg/dns v1.1.31/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM= github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -586,6 +629,7 @@ github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGq github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -598,6 +642,7 @@ github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8m github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= @@ -656,6 +701,7 @@ github.com/openshift/custom-resource-status v1.1.2 h1:C3DL44LEbvlbItfd8mT5jWrqPf github.com/openshift/custom-resource-status v1.1.2/go.mod h1:DB/Mf2oTeiAmVVX1gN+NEqweonAPY0TKUwADizj8+ZA= github.com/ovn-kubernetes/libovsdb v0.8.1 h1:M2J8bcJt5mXCom0HqzfEtuHkT80CTSQRcYG7acT8gf4= github.com/ovn-kubernetes/libovsdb v0.8.1/go.mod h1:ZlnHLzagmLOSvyd9qfxBIZp6wOSOw0IsRsc+6lNUGbU= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= @@ -670,6 +716,7 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -677,6 +724,7 @@ github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDf github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -692,6 +740,7 @@ github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8 github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= @@ -708,16 +757,19 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/safchain/ethtool v0.3.1-0.20231027162144-83e5e0097c91 h1:q815fjV3G+4JvXNo2VwT2m+/msMU0sUkCK68CgHV9Y8= github.com/safchain/ethtool v0.3.1-0.20231027162144-83e5e0097c91/go.mod h1:qIWCTaK0xQlXNlNlIVoZjKMZFopqfMZcg4JcRqGoYc0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= @@ -731,6 +783,7 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= @@ -741,6 +794,7 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -749,6 +803,7 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -771,6 +826,7 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= @@ -829,24 +885,39 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= +go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= +go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= +go.opentelemetry.io/otel/oteltest v0.20.0/go.mod h1:L7bgKf9ZB7qCwT9Up7i9/pn0PWIa9FqQ2IQ8LoxiGnw= +go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= +go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180214000028-650f4a345ab4/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -887,6 +958,7 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -904,7 +976,9 @@ golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -979,9 +1053,11 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1030,9 +1106,11 @@ golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1054,6 +1132,7 @@ golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1111,6 +1190,7 @@ golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1213,6 +1293,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -1228,6 +1309,7 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= @@ -1247,9 +1329,13 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/grpc/examples v0.0.0-20201112215255-90f1b3ee835b h1:NuxyvVZoDfHZwYW9LD4GJiF5/nhiSyP4/InTrvw9Ibk= @@ -1293,6 +1379,9 @@ gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/k8snetworkplumbingwg/multus-cni.v4 v4.0.2 h1:71L/o4mHzVZit0yaIHkOBaEO1fj0lucarJCmhuPCsOA= +gopkg.in/k8snetworkplumbingwg/multus-cni.v4 v4.0.2/go.mod h1:u1IhI+b7jS0cdRNRJajg0VIzEbQkNNXPZGBSvDr7t0M= gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4 h1:hILp2hNrRnYjZpmIbx70psAHbBSEcQ1NIzDcUbJ1b6g= gopkg.in/mgo.v2 v2.0.0-20160818015218-f2b6f6c918c4/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= @@ -1310,6 +1399,7 @@ gopkg.in/yaml.v2 v2.0.0-20170712054546-1be3d31502d6/go.mod h1:JAlM8MvJe8wmxCU4Bl gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -1335,6 +1425,7 @@ k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= k8s.io/api v0.22.7/go.mod h1:7hejA1BgBEiSsWljUyRkIjj+AISXO16IwsaDgFjJsQE= +k8s.io/api v0.22.8/go.mod h1:uLlWJNRJ+AYwgAdsNwf0TsD3eByNYW9RlXFmkMdL3yk= k8s.io/api v0.23.3/go.mod h1:w258XdGyvCmnBj/vGzQMj6kzdufJZVUwEM1U2fRJwSQ= k8s.io/api v0.33.3 h1:SRd5t//hhkI1buzxb288fy2xvjubstenEKL9K51KBI8= k8s.io/api v0.33.3/go.mod h1:01Y/iLUjNBM3TAvypct7DIj0M0NIZc+PzAHCIo0CYGE= @@ -1344,6 +1435,7 @@ k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRp k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= k8s.io/apimachinery v0.22.7/go.mod h1:ZvVLP5iLhwVFg2Yx9Gh5W0um0DUauExbRhe+2Z8I1EU= +k8s.io/apimachinery v0.22.8/go.mod h1:ZvVLP5iLhwVFg2Yx9Gh5W0um0DUauExbRhe+2Z8I1EU= k8s.io/apimachinery v0.23.3/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM= k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= @@ -1356,6 +1448,7 @@ k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= k8s.io/client-go v0.22.7/go.mod h1:pGU/tWSzzvsYT7M3npHhoZ3Jh9qJTTIvFvDtWuW31dw= +k8s.io/client-go v0.22.8/go.mod h1:dOHOy82WOBz0siYHpVyY7FqTIq+iXFXW3+THFk6qErU= k8s.io/client-go v0.33.3 h1:M5AfDnKfYmVJif92ngN532gFqakcGi6RvaOF16efrpA= k8s.io/client-go v0.33.3/go.mod h1:luqKBQggEf3shbxHY4uVENAxrDISLOarxpTKMiUuujg= k8s.io/code-generator v0.22.7/go.mod h1:iOZwYADSgFPNGWfqHFfg1V0TNJnl1t0WyZluQp4baqU= @@ -1363,6 +1456,7 @@ k8s.io/code-generator v0.23.3/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12 k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/component-base v0.22.8/go.mod h1:rt0sHx3GT3i8e72rPKQSrTIV3THWyEl2IDYcnEbxraI= k8s.io/component-base v0.33.3 h1:mlAuyJqyPlKZM7FyaoM/LcunZaaY353RXiOd2+B5tGA= k8s.io/component-base v0.33.3/go.mod h1:ktBVsBzkI3imDuxYXmVxZ2zxJnYTZ4HAsVj9iF09qp4= k8s.io/component-helpers v0.33.3 h1:fjWVORSQfI0WKzPeIFSju/gMD9sybwXBJ7oPbqQu6eM= @@ -1391,6 +1485,8 @@ k8s.io/kube-openapi v0.0.0-20211115234752-e816edb12b65/go.mod h1:sX9MT8g7NVZM5lV k8s.io/kube-openapi v0.0.0-20220124234850-424119656bbf/go.mod h1:sX9MT8g7NVZM5lVL/j8QyCCJe8YSMW30QvGZWaCIDIk= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/kubelet v0.22.8 h1:Ce1yoWCd03Gp5Dqe4BUy1FlQntyIUfCUcL8E6mKIgi4= +k8s.io/kubelet v0.22.8/go.mod h1:IHfh4AnROFQ6fMnCeoTQIrnF0H148PlfllXeupW698c= k8s.io/kubernetes v1.33.3 h1:dBx5Z2ZhR8kNzAwCoCz4j1niUbUrNUDVxeSj4/Ienu0= k8s.io/kubernetes v1.33.3/go.mod h1:nrt8sldmckKz2fCZhgRX3SKfS2e+CzXATPv6ITNkU00= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= diff --git a/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template.go b/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template.go index 02c7912e85..6c4d322ecc 100644 --- a/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template.go +++ b/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template.go @@ -72,6 +72,7 @@ func RenderNetAttachDefManifest(obj client.Object, targetNamespace string) (*net Name: obj.GetName(), OwnerReferences: []metav1.OwnerReference{ownerRef}, Labels: renderNADLabels(obj), + Annotations: renderNADAnnotations(obj), Finalizers: []string{FinalizerUserDefinedNetwork}, }, Spec: *nadSpec, @@ -109,6 +110,21 @@ func renderNADLabels(obj client.Object) map[string]string { return labels } +// renderNADAnnotations copies annotations from UDN to corresponding NAD +func renderNADAnnotations(obj client.Object) map[string]string { + udnAnnotations := obj.GetAnnotations() + annotations := make(map[string]string) + for k, v := range udnAnnotations { + if !strings.HasPrefix(k, types.OvnK8sPrefix) { + annotations[k] = v + } + } + if len(annotations) == 0 { + return nil + } + return annotations +} + func validateTopology(spec SpecGetter) error { if spec.GetTopology() == userdefinednetworkv1.NetworkTopologyLayer3 && spec.GetLayer3() == nil || spec.GetTopology() == userdefinednetworkv1.NetworkTopologyLayer2 && spec.GetLayer2() == nil || diff --git a/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template_test.go b/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template_test.go index c02109bf0e..c24b56503e 100644 --- a/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template_test.go +++ b/go-controller/pkg/clustermanager/userdefinednetwork/template/net-attach-def-template_test.go @@ -295,7 +295,8 @@ var _ = Describe("NetAttachDefTemplate", func() { func(testSpec udnv1.UserDefinedNetworkSpec, expectedNadNetConf string) { testUdn := &udnv1.UserDefinedNetwork{ ObjectMeta: metav1.ObjectMeta{Namespace: "mynamespace", Name: "test-net", UID: "1", - Labels: map[string]string{"testLabel": "test"}}, + Annotations: map[string]string{"testAnnotation": "test", "k8s.ovn.org/testAnnotation": "test"}, + Labels: map[string]string{"testLabel": "test"}}, Spec: testSpec, } testNs := "mynamespace" @@ -310,6 +311,7 @@ var _ = Describe("NetAttachDefTemplate", func() { expectedNAD := &netv1.NetworkAttachmentDefinition{ ObjectMeta: metav1.ObjectMeta{ Name: "test-net", + Annotations: map[string]string{"testAnnotation": "test"}, Labels: map[string]string{"k8s.ovn.org/user-defined-network": "", "testLabel": "test"}, OwnerReferences: []metav1.OwnerReference{ownerRef}, Finalizers: []string{"k8s.ovn.org/user-defined-network-protection"}, diff --git a/go-controller/pkg/cni/cni.go b/go-controller/pkg/cni/cni.go index faf800d52e..cf66980dd9 100644 --- a/go-controller/pkg/cni/cni.go +++ b/go-controller/pkg/cni/cni.go @@ -8,10 +8,10 @@ import ( "time" current "github.com/containernetworking/cni/pkg/types/100" - v1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/resource" + corev1listers "k8s.io/client-go/listers/core/v1" "k8s.io/klog/v2" utilnet "k8s.io/utils/net" @@ -115,6 +115,32 @@ func (pr *PodRequest) cmdAdd(kubeAuth *KubeAPIAuth, clientset *ClientSet, networ return pr.cmdAddWithGetCNIResultFunc(kubeAuth, clientset, getCNIResult, networkManager, ovsClient) } +// primaryDPUReady makes sure previous annotation condition is ready, then if primary UDN interface is needed and it is +// in the DPU-HOST/DPU setup, checks if DPU connection annotations for primary UDN interface are ready. +func (pr *PodRequest) primaryDPUReady(primaryUDN *udn.UserDefinedPrimaryNetwork, k kube.Interface, podLister corev1listers.PodLister, annotCondFn podAnnotWaitCond) podAnnotWaitCond { + return func(pod *corev1.Pod, nadName string) (*util.PodAnnotation, bool, error) { + // First, check the original annotation condition + annotation, isReady, err := annotCondFn(pod, nadName) + if err != nil || !isReady { + return annotation, isReady, err + } + // primaryUDNPodRequest would be nil if no primary UDN interface is needed + primaryUDNPodRequest := pr.buildPrimaryUDNPodRequest(primaryUDN) + // DPU-Host: add DPU connection-details annotation to allow DPU performs the needed primary UDN interface plumbing. + if config.OvnKubeNode.Mode == types.NodeModeDPUHost && + primaryUDNPodRequest != nil && primaryUDNPodRequest.CNIConf.DeviceID != "" { + netdevName := primaryUDN.NetworkDevice() + if err := primaryUDNPodRequest.addDPUConnectionDetailsAnnot(k, podLister, netdevName); err != nil { + return annotation, false, err + } + // Check if DPU status annotation is ready (passing nil as we've already checked annotation) + return isDPUReady(nil, primaryUDN.NADName())(pod, nadName) + } + // Non-DPU case: proceed normally + return annotation, true, nil + } +} + func (pr *PodRequest) cmdAddWithGetCNIResultFunc( kubeAuth *KubeAPIAuth, clientset *ClientSet, @@ -146,7 +172,7 @@ func (pr *PodRequest) cmdAddWithGetCNIResultFunc( if err = pr.addDPUConnectionDetailsAnnot(kubecli, clientset.podLister, netdevName); err != nil { return nil, err } - annotCondFn = isDPUReady + // Defer default-network DPU readiness gating so the primary UDN annotation/DPU readiness can progress in parallel when present. } // In the case of SmartNIC (CX5), we store the netdevname in the representor's // OVS interface's external_id column. This is done in ConfigureInterface(). @@ -154,14 +180,34 @@ func (pr *PodRequest) cmdAddWithGetCNIResultFunc( // Get the IP address and MAC address of the pod // for DPU, ensure connection-details is present - primaryUDN := udn.NewPrimaryNetwork(networkManager) + primaryUDN := udn.NewPrimaryNetwork(networkManager, clientset.nadLister) if util.IsNetworkSegmentationSupportEnabled() { - annotCondFn = primaryUDN.WaitForPrimaryAnnotationFn(podName, namespace, annotCondFn) + annotCondFn = primaryUDN.WaitForPrimaryAnnotationFn(annotCondFn) + // checks for primary UDN network's DPU connections status + annotCondFn = pr.primaryDPUReady(primaryUDN, kubecli, clientset.podLister, annotCondFn) + } + + // now checks for default network's DPU connection status + if config.OvnKubeNode.Mode == types.NodeModeDPUHost { + if pr.CNIConf.DeviceID != "" { + annotCondFn = isDPUReady(annotCondFn, pr.nadName) + } } pod, annotations, podNADAnnotation, err := GetPodWithAnnotations(pr.ctx, clientset, namespace, podName, pr.nadName, annotCondFn) if err != nil { return nil, fmt.Errorf("failed to get pod annotation: %v", err) } + + var primaryUDNPodInfo *PodInterfaceInfo + primaryUDNPodRequest := pr.buildPrimaryUDNPodRequest(primaryUDN) + if primaryUDNPodRequest != nil { + primaryUDNPodInfo, err = primaryUDNPodRequest.buildPodInterfaceInfo(annotations, primaryUDN.Annotation(), primaryUDN.NetworkDevice()) + if err != nil { + return nil, err + } + klog.V(4).Infof("Pod %s/%s primaryUDN podRequest %v podInfo %v", namespace, podName, primaryUDNPodRequest, primaryUDNPodInfo) + } + if err = pr.checkOrUpdatePodUID(pod); err != nil { return nil, err } @@ -175,9 +221,6 @@ func (pr *PodRequest) cmdAddWithGetCNIResultFunc( response := &Response{KubeAuth: kubeAuth} if !config.UnprivilegedMode { - //TODO: There is nothing technical to run this at unprivileged mode but - // we will tackle that later on. - netName := pr.netName if pr.CNIConf.PhysicalNetworkName != "" { netName = pr.CNIConf.PhysicalNetworkName @@ -194,38 +237,46 @@ func (pr *PodRequest) cmdAddWithGetCNIResultFunc( if err != nil { return nil, err } - if primaryUDN.Found() { - primaryUDNPodRequest := pr.buildPrimaryUDNPodRequest(pod, primaryUDN) - primaryUDNPodInfo, err := primaryUDNPodRequest.buildPodInterfaceInfo(annotations, primaryUDN.Annotation(), primaryUDN.NetworkDevice()) - if err != nil { - return nil, err - } - primaryUDNResult, err := getCNIResultFn(primaryUDNPodRequest, clientset, primaryUDNPodInfo) + if primaryUDNPodRequest != nil { + err = primaryUDNCmdAddGetCNIResultFunc(response, getCNIResultFn, primaryUDNPodRequest, clientset, primaryUDNPodInfo) if err != nil { return nil, err } - - response.Result.Routes = append(response.Result.Routes, primaryUDNResult.Routes...) - numOfInitialIPs := len(response.Result.IPs) - numOfInitialIfaces := len(response.Result.Interfaces) - response.Result.Interfaces = append(response.Result.Interfaces, primaryUDNResult.Interfaces...) - response.Result.IPs = append(response.Result.IPs, primaryUDNResult.IPs...) - - // Offset the index of the default network IPs to correctly point to the default network interfaces - for i := numOfInitialIPs; i < len(response.Result.IPs); i++ { - ifaceIPConfig := response.Result.IPs[i].Copy() - if response.Result.IPs[i].Interface != nil { - response.Result.IPs[i].Interface = current.Int(*ifaceIPConfig.Interface + numOfInitialIfaces) - } - } } } else { response.PodIFInfo = podInterfaceInfo + if primaryUDNPodRequest != nil { + response.PrimaryUDNPodInfo = primaryUDNPodInfo + response.PrimaryUDNPodReq = primaryUDNPodRequest + } } return response, nil } +func primaryUDNCmdAddGetCNIResultFunc(response *Response, getCNIResultFn getCNIResultFunc, primaryUDNPodRequest *PodRequest, + clientset PodInfoGetter, primaryUDNPodInfo *PodInterfaceInfo) error { + primaryUDNResult, err := getCNIResultFn(primaryUDNPodRequest, clientset, primaryUDNPodInfo) + if err != nil { + return err + } + + response.Result.Routes = append(response.Result.Routes, primaryUDNResult.Routes...) + numOfInitialIPs := len(response.Result.IPs) + numOfInitialIfaces := len(response.Result.Interfaces) + response.Result.Interfaces = append(response.Result.Interfaces, primaryUDNResult.Interfaces...) + response.Result.IPs = append(response.Result.IPs, primaryUDNResult.IPs...) + + // Offset the index of the default network IPs to correctly point to the default network interfaces + for i := numOfInitialIPs; i < len(response.Result.IPs); i++ { + ifaceIPConfig := response.Result.IPs[i].Copy() + if response.Result.IPs[i].Interface != nil { + response.Result.IPs[i].Interface = current.Int(*ifaceIPConfig.Interface + numOfInitialIfaces) + } + } + return nil +} + func (pr *PodRequest) cmdDel(clientset *ClientSet) (*Response, error) { // assume success case, return an empty Result response := &Response{} @@ -401,30 +452,38 @@ func getCNIResult(pr *PodRequest, getter PodInfoGetter, podInterfaceInfo *PodInt }, nil } +// buildPrimaryUDNPodRequest returns PodRequest for primary UDN interface, +// it returns nil if primary UDN is not requested on the Pod func (pr *PodRequest) buildPrimaryUDNPodRequest( - pod *corev1.Pod, primaryUDN *udn.UserDefinedPrimaryNetwork, ) *PodRequest { + if !primaryUDN.Found() { + // if primary UDN interface is not needed, return nil + return nil + } + deviceID, deviceInfo, isVFIO := primaryUDN.NetworkDeviceInfo() req := &PodRequest{ Command: pr.Command, - PodNamespace: pod.Namespace, - PodName: pod.Name, - PodUID: string(pod.UID), + PodNamespace: pr.PodNamespace, + PodName: pr.PodName, + PodUID: pr.PodUID, SandboxID: pr.SandboxID, Netns: pr.Netns, IfName: primaryUDN.InterfaceName(), CNIConf: &ovncnitypes.NetConf{ // primary UDN MTU will be taken from config.Default.MTU // if not specified at the NAD - MTU: primaryUDN.MTU(), + MTU: primaryUDN.MTU(), + DeviceID: deviceID, }, timestamp: time.Now(), - IsVFIO: pr.IsVFIO, + IsVFIO: isVFIO, netName: primaryUDN.NetworkName(), nadName: primaryUDN.NADName(), - deviceInfo: v1.DeviceInfo{}, + deviceInfo: *deviceInfo, } - req.ctx, req.cancel = context.WithTimeout(context.Background(), 2*time.Minute) + + req.ctx, req.cancel = context.WithCancel(pr.ctx) return req } diff --git a/go-controller/pkg/cni/cni_dpu_test.go b/go-controller/pkg/cni/cni_dpu_test.go index 076909bf63..4ff79027eb 100644 --- a/go-controller/pkg/cni/cni_dpu_test.go +++ b/go-controller/pkg/cni/cni_dpu_test.go @@ -5,6 +5,7 @@ import ( "time" cnitypes "github.com/containernetworking/cni/pkg/types" + nadapi "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -43,10 +44,11 @@ var _ = Describe("cni_dpu tests", func() { NetConf: cnitypes.NetConf{}, DeviceID: "", }, - timestamp: time.Time{}, - IsVFIO: false, - netName: ovntypes.DefaultNetworkName, - nadName: ovntypes.DefaultNetworkName, + timestamp: time.Time{}, + IsVFIO: false, + netName: ovntypes.DefaultNetworkName, + nadName: ovntypes.DefaultNetworkName, + deviceInfo: nadapi.DeviceInfo{}, } pod = &corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ diff --git a/go-controller/pkg/cni/cni_test.go b/go-controller/pkg/cni/cni_test.go index 778c83c03c..309ea23eff 100644 --- a/go-controller/pkg/cni/cni_test.go +++ b/go-controller/pkg/cni/cni_test.go @@ -8,11 +8,9 @@ import ( cnitypes "github.com/containernetworking/cni/pkg/types" current "github.com/containernetworking/cni/pkg/types/100" - nadv1 "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/kubernetes/fake" "github.com/ovn-kubernetes/libovsdb/client" @@ -20,6 +18,7 @@ import ( "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/cni/types" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/networkmanager" + "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing" libovsdbtest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/libovsdb" v1nadmocks "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/mocks/github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/listers/k8s.cni.cncf.io/v1" v1mocks "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing/mocks/k8s.io/client-go/listers/core/v1" @@ -96,6 +95,7 @@ var _ = Describe("Network Segmentation", func() { nadLister = v1nadmocks.NetworkAttachmentDefinitionLister{} clientSet = &ClientSet{ podLister: &podLister, + nadLister: &nadLister, kclient: fakeClientset, } kubeAuth = &KubeAPIAuth{ @@ -222,28 +222,20 @@ var _ = Describe("Network Segmentation", func() { Name: pr.PodName, Namespace: pr.PodNamespace, Annotations: map[string]string{ - "k8s.ovn.org/pod-networks": `{"default":{"ip_addresses":["100.10.10.3/24","fd44::33/64"],"mac_address":"0a:58:fd:98:00:01", "role":"infrastructure-locked"}, "meganet":{"ip_addresses":["10.10.10.30/24","fd10::3/64"],"mac_address":"02:03:04:05:06:07", "role":"primary"}}`, + "k8s.ovn.org/pod-networks": `{"default":{"ip_addresses":["100.10.10.3/24","fd44::33/64"],"mac_address":"0a:58:fd:98:00:01", "role":"infrastructure-locked"}, "foo-ns/meganet":{"ip_addresses":["10.10.10.30/24","fd10::3/64"],"mac_address":"02:03:04:05:06:07", "role":"primary"}}`, }, }, } - nad := &nadv1.NetworkAttachmentDefinition{ - ObjectMeta: metav1.ObjectMeta{ - Name: nadName, - Namespace: namespace, - }, - Spec: nadv1.NetworkAttachmentDefinitionSpec{ - Config: dummyPrimaryUDNConfig(namespace, nadName), - }, - } + nadMegaNet := testing.GenerateNADWithConfig("meganet", namespace, dummyPrimaryUDNConfig(namespace, "meganet")) nadNamespaceLister := &v1nadmocks.NetworkAttachmentDefinitionNamespaceLister{} - nadNamespaceLister.On("List", labels.Everything()).Return([]*nadv1.NetworkAttachmentDefinition{nad}, nil) nadLister.On("NetworkAttachmentDefinitions", "foo-ns").Return(nadNamespaceLister) - nadNetwork, err := util.ParseNADInfo(nad) + nadNamespaceLister.On("Get", "meganet").Return(nadMegaNet, nil) + nadNetwork, err := util.ParseNADInfo(nadMegaNet) Expect(err).NotTo(HaveOccurred()) fakeNetworkManager = &testnm.FakeNetworkManager{ PrimaryNetworks: make(map[string]util.NetInfo), } - fakeNetworkManager.PrimaryNetworks[nad.Namespace] = nadNetwork + fakeNetworkManager.PrimaryNetworks[nadMegaNet.Namespace] = nadNetwork getCNIResultStub = dummyGetCNIResult }) diff --git a/go-controller/pkg/cni/cniserver.go b/go-controller/pkg/cni/cniserver.go index 17b888dd63..19378f4483 100644 --- a/go-controller/pkg/cni/cniserver.go +++ b/go-controller/pkg/cni/cniserver.go @@ -11,6 +11,7 @@ import ( "time" "github.com/gorilla/mux" + nadv1Listers "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/listers/k8s.cni.cncf.io/v1" "k8s.io/client-go/kubernetes" corev1listers "k8s.io/client-go/listers/core/v1" @@ -58,17 +59,23 @@ func NewCNIServer( networkManager networkmanager.Interface, ovsClient client.Client, ) (*Server, error) { + var nadLister nadv1Listers.NetworkAttachmentDefinitionLister + if config.OvnKubeNode.Mode == types.NodeModeDPU { return nil, fmt.Errorf("unsupported ovnkube-node mode for CNI server: %s", config.OvnKubeNode.Mode) } router := mux.NewRouter() + if util.IsNetworkSegmentationSupportEnabled() { + nadLister = factory.NADInformer().Lister() + } s := &Server{ Server: http.Server{ Handler: router, }, clientSet: &ClientSet{ + nadLister: nadLister, podLister: corev1listers.NewPodLister(factory.LocalPodInformer().GetIndexer()), kclient: kclient, }, diff --git a/go-controller/pkg/cni/cnishim.go b/go-controller/pkg/cni/cnishim.go index 5f104e8c64..636a81f121 100644 --- a/go-controller/pkg/cni/cnishim.go +++ b/go-controller/pkg/cni/cnishim.go @@ -262,6 +262,16 @@ func (p *Plugin) CmdAdd(args *skel.CmdArgs) error { klog.Error(err.Error()) return err } + if response.PrimaryUDNPodInfo != nil { + primaryUDNPodRequest := response.PrimaryUDNPodReq + primaryUDNPodRequest.ctx, primaryUDNPodRequest.cancel = context.WithCancel(pr.ctx) + defer primaryUDNPodRequest.cancel() + err = primaryUDNCmdAddGetCNIResultFunc(response, getCNIResult, primaryUDNPodRequest, clientset, response.PrimaryUDNPodInfo) + if err != nil { + klog.Error(err.Error()) + return err + } + } } return types.PrintResult(result, conf.CNIVersion) diff --git a/go-controller/pkg/cni/types.go b/go-controller/pkg/cni/types.go index 7a20787d73..332898ac03 100644 --- a/go-controller/pkg/cni/types.go +++ b/go-controller/pkg/cni/types.go @@ -8,6 +8,7 @@ import ( current "github.com/containernetworking/cni/pkg/types/100" nadapi "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" + nadv1Listers "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/listers/k8s.cni.cncf.io/v1" corev1 "k8s.io/api/core/v1" "k8s.io/client-go/kubernetes" @@ -96,9 +97,11 @@ type CNIRequestMetrics struct { // Response sent to the OVN CNI plugin by the Server type Response struct { - Result *current.Result - PodIFInfo *PodInterfaceInfo - KubeAuth *KubeAPIAuth + Result *current.Result + PodIFInfo *PodInterfaceInfo + PrimaryUDNPodInfo *PodInterfaceInfo + PrimaryUDNPodReq *PodRequest + KubeAuth *KubeAPIAuth } func (response *Response) Marshal() ([]byte, error) { @@ -181,6 +184,7 @@ type ClientSet struct { PodInfoGetter kclient kubernetes.Interface podLister corev1listers.PodLister + nadLister nadv1Listers.NetworkAttachmentDefinitionLister } func NewClientSet(kclient kubernetes.Interface, podLister corev1listers.PodLister) *ClientSet { diff --git a/go-controller/pkg/cni/udn/primary_network.go b/go-controller/pkg/cni/udn/primary_network.go index c51e90a3dc..c751a6cbda 100644 --- a/go-controller/pkg/cni/udn/primary_network.go +++ b/go-controller/pkg/cni/udn/primary_network.go @@ -2,7 +2,14 @@ package udn import ( "fmt" + "strings" + nadapi "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" + nadv1Listers "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/listers/k8s.cni.cncf.io/v1" + nadutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils" + + corev1 "k8s.io/api/core/v1" + "k8s.io/client-go/tools/cache" "k8s.io/klog/v2" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/networkmanager" @@ -10,18 +17,37 @@ import ( "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/util" ) -// wait on a certain pod annotation related condition -type podAnnotWaitCond = func(map[string]string, string) (*util.PodAnnotation, bool) +// wait on a certain pod annotation related condition. in case of non-nil error, +// return error to abort the retry attempt +type podAnnotWaitCond = func(*corev1.Pod, string) (*util.PodAnnotation, bool, error) + +const resourceNameAnnot = "k8s.v1.cni.cncf.io/resourceName" type UserDefinedPrimaryNetwork struct { networkManager networkmanager.Interface + nadLister nadv1Listers.NetworkAttachmentDefinitionLister annotation *util.PodAnnotation - activeNetwork util.NetInfo + + // the pod's UDN primary network, and its associated NAD name and the resourceName defined in the NAD + activeNetwork util.NetInfo + nadName string + resourceName string + + // the pod's UDN primary interface's device information (when UDN's resourceName is defined) + // deviceInfo is extra device information stored by SRIOV device plugin in /var/run/k8s.cni.cncf.io/devinfo/dp + deviceInfo *nadapi.DeviceInfo + // deviceID is either a PCI address or an Auxiliary address (format .., i.e. mlx5_core.sf.2) of the VF assigned to the pod + deviceID string + // netdevName is the linux network device name (such as enps0) when the VF device is bound to a kernel driver + netdevName string + // isVFIO indicates if the VF device is bound to the VFIO driver, exposing direct device access to userspace applications or virtual machines + isVFIO bool } -func NewPrimaryNetwork(networkManager networkmanager.Interface) *UserDefinedPrimaryNetwork { +func NewPrimaryNetwork(networkManager networkmanager.Interface, nadLister nadv1Listers.NetworkAttachmentDefinitionLister) *UserDefinedPrimaryNetwork { return &UserDefinedPrimaryNetwork{ networkManager: networkManager, + nadLister: nadLister, } } @@ -30,8 +56,11 @@ func (p *UserDefinedPrimaryNetwork) InterfaceName() string { } func (p *UserDefinedPrimaryNetwork) NetworkDevice() string { - // TODO: Support for non VFIO devices like SRIOV have to be implemented - return "" + return p.netdevName +} + +func (p *UserDefinedPrimaryNetwork) NetworkDeviceInfo() (string, *nadapi.DeviceInfo, bool) { + return p.deviceID, p.deviceInfo, p.isVFIO } func (p *UserDefinedPrimaryNetwork) Annotation() *util.PodAnnotation { @@ -46,14 +75,7 @@ func (p *UserDefinedPrimaryNetwork) NetworkName() string { } func (p *UserDefinedPrimaryNetwork) NADName() string { - if p.activeNetwork == nil || p.activeNetwork.IsDefault() { - return "" - } - nads := p.activeNetwork.GetNADs() - if len(nads) < 1 { - return "" - } - return nads[0] + return p.nadName } func (p *UserDefinedPrimaryNetwork) MTU() int { @@ -64,38 +86,38 @@ func (p *UserDefinedPrimaryNetwork) MTU() int { } func (p *UserDefinedPrimaryNetwork) Found() bool { - return p.annotation != nil && p.activeNetwork != nil + // if the primary UDN interface is not VF backed, its deviceInfo is empty but not nil. + return p.annotation != nil && p.activeNetwork != nil && p.deviceInfo != nil } -func (p *UserDefinedPrimaryNetwork) WaitForPrimaryAnnotationFn(podName, namespace string, annotCondFn podAnnotWaitCond) podAnnotWaitCond { - return func(annotations map[string]string, nadName string) (*util.PodAnnotation, bool) { - annotation, isReady := annotCondFn(annotations, nadName) - if annotation == nil { - return nil, false +func (p *UserDefinedPrimaryNetwork) WaitForPrimaryAnnotationFn(annotCondFn podAnnotWaitCond) podAnnotWaitCond { + return func(pod *corev1.Pod, nadName string) (*util.PodAnnotation, bool, error) { + annotation, isReady, err := annotCondFn(pod, nadName) + if err != nil || !isReady { + return annotation, isReady, err } - if err := p.ensure(namespace, annotations, nadName, annotation); err != nil { - klog.Errorf("Failed ensuring user defined primary network for pod '%s/%s': %v", namespace, podName, err) - return nil, false + isReady, err = p.ensure(pod, nadName, annotation) + if err == nil && isReady { + return annotation, isReady, err } - return annotation, isReady + return nil, false, err } } -func (p *UserDefinedPrimaryNetwork) Ensure(namespace string, annotations map[string]string, nadName string) error { - return p.ensure(namespace, annotations, nadName, nil /* parse annotation */) -} - -func (p *UserDefinedPrimaryNetwork) ensure(namespace string, annotations map[string]string, nadName string, annotation *util.PodAnnotation) error { +func (p *UserDefinedPrimaryNetwork) ensure(pod *corev1.Pod, nadName string, annotation *util.PodAnnotation) (bool, error) { // non default network is not related to primary UDNs if nadName != types.DefaultNetworkName { - return nil + return true, nil } if annotation == nil { var err error - annotation, err = util.UnmarshalPodAnnotation(annotations, nadName) + annotation, err = util.UnmarshalPodAnnotation(pod.Annotations, nadName) if err != nil { - return fmt.Errorf("failed looking for ovn pod annotations: %w", err) + if util.IsAnnotationNotSetError(err) { + return false, nil + } + return false, err } } @@ -107,16 +129,19 @@ func (p *UserDefinedPrimaryNetwork) ensure(namespace string, annotations map[str // If default network is the primary there is nothing else to do if annotation.Role == types.NetworkRolePrimary { - return nil + return true, nil } - if err := p.ensureAnnotation(annotations); err != nil { - return fmt.Errorf("failed looking for primary network annotation: %w", err) + ready, err := p.ensureAnnotation(pod) + if !ready || err != nil { + return ready, err } - if err := p.ensureActiveNetwork(namespace); err != nil { - return fmt.Errorf("failed ensuring primary network for namespace: %q: %w", namespace, err) + err = p.ensureActiveNetwork(pod.Namespace) + if err != nil { + return false, err } - return nil + err = p.ensureNetworkDevice(pod) + return err == nil, err } func (p *UserDefinedPrimaryNetwork) ensureActiveNetwork(namespace string) error { @@ -130,30 +155,98 @@ func (p *UserDefinedPrimaryNetwork) ensureActiveNetwork(namespace string) error if activeNetwork.IsDefault() { return fmt.Errorf("missing primary user defined network NAD for namespace '%s'", namespace) } + p.activeNetwork = activeNetwork return nil } -func (p *UserDefinedPrimaryNetwork) ensureAnnotation(annotations map[string]string) error { +func (p *UserDefinedPrimaryNetwork) ensureAnnotation(pod *corev1.Pod) (bool, error) { if p.annotation != nil { - return nil + return true, nil } + annotations := pod.GetAnnotations() podNetworks, err := util.UnmarshalPodAnnotationAllNetworks(annotations) if err != nil { - return err + return false, err } - for nadName, podNetwork := range podNetworks { + for nadFullName, podNetwork := range podNetworks { if podNetwork.Role != types.NetworkRolePrimary { continue } - p.annotation, err = util.UnmarshalPodAnnotation(annotations, nadName) + annotation, err := util.UnmarshalPodAnnotation(annotations, nadFullName) + if err != nil { + if util.IsAnnotationNotSetError(err) { + return false, nil + } + return false, err + } + // To support for non VFIO devices like SRIOV, get the primary UDN's resource name + nadNamespace, nadName, err := cache.SplitMetaNamespaceKey(nadFullName) if err != nil { - return err + return false, fmt.Errorf("invalid NAD name %s", nadFullName) } - break + nad, err := p.nadLister.NetworkAttachmentDefinitions(nadNamespace).Get(nadName) + if err != nil { + return false, fmt.Errorf("failed to get primary UDN's network-attachment-definition %s: %v", nadFullName, err) + } + p.annotation = annotation + p.nadName = nadFullName + p.resourceName = nad.Annotations[resourceNameAnnot] + klog.Infof("Primary UDN for pod %s/%s NAD %s resource %s", pod.Namespace, pod.Name, p.nadName, p.resourceName) + + return true, nil + } + return false, nil +} + +func (p *UserDefinedPrimaryNetwork) ensureNetworkDevice(pod *corev1.Pod) error { + // everything has already been set, no need to retry + if p.deviceInfo != nil && (p.resourceName == "" || p.isVFIO || p.netdevName != "") { + return nil } - if p.annotation == nil { - return fmt.Errorf("missing network annotation with primary role '%+v'", annotations) + // this UDN primary network interface does not request specific device + if p.resourceName == "" { + p.deviceInfo = &nadapi.DeviceInfo{} + return nil } + deviceID, err := GetPodPrimaryUDNDeviceID(pod, p.resourceName) + if err != nil { + return fmt.Errorf("failed to get primary UDN device ID for pod %s/%s resource %s: %v", + pod.Namespace, pod.Name, p.resourceName, err) + } + if util.IsPCIDeviceName(deviceID) { + // DeviceID is a PCI address + p.isVFIO = util.GetSriovnetOps().IsVfPciVfioBound(deviceID) + } else if util.IsAuxDeviceName(deviceID) { + // DeviceID is an Auxiliary device name - .. + chunks := strings.Split(deviceID, ".") + if chunks[1] != "sf" { + return fmt.Errorf("primary UDN's device info for pod %s/%s resource %s deviceID %s: only SF auxiliary devices are supported", + pod.Namespace, pod.Name, p.resourceName, deviceID) + } + } else { + return fmt.Errorf("primary UDN's device info for pod %s/%s resource %s deviceID %s: unexpected PCI or Auxiliary device name", + pod.Namespace, pod.Name, p.resourceName, deviceID) + } + p.deviceID = deviceID + + deviceInfo, err := nadutils.LoadDeviceInfoFromDP(p.resourceName, deviceID) + if err != nil { + return fmt.Errorf("failed to load primary UDN's device info for pod %s/%s resource %s deviceID %s: %w", + pod.Namespace, pod.Name, p.resourceName, deviceID, err) + } + p.deviceInfo = deviceInfo + + if !p.isVFIO { + netdevName, err := util.GetNetdevNameFromDeviceId(deviceID, *deviceInfo) + if err != nil { + return fmt.Errorf("failed to get primary UDN's netdev name for pod %s/%s resource %s deviceID %s: %w", + pod.Namespace, pod.Name, p.resourceName, deviceID, err) + } + p.netdevName = netdevName + } + + klog.Infof("Primary UDN %s for pod %s/%s NAD %s resource %s deviceID %s deviceInfo %v netdevName %v isVFIO %v", + p.activeNetwork.GetNetworkName(), pod.Namespace, pod.Name, p.nadName, p.resourceName, deviceID, p.deviceInfo, p.netdevName, p.isVFIO) return nil } diff --git a/go-controller/pkg/cni/udn/primary_network_test.go b/go-controller/pkg/cni/udn/primary_network_test.go index e8ae809c72..c2831cf8d1 100644 --- a/go-controller/pkg/cni/udn/primary_network_test.go +++ b/go-controller/pkg/cni/udn/primary_network_test.go @@ -1,11 +1,12 @@ package udn import ( + "fmt" "testing" nadapi "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1" - "k8s.io/apimachinery/pkg/labels" + corev1 "k8s.io/api/core/v1" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config" ovntest "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/testing" @@ -21,14 +22,24 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { config.OVNKubernetesFeature.EnableMultiNetwork = true config.OVNKubernetesFeature.EnableNetworkSegmentation = true + namespace := "ns1" + nadName := "nad1" + wrongNadName := "nad2" + pod := &corev1.Pod{} + pod.Namespace = namespace + pod.Annotations = map[string]string{ + "k8s.ovn.org/pod-networks": `{"ns1/nad1": { + "role": "primary", + "mac_address": "0a:58:fd:98:00:01" + }}`, + } tests := []struct { description string - namespace string nadName string annotationFromFn *util.PodAnnotation isReadyFromFn bool - annotations map[string]string + pod *corev1.Pod nads []*nadapi.NetworkAttachmentDefinition getActiveNetworkError error expectedIsReady bool @@ -37,6 +48,7 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { expectedNADName string expectedNetworkName string expectedMTU int + expectedError error }{ { description: "With non default nad should be ready", @@ -45,6 +57,7 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { Role: types.NetworkRoleSecondary, }, isReadyFromFn: true, + pod: &corev1.Pod{}, expectedIsReady: true, expectedAnnotation: &util.PodAnnotation{ Role: types.NetworkRoleSecondary, @@ -55,6 +68,7 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { nadName: types.DefaultNetworkName, annotationFromFn: nil, isReadyFromFn: true, + pod: &corev1.Pod{}, expectedAnnotation: nil, expectedIsReady: false, }, @@ -65,6 +79,7 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { Role: types.NetworkRolePrimary, }, isReadyFromFn: true, + pod: &corev1.Pod{}, expectedAnnotation: &util.PodAnnotation{ Role: types.NetworkRolePrimary, }, @@ -77,6 +92,7 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { Role: "", }, isReadyFromFn: true, + pod: &corev1.Pod{}, expectedAnnotation: &util.PodAnnotation{ Role: types.NetworkRolePrimary, }, @@ -90,23 +106,23 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { Role: types.NetworkRoleInfrastructure, }, isReadyFromFn: true, + pod: &corev1.Pod{}, expectedIsReady: false, }, { description: "With primary network annotation and missing active network should return not ready", - namespace: "ns1", nadName: types.DefaultNetworkName, annotationFromFn: &util.PodAnnotation{ Role: types.NetworkRoleInfrastructure, }, - isReadyFromFn: true, - annotations: map[string]string{ - "k8s.ovn.org/pod-networks": `{"ns1/nad1": { - "role": "primary", - "mac_address": "0a:58:fd:98:00:01" - }}`, + nads: []*nadapi.NetworkAttachmentDefinition{ + ovntest.GenerateNAD("blue", wrongNadName, namespace, + types.Layer2Topology, "10.100.200.0/24", types.NetworkRolePrimary), }, + isReadyFromFn: true, + pod: pod, expectedIsReady: false, + expectedError: fmt.Errorf("failed to get primary UDN's network-attachment-definition %s/%s: not found", namespace, nadName), }, { description: "With missing primary network annotation and active network should return not ready", @@ -115,34 +131,29 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { Role: types.NetworkRoleInfrastructure, }, isReadyFromFn: true, + pod: &corev1.Pod{}, nads: []*nadapi.NetworkAttachmentDefinition{ - ovntest.GenerateNAD("blue", "nad1", "ns1", + ovntest.GenerateNAD("blue", nadName, namespace, types.Layer2Topology, "10.100.200.0/24", types.NetworkRolePrimary), }, expectedIsReady: false, }, { description: "With primary network annotation and active network should return ready", - namespace: "ns1", nadName: types.DefaultNetworkName, annotationFromFn: &util.PodAnnotation{ Role: types.NetworkRoleInfrastructure, }, isReadyFromFn: true, - annotations: map[string]string{ - "k8s.ovn.org/pod-networks": `{"ns1/nad1": { - "role": "primary", - "mac_address": "0a:58:fd:98:00:01" - }}`, - }, + pod: pod, nads: []*nadapi.NetworkAttachmentDefinition{ - ovntest.GenerateNAD("blue", "nad1", "ns1", + ovntest.GenerateNAD("blue", nadName, namespace, types.Layer2Topology, "10.100.200.0/24", types.NetworkRolePrimary), }, expectedIsReady: true, expectedFound: true, expectedNetworkName: "blue", - expectedNADName: "ns1/nad1", + expectedNADName: util.GetNADName(namespace, nadName), expectedAnnotation: &util.PodAnnotation{ Role: types.NetworkRoleInfrastructure, }, @@ -150,26 +161,20 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { }, { description: "With primary network annotation and active network and no MTU should return ready with default MTU", - namespace: "ns1", nadName: types.DefaultNetworkName, annotationFromFn: &util.PodAnnotation{ Role: types.NetworkRoleInfrastructure, }, isReadyFromFn: true, - annotations: map[string]string{ - "k8s.ovn.org/pod-networks": `{"ns1/nad1": { - "role": "primary", - "mac_address": "0a:58:fd:98:00:01" - }}`, - }, + pod: pod, nads: []*nadapi.NetworkAttachmentDefinition{ - ovntest.GenerateNADWithoutMTU("blue", "nad1", "ns1", + ovntest.GenerateNADWithoutMTU("blue", nadName, namespace, types.Layer2Topology, "10.100.200.0/24", types.NetworkRolePrimary), }, expectedIsReady: true, expectedFound: true, expectedNetworkName: "blue", - expectedNADName: "ns1/nad1", + expectedNADName: util.GetNADName(namespace, nadName), expectedAnnotation: &util.PodAnnotation{ Role: types.NetworkRoleInfrastructure, }, @@ -184,10 +189,16 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { config.IPv6Mode = true nadLister := v1nadmocks.NetworkAttachmentDefinitionLister{} nadNamespaceLister := v1nadmocks.NetworkAttachmentDefinitionNamespaceLister{} - nadLister.On("NetworkAttachmentDefinitions", tt.namespace).Return(&nadNamespaceLister) - nadNamespaceLister.On("List", labels.Everything()).Return(tt.nads, nil) - waitCond := func(map[string]string, string) (*util.PodAnnotation, bool) { - return tt.annotationFromFn, tt.isReadyFromFn + if tt.nads != nil { + nadLister.On("NetworkAttachmentDefinitions", tt.pod.Namespace).Return(&nadNamespaceLister) + if tt.expectedNADName != "" { + nadNamespaceLister.On("Get", nadName).Return(tt.nads[0], nil) + } else { + nadNamespaceLister.On("Get", nadName).Return(nil, fmt.Errorf("not found")) + } + } + waitCond := func(*corev1.Pod, string) (*util.PodAnnotation, bool, error) { + return tt.annotationFromFn, tt.isReadyFromFn, nil } fakeNetworkManager := &networkmanager.FakeNetworkManager{ @@ -205,13 +216,18 @@ func TestWaitForPrimaryAnnotationFn(t *testing.T) { } } - userDefinedPrimaryNetwork := NewPrimaryNetwork(fakeNetworkManager) - obtainedAnnotation, obtainedIsReady := userDefinedPrimaryNetwork.WaitForPrimaryAnnotationFn( - "testPod", tt.namespace, waitCond)(tt.annotations, tt.nadName) + userDefinedPrimaryNetwork := NewPrimaryNetwork(fakeNetworkManager, &nadLister) + obtainedAnnotation, obtainedIsReady, err := userDefinedPrimaryNetwork.WaitForPrimaryAnnotationFn( + waitCond)(tt.pod, tt.nadName) obtainedFound := userDefinedPrimaryNetwork.Found() obtainedNetworkName := userDefinedPrimaryNetwork.NetworkName() obtainedNADName := userDefinedPrimaryNetwork.NADName() obtainedMTU := userDefinedPrimaryNetwork.MTU() + if tt.expectedError == nil { + g.Expect(err).ToNot(HaveOccurred(), "should not return error") + } else { + g.Expect(err).To(MatchError(tt.expectedError.Error()), "should return expected error") + } g.Expect(obtainedIsReady).To(Equal(tt.expectedIsReady), "should return expected readiness") g.Expect(obtainedFound).To(Equal(tt.expectedFound), "should return expected found flag") g.Expect(obtainedNetworkName).To(Equal(tt.expectedNetworkName), "should return expected network name") diff --git a/go-controller/pkg/cni/udn/resource.go b/go-controller/pkg/cni/udn/resource.go new file mode 100644 index 0000000000..5dd6933554 --- /dev/null +++ b/go-controller/pkg/cni/udn/resource.go @@ -0,0 +1,42 @@ +package udn + +import ( + "fmt" + + "gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/kubeletclient" + "gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types" + + corev1 "k8s.io/api/core/v1" + "k8s.io/klog/v2" +) + +func getPodResourceInfo(pod *corev1.Pod, resourceName string) (*types.ResourceInfo, error) { + podDesc := fmt.Sprintf("%s/%s", pod.Namespace, pod.Name) + ck, err := kubeletclient.GetResourceClient("") + if err != nil { + return nil, fmt.Errorf("failed to get a ResourceClient instance get resource info for pod %s: %v", podDesc, err) + } + resourceMap, err := ck.GetPodResourceMap(pod) + if err != nil { + return nil, fmt.Errorf("failed to get resources allocated for pod %s from ResourceClient: %v", podDesc, err) + } + klog.V(5).Infof("ResourceMap for pod %s: %+v", pod, resourceMap) + + entry, ok := resourceMap[resourceName] + if !ok { + return nil, fmt.Errorf("failed to get resources allocated for pod %s: no resources for resource %s", podDesc, resourceName) + } + return entry, nil +} + +// GetPodPrimaryUDNDeviceID gets last deviceId of the specified resources allocated for the given Pod +func GetPodPrimaryUDNDeviceID(pod *corev1.Pod, resourceName string) (string, error) { + entry, err := getPodResourceInfo(pod, resourceName) + if err != nil { + return "", err + } + if len(entry.DeviceIDs) == 0 { + return "", fmt.Errorf("no device IDs found for pod %s/%s, resource %s", pod.Namespace, pod.Name, resourceName) + } + return entry.DeviceIDs[len(entry.DeviceIDs)-1], nil +} diff --git a/go-controller/pkg/cni/utils.go b/go-controller/pkg/cni/utils.go index 8ba91a5586..2f063c6aea 100644 --- a/go-controller/pkg/cni/utils.go +++ b/go-controller/pkg/cni/utils.go @@ -16,27 +16,44 @@ import ( ) // wait on a certain pod annotation related condition -type podAnnotWaitCond = func(map[string]string, string) (*util.PodAnnotation, bool) +// return error to abort the retry attempt +type podAnnotWaitCond = func(*corev1.Pod, string) (*util.PodAnnotation, bool, error) // isOvnReady is a wait condition for OVN master to set pod-networks annotation -func isOvnReady(podAnnotation map[string]string, nadName string) (*util.PodAnnotation, bool) { - podNADAnnotation, err := util.UnmarshalPodAnnotation(podAnnotation, nadName) - return podNADAnnotation, err == nil +func isOvnReady(pod *corev1.Pod, nadName string) (*util.PodAnnotation, bool, error) { + podNADAnnotation, err := util.UnmarshalPodAnnotation(pod.Annotations, nadName) + if err != nil { + if util.IsAnnotationNotSetError(err) { + return nil, false, nil + } + return nil, false, err + } + return podNADAnnotation, true, nil } // isDPUReady is a wait condition which waits for OVN master to set pod-networks annotation and // ovnkube running on DPU to set connection-status pod annotation and its status is Ready -func isDPUReady(podAnnotation map[string]string, nadName string) (*util.PodAnnotation, bool) { - podNADAnnotation, ready := isOvnReady(podAnnotation, nadName) - if ready { - // check DPU connection status - if status, err := util.UnmarshalPodDPUConnStatus(podAnnotation, nadName); err == nil { - if status.Status == util.DPUConnectionStatusReady { - return podNADAnnotation, true +func isDPUReady(annotCondFn podAnnotWaitCond, nadName string) podAnnotWaitCond { + return func(pod *corev1.Pod, nad string) (annotation *util.PodAnnotation, ready bool, err error) { + if annotCondFn != nil { + annotation, ready, err = annotCondFn(pod, nad) + if err != nil || !ready { + return + } + } + // check DPU connection status of the given nad name + status, err := util.UnmarshalPodDPUConnStatus(pod.Annotations, nadName) + if err != nil { + if util.IsAnnotationNotSetError(err) { + return annotation, false, nil } + return annotation, false, err + } + if status.Status == util.DPUConnectionStatusReady { + return annotation, true, nil } + return annotation, false, fmt.Errorf("DPU plumb failed: connection status is %v", status.Status) } - return nil, false } // getPod tries to read a Pod object from the informer cache, or if the pod @@ -85,8 +102,10 @@ func GetPodWithAnnotations(ctx context.Context, getter PodInfoGetter, } // drop through to try again } else if pod != nil { - podNADAnnotation, ready := annotCond(pod.Annotations, nadName) - if ready { + podNADAnnotation, ready, err := annotCond(pod, nadName) + if err != nil { + return nil, nil, nil, err + } else if ready { return pod, pod.Annotations, podNADAnnotation, nil } } diff --git a/go-controller/pkg/cni/utils_test.go b/go-controller/pkg/cni/utils_test.go index d6f2c470e6..60f5d4d0af 100644 --- a/go-controller/pkg/cni/utils_test.go +++ b/go-controller/pkg/cni/utils_test.go @@ -53,7 +53,15 @@ func newFakeClientSet(pod *corev1.Pod, podNamespaceLister *mocks.PodNamespaceLis var _ = Describe("CNI Utils tests", func() { var defaultPodAnnotation string + var pod *corev1.Pod + + const ( + namespace = "some-ns" + podName = "some-pod" + ) + BeforeEach(func() { + pod = newPod(namespace, podName, nil) defaultPodAnnotation = `{ "default":{"ip_addresses":["192.168.2.3/24"], "mac_address":"0a:58:c0:a8:02:03", @@ -66,13 +74,15 @@ var _ = Describe("CNI Utils tests", func() { Context("isOvnReady", func() { It("Returns true if OVN pod network annotation exists", func() { podAnnot := map[string]string{util.OvnPodAnnotationName: defaultPodAnnotation} - _, ready := isOvnReady(podAnnot, ovntypes.DefaultNetworkName) + pod.Annotations = podAnnot + _, ready, _ := isOvnReady(pod, ovntypes.DefaultNetworkName) Expect(ready).To(BeTrue()) }) It("Returns false if OVN pod network annotation does not exist", func() { podAnnot := map[string]string{} - _, ready := isOvnReady(podAnnot, ovntypes.DefaultNetworkName) + pod.Annotations = podAnnot + _, ready, _ := isOvnReady(pod, ovntypes.DefaultNetworkName) Expect(ready).To(BeFalse()) }) }) @@ -82,7 +92,9 @@ var _ = Describe("CNI Utils tests", func() { podAnnot := map[string]string{ util.OvnPodAnnotationName: defaultPodAnnotation, util.DPUConnectionStatusAnnot: `{"Status":"Ready"}`} - _, ready := isDPUReady(podAnnot, ovntypes.DefaultNetworkName) + pod.Annotations = podAnnot + _, ready, err := isDPUReady(nil, ovntypes.DefaultNetworkName)(pod, ovntypes.DefaultNetworkName) + Expect(err).ToNot(HaveOccurred()) Expect(ready).To(BeTrue()) }) @@ -90,7 +102,9 @@ var _ = Describe("CNI Utils tests", func() { podAnnot := map[string]string{ util.OvnPodAnnotationName: defaultPodAnnotation, util.DPUConnectionStatusAnnot: `{"Status":"NotReady"}`} - _, ready := isDPUReady(podAnnot, ovntypes.DefaultNetworkName) + pod.Annotations = podAnnot + _, ready, err := isDPUReady(nil, ovntypes.DefaultNetworkName)(pod, ovntypes.DefaultNetworkName) + Expect(err).To(HaveOccurred()) Expect(ready).To(BeFalse()) }) @@ -98,35 +112,34 @@ var _ = Describe("CNI Utils tests", func() { podAnnot := map[string]string{ util.OvnPodAnnotationName: defaultPodAnnotation, util.DPUConnectionStatusAnnot: `{"Foo":"Bar"}`} - _, ready := isDPUReady(podAnnot, ovntypes.DefaultNetworkName) + pod.Annotations = podAnnot + _, ready, err := isDPUReady(nil, ovntypes.DefaultNetworkName)(pod, ovntypes.DefaultNetworkName) + Expect(err).To(HaveOccurred()) Expect(ready).To(BeFalse()) }) It("Returns false if dpu.connection-status is not present", func() { podAnnot := map[string]string{util.OvnPodAnnotationName: defaultPodAnnotation} - _, ready := isDPUReady(podAnnot, ovntypes.DefaultNetworkName) + pod.Annotations = podAnnot + _, ready, err := isDPUReady(nil, ovntypes.DefaultNetworkName)(pod, ovntypes.DefaultNetworkName) + Expect(err).ToNot(HaveOccurred()) Expect(ready).To(BeFalse()) }) - It("Returns false if OVN pod-networks is not present", func() { + It("Returns false if OVN pod annotation is empty", func() { podAnnot := map[string]string{} - _, ready := isDPUReady(podAnnot, ovntypes.DefaultNetworkName) + pod.Annotations = podAnnot + _, ready, err := isDPUReady(nil, ovntypes.DefaultNetworkName)(pod, ovntypes.DefaultNetworkName) + Expect(err).ToNot(HaveOccurred()) Expect(ready).To(BeFalse()) }) }) Context("GetPodWithAnnotations", func() { var podNamespaceLister mocks.PodNamespaceLister - var pod *corev1.Pod - - const ( - namespace = "some-ns" - podName = "some-pod" - ) BeforeEach(func() { podNamespaceLister = mocks.PodNamespaceLister{} - pod = newPod(namespace, podName, nil) }) It("Returns Pod annotation if annotation condition is met", func() { @@ -135,11 +148,11 @@ var _ = Describe("CNI Utils tests", func() { ctx, cancelFunc := context.WithTimeout(context.Background(), 20*time.Millisecond) defer cancelFunc() - cond := func(podAnnotation map[string]string, _ string) (*util.PodAnnotation, bool) { - if _, ok := podAnnotation["foo"]; ok { - return nil, true + cond := func(pod *corev1.Pod, _ string) (*util.PodAnnotation, bool, error) { + if _, ok := pod.Annotations["foo"]; ok { + return nil, true, nil } - return nil, false + return nil, false, nil } clientset := newFakeClientSet(pod, &podNamespaceLister) @@ -154,8 +167,8 @@ var _ = Describe("CNI Utils tests", func() { It("Returns with Error if context is canceled", func() { ctx, cancelFunc := context.WithCancel(context.Background()) - cond := func(map[string]string, string) (*util.PodAnnotation, bool) { - return nil, false + cond := func(*corev1.Pod, string) (*util.PodAnnotation, bool, error) { + return nil, false, nil } go func() { @@ -175,12 +188,12 @@ var _ = Describe("CNI Utils tests", func() { defer cancelFunc() calledOnce := false - cond := func(map[string]string, string) (*util.PodAnnotation, bool) { + cond := func(*corev1.Pod, string) (*util.PodAnnotation, bool, error) { if calledOnce { - return nil, true + return nil, true, nil } calledOnce = true - return nil, false + return nil, false, nil } clientset := newFakeClientSet(pod, &podNamespaceLister) @@ -194,8 +207,8 @@ var _ = Describe("CNI Utils tests", func() { ctx, cancelFunc := context.WithTimeout(context.Background(), 1*time.Second) defer cancelFunc() - cond := func(map[string]string, string) (*util.PodAnnotation, bool) { - return nil, false + cond := func(*corev1.Pod, string) (*util.PodAnnotation, bool, error) { + return nil, false, nil } clientset := newFakeClientSet(pod, &podNamespaceLister) @@ -211,17 +224,17 @@ var _ = Describe("CNI Utils tests", func() { defer cancelFunc() calledOnce := false - cond := func(map[string]string, string) (*util.PodAnnotation, bool) { + cond := func(*corev1.Pod, string) (*util.PodAnnotation, bool, error) { if calledOnce { - return nil, true + return nil, true, nil } calledOnce = true - return nil, false + return nil, false, nil } clientset := newFakeClientSet(pod, &podNamespaceLister) - podNamespaceLister.On("Get", mock.AnythingOfType("string")).Return(nil, apierrors.NewNotFound(corev1.Resource("pod"), name)) + podNamespaceLister.On("Get", mock.AnythingOfType("string")).Return(nil, apierrors.NewNotFound(corev1.Resource("pod"), podName)) _, _, _, err := GetPodWithAnnotations(ctx, clientset, namespace, podName, ovntypes.DefaultNetworkName, cond) Expect(err).ToNot(HaveOccurred()) }) @@ -230,13 +243,13 @@ var _ = Describe("CNI Utils tests", func() { ctx, cancelFunc := context.WithTimeout(context.Background(), 1*time.Second) defer cancelFunc() - cond := func(map[string]string, string) (*util.PodAnnotation, bool) { - return nil, false + cond := func(*corev1.Pod, string) (*util.PodAnnotation, bool, error) { + return nil, false, nil } clientset := newFakeClientSet(nil, &podNamespaceLister) - podNamespaceLister.On("Get", mock.AnythingOfType("string")).Return(nil, apierrors.NewNotFound(corev1.Resource("pod"), name)) + podNamespaceLister.On("Get", mock.AnythingOfType("string")).Return(nil, apierrors.NewNotFound(corev1.Resource("pod"), podName)) _, _, _, err := GetPodWithAnnotations(ctx, clientset, namespace, podName, ovntypes.DefaultNetworkName, cond) Expect(err).To(HaveOccurred()) Expect(err.Error()).To(ContainSubstring("timed out waiting for pod after 1s")) diff --git a/go-controller/pkg/config/config.go b/go-controller/pkg/config/config.go index 596cc7b3ca..877d64fdfe 100644 --- a/go-controller/pkg/config/config.go +++ b/go-controller/pkg/config/config.go @@ -134,6 +134,7 @@ var ( // OVNKubernetesFeatureConfig holds OVN-Kubernetes feature enhancement config file parameters and command-line overrides OVNKubernetesFeature = OVNKubernetesFeatureConfig{ EgressIPReachabiltyTotalTimeout: 1, + AdvertisedUDNIsolationMode: AdvertisedUDNIsolationModeStrict, } // OvnNorth holds northbound OVN database client and server authentication and location details @@ -434,6 +435,9 @@ type OVNKubernetesFeatureConfig struct { EnableServiceTemplateSupport bool `gcfg:"enable-svc-template-support"` EnableObservability bool `gcfg:"enable-observability"` EnableNetworkQoS bool `gcfg:"enable-network-qos"` + // This feature requires a kernel fix https://github.com/torvalds/linux/commit/7f3287db654395f9c5ddd246325ff7889f550286 + // to work on a kind cluster. Flag allows to disable it for current CI, will be turned on when github runners have this fix. + AdvertisedUDNIsolationMode string `gcfg:"advertised-udn-isolation-mode"` } // GatewayMode holds the node gateway mode @@ -448,6 +452,13 @@ const ( GatewayModeLocal GatewayMode = "local" ) +const ( + // AdvertisedUDNIsolationModeStrict pod isolation across advertised UDN networks is enabled. + AdvertisedUDNIsolationModeStrict = "strict" + // AdvertisedUDNIsolationModeLoose pod isolation across advertised UDN networks is disabled. + AdvertisedUDNIsolationModeLoose = "loose" +) + // GatewayConfig holds node gateway-related parsed config file parameters and command-line overrides type GatewayConfig struct { // Mode is the gateway mode; if may be either empty (disabled), "shared", or "local" @@ -498,7 +509,7 @@ type GatewayConfig struct { // EphemeralPortRange is the range of ports used by egress SNAT operations in OVN. Specifically for NAT where // the source IP of the NAT will be a shared Node IP address. If unset, the value will be determined by sysctl lookup // for the kernel's ephemeral range: net.ipv4.ip_local_port_range. Format is "-". - EphemeralPortRange string `gfcg:"ephemeral-port-range"` + EphemeralPortRange string `gcfg:"ephemeral-port-range"` } // OvnAuthConfig holds client authentication and location details for @@ -1102,6 +1113,12 @@ var OVNK8sFeatureFlags = []cli.Flag{ Destination: &cliConfig.OVNKubernetesFeature.EnableRouteAdvertisements, Value: OVNKubernetesFeature.EnableRouteAdvertisements, }, + &cli.StringFlag{ + Name: "advertised-udn-isolation-mode", + Usage: "Configure to use pod isolation for BGP advertised UDN networks. Valid values are 'strict' or 'loose'.", + Destination: &cliConfig.OVNKubernetesFeature.AdvertisedUDNIsolationMode, + Value: OVNKubernetesFeature.AdvertisedUDNIsolationMode, + }, &cli.BoolFlag{ Name: "enable-stateless-netpol", Usage: "Configure to use stateless network policy feature with ovn-kubernetes.", @@ -2014,6 +2031,10 @@ func buildOVNKubernetesFeatureConfig(cli, file *config) error { if err := overrideFields(&OVNKubernetesFeature, &cli.OVNKubernetesFeature, &savedOVNKubernetesFeature); err != nil { return err } + if OVNKubernetesFeature.AdvertisedUDNIsolationMode != AdvertisedUDNIsolationModeStrict && OVNKubernetesFeature.AdvertisedUDNIsolationMode != AdvertisedUDNIsolationModeLoose { + return fmt.Errorf("invalid advertised-udn-isolation-mode %q: expect one of %s or %s", + OVNKubernetesFeature.AdvertisedUDNIsolationMode, AdvertisedUDNIsolationModeStrict, AdvertisedUDNIsolationModeLoose) + } return nil } diff --git a/go-controller/pkg/config/config_test.go b/go-controller/pkg/config/config_test.go index 39e7fa41bc..c5a032c92c 100644 --- a/go-controller/pkg/config/config_test.go +++ b/go-controller/pkg/config/config_test.go @@ -229,6 +229,7 @@ enable-multi-networkpolicy=false enable-network-segmentation=false enable-preconfigured-udn-addresses=false enable-route-advertisements=false +advertised-udn-isolation-mode=strict enable-interconnect=false enable-multi-external-gateway=false enable-admin-network-policy=false @@ -346,6 +347,7 @@ var _ = Describe("Config Operations", func() { gomega.Expect(OVNKubernetesFeature.EnableMultiExternalGateway).To(gomega.BeFalse()) gomega.Expect(OVNKubernetesFeature.EnableAdminNetworkPolicy).To(gomega.BeFalse()) gomega.Expect(OVNKubernetesFeature.EnablePersistentIPs).To(gomega.BeFalse()) + gomega.Expect(OVNKubernetesFeature.AdvertisedUDNIsolationMode).To(gomega.Equal(AdvertisedUDNIsolationModeStrict)) for _, a := range []OvnAuthConfig{OvnNorth, OvnSouth} { gomega.Expect(a.Scheme).To(gomega.Equal(OvnDBSchemeUnix)) @@ -601,6 +603,7 @@ var _ = Describe("Config Operations", func() { "enable-network-segmentation=true", "enable-preconfigured-udn-addresses=true", "enable-route-advertisements=true", + "advertised-udn-isolation-mode=loose", "enable-interconnect=true", "enable-multi-external-gateway=true", "enable-admin-network-policy=true", @@ -692,6 +695,7 @@ var _ = Describe("Config Operations", func() { gomega.Expect(OVNKubernetesFeature.EnableNetworkSegmentation).To(gomega.BeTrue()) gomega.Expect(OVNKubernetesFeature.EnablePreconfiguredUDNAddresses).To(gomega.BeTrue()) gomega.Expect(OVNKubernetesFeature.EnableRouteAdvertisements).To(gomega.BeTrue()) + gomega.Expect(OVNKubernetesFeature.AdvertisedUDNIsolationMode).To(gomega.Equal(AdvertisedUDNIsolationModeLoose)) gomega.Expect(OVNKubernetesFeature.EnableInterconnect).To(gomega.BeTrue()) gomega.Expect(OVNKubernetesFeature.EnableMultiExternalGateway).To(gomega.BeTrue()) gomega.Expect(OVNKubernetesFeature.EnableAdminNetworkPolicy).To(gomega.BeTrue()) @@ -800,6 +804,7 @@ var _ = Describe("Config Operations", func() { gomega.Expect(OVNKubernetesFeature.EnableNetworkSegmentation).To(gomega.BeTrue()) gomega.Expect(OVNKubernetesFeature.EnablePreconfiguredUDNAddresses).To(gomega.BeTrue()) gomega.Expect(OVNKubernetesFeature.EnableRouteAdvertisements).To(gomega.BeTrue()) + gomega.Expect(OVNKubernetesFeature.AdvertisedUDNIsolationMode).To(gomega.Equal(AdvertisedUDNIsolationModeLoose)) gomega.Expect(OVNKubernetesFeature.EnableMultiNetworkPolicy).To(gomega.BeTrue()) gomega.Expect(OVNKubernetesFeature.EnableInterconnect).To(gomega.BeTrue()) gomega.Expect(OVNKubernetesFeature.EnableMultiExternalGateway).To(gomega.BeTrue()) @@ -876,6 +881,7 @@ var _ = Describe("Config Operations", func() { "-enable-network-segmentation=true", "-enable-preconfigured-udn-addresses=true", "-enable-route-advertisements=true", + "-advertised-udn-isolation-mode=loose", "-enable-interconnect=true", "-enable-multi-external-gateway=true", "-enable-admin-network-policy=true", @@ -1593,6 +1599,24 @@ foo=bar gomega.Expect(err).NotTo(gomega.HaveOccurred()) }) + It("rejects a config with invalid advertised-udn-isolation-mode", func() { + err := os.WriteFile(cfgFile.Name(), []byte(`[ovnkubernetesfeature] +advertised-udn-isolation-mode=foo +`), 0o644) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + + app.Action = func(ctx *cli.Context) error { + _, err := InitConfig(ctx, kexec.New(), nil) + gomega.Expect(err).To(gomega.MatchError( + gomega.ContainSubstring("invalid advertised-udn-isolation-mode \"foo\": expect one of strict or loose")), + ) + return nil + } + cliArgs := []string{app.Name, "-config-file=" + cfgFile.Name()} + err = app.Run(cliArgs) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + }) + It("rejects a config with invalid syntax", func() { err := os.WriteFile(cfgFile.Name(), []byte(`[default] mtu=1234 diff --git a/go-controller/pkg/controllermanager/controller_manager.go b/go-controller/pkg/controllermanager/controller_manager.go index 27db274d05..55aaec6831 100644 --- a/go-controller/pkg/controllermanager/controller_manager.go +++ b/go-controller/pkg/controllermanager/controller_manager.go @@ -2,6 +2,7 @@ package controllermanager import ( "context" + "errors" "fmt" "sync" "time" @@ -195,15 +196,15 @@ func (cm *ControllerManager) CleanupStaleNetworks(validNetworks ...util.NetInfo) } } - if util.IsRouteAdvertisementsEnabled() { - // Remove stale subnets from the advertised networks address set used for isolation - // NOTE: network reconciliation will take care of removing the subnets for existing networks that are no longer - // advertised. - addressSetFactory := addressset.NewOvnAddressSetFactory(cm.nbClient, config.IPv4Mode, config.IPv6Mode) - advertisedSubnets, err := addressSetFactory.GetAddressSet(ovn.GetAdvertisedNetworkSubnetsAddressSetDBIDs()) - if err != nil { - return fmt.Errorf("failed to get advertised subnets addresset %s: %w", ovn.GetAdvertisedNetworkSubnetsAddressSetDBIDs(), err) - } + // Remove stale subnets from the advertised networks address set used for isolation + // NOTE: network reconciliation will take care of removing the subnets for existing networks that are no longer + // advertised. + addressSetFactory := addressset.NewOvnAddressSetFactory(cm.nbClient, config.IPv4Mode, config.IPv6Mode) + advertisedSubnets, err := addressSetFactory.GetAddressSet(ovn.GetAdvertisedNetworkSubnetsAddressSetDBIDs()) + if err != nil && !errors.Is(err, libovsdbclient.ErrNotFound) { + return fmt.Errorf("failed to get advertised subnets addresset %s: %w", ovn.GetAdvertisedNetworkSubnetsAddressSetDBIDs(), err) + } + if advertisedSubnets != nil { v4AdvertisedSubnets, v6AdvertisedSubnets := advertisedSubnets.GetAddresses() var invalidSubnets []string for _, subnet := range append(v4AdvertisedSubnets, v6AdvertisedSubnets...) { diff --git a/go-controller/pkg/controllermanager/node_controller_manager.go b/go-controller/pkg/controllermanager/node_controller_manager.go index aca81d30f7..94fbf18fd2 100644 --- a/go-controller/pkg/controllermanager/node_controller_manager.go +++ b/go-controller/pkg/controllermanager/node_controller_manager.go @@ -60,7 +60,7 @@ func (ncm *NodeControllerManager) NewNetworkController(nInfo util.NetInfo) (netw // Pass a shallow clone of the watch factory, this allows multiplexing // informers for secondary networks. return node.NewSecondaryNodeNetworkController(ncm.newCommonNetworkControllerInfo(ncm.watchFactory.(*factory.WatchFactory).ShallowClone()), - nInfo, ncm.vrfManager, ncm.ruleManager, ncm.defaultNodeNetworkController.Gateway) + nInfo, ncm.networkManager.Interface(), ncm.vrfManager, ncm.ruleManager, ncm.defaultNodeNetworkController.Gateway) } return nil, fmt.Errorf("topology type %s not supported", topoType) } diff --git a/go-controller/pkg/node/base_node_network_controller_dpu.go b/go-controller/pkg/node/base_node_network_controller_dpu.go index 59530ecf16..26de1386c1 100644 --- a/go-controller/pkg/node/base_node_network_controller_dpu.go +++ b/go-controller/pkg/node/base_node_network_controller_dpu.go @@ -103,6 +103,9 @@ func (bnnc *BaseNodeNetworkController) watchPodsDPU() (*factory.Handler, error) netName := bnnc.GetNetworkName() return bnnc.watchFactory.AddPodHandler(cache.ResourceEventHandlerFuncs{ AddFunc: func(obj interface{}) { + var activeNetwork util.NetInfo + var err error + pod := obj.(*corev1.Pod) klog.V(5).Infof("Add for Pod: %s/%s for network %s", pod.Namespace, pod.Name, netName) if util.PodWantsHostNetwork(pod) { @@ -113,7 +116,15 @@ func (bnnc *BaseNodeNetworkController) watchPodsDPU() (*factory.Handler, error) // For default network, NAD name is DefaultNetworkName. nadToDPUCDMap := map[string]*util.DPUConnectionDetails{} if bnnc.IsSecondary() { - on, networkMap, err := util.GetPodNADToNetworkMapping(pod, bnnc.GetNetInfo()) + if bnnc.IsPrimaryNetwork() { + activeNetwork, err = bnnc.networkManager.GetActiveNetworkForNamespace(pod.Namespace) + if err != nil { + klog.Errorf("Failed looking for the active network for namespace %s: %v", pod.Namespace, err) + return + } + } + + on, networkMap, err := util.GetPodNADToNetworkMappingWithActiveNetwork(pod, bnnc.GetNetInfo(), activeNetwork) if err != nil || !on { if err != nil { // configuration error, no need to retry, do not return error diff --git a/go-controller/pkg/node/base_node_network_controller_dpu_test.go b/go-controller/pkg/node/base_node_network_controller_dpu_test.go index cd9457708e..d372d49777 100644 --- a/go-controller/pkg/node/base_node_network_controller_dpu_test.go +++ b/go-controller/pkg/node/base_node_network_controller_dpu_test.go @@ -118,7 +118,7 @@ var _ = Describe("Node DPU tests", func() { apbExternalRouteClient := adminpolicybasedrouteclient.NewSimpleClientset() factoryMock = factorymocks.NodeWatchFactory{} cnnci := newCommonNodeNetworkControllerInfo(nil, &kubeMock, apbExternalRouteClient, &factoryMock, nil, "", routeManager) - dnnc = newDefaultNodeNetworkController(cnnci, nil, nil, routeManager, nil) + dnnc = newDefaultNodeNetworkController(cnnci, nil, nil, routeManager, nil, nil) podInformer = coreinformermocks.PodInformer{} podNamespaceLister = v1mocks.PodNamespaceLister{} @@ -190,7 +190,7 @@ var _ = Describe("Node DPU tests", func() { It("Fails if GetPCIFromDeviceName fails", func() { sriovnetOpsMock.On("GetVfRepresentorDPU", "0", "9").Return(vfRep, nil) sriovnetOpsMock.On("GetPCIFromDeviceName", vfRep).Return("", fmt.Errorf("could not find PCI Address")) - podNamespaceLister.On("Get", mock.AnythingOfType("string")).Return(pod, nil) + podNamespaceLister.On("Get", mock.AnythingOfType("string")).Return(&pod, nil) // call addRepPort() err := dnnc.addRepPort(&pod, &scd, ifInfo, clientset) diff --git a/go-controller/pkg/node/bridgeconfig/bridgeflows.go b/go-controller/pkg/node/bridgeconfig/bridgeflows.go index 862a88c369..0e46cf1472 100644 --- a/go-controller/pkg/node/bridgeconfig/bridgeflows.go +++ b/go-controller/pkg/node/bridgeconfig/bridgeflows.go @@ -737,70 +737,94 @@ func (b *BridgeConfiguration) commonFlows(hostSubnets []*net.IPNet) ([]string, e "actions=ct(zone=%d, nat, table=1)", nodetypes.DefaultOpenFlowCookie, ofPortPhys, config.Default.ConntrackZone)) } } - // Egress IP is often configured on a node different from the one hosting the affected pod. - // Due to the fact that ovn-controllers on different nodes apply the changes independently, - // there is a chance that the pod traffic will reach the egress node before it configures the SNAT flows. - // Drop pod traffic that is not SNATed, excluding local pods(required for ICNIv2) - defaultNetConfig := b.netConfig[types.DefaultNetworkName] - if config.OVNKubernetesFeature.EnableEgressIP { - for _, clusterEntry := range config.Default.ClusterSubnets { - cidr := clusterEntry.CIDR - ipv := getIPv(cidr) - // table 0, drop packets coming from pods headed externally that were not SNATed. - dftFlows = append(dftFlows, - fmt.Sprintf("cookie=%s, priority=104, in_port=%s, %s, %s_src=%s, actions=drop", - nodetypes.DefaultOpenFlowCookie, defaultNetConfig.OfPortPatch, ipv, ipv, cidr)) - } - for _, subnet := range defaultNetConfig.NodeSubnets { - ipv := getIPv(subnet) - if ofPortPhys != "" { - // table 0, commit connections from local pods. - // ICNIv2 requires that local pod traffic can leave the node without SNAT. - dftFlows = append(dftFlows, - fmt.Sprintf("cookie=%s, priority=109, in_port=%s, dl_src=%s, %s, %s_src=%s"+ - "actions=ct(commit, zone=%d, exec(set_field:%s->ct_mark)), output:%s", - nodetypes.DefaultOpenFlowCookie, defaultNetConfig.OfPortPatch, bridgeMacAddress, ipv, ipv, subnet, - config.Default.ConntrackZone, nodetypes.CtMarkOVN, ofPortPhys)) - } - } - } - if ofPortPhys != "" { + defaultNetConfig := b.netConfig[types.DefaultNetworkName] + // table 0, Ingress/Egress flows for MEG enabled pods and advertised UDNs + // priority 300: Ingress traffic to MEG pods and advertised UDNs + // priority 301: Ingress traffic to node management traffic + // priority 104: Egress traffic from advertised UDNs or MEG enabled pods + // priority 103: For egressIP, drop packets coming from pods from other nodes headed externally that were not SNATed. + // example flows in SGW mode EIP enabled: + // table=0, n_packets=0, n_bytes=0, priority=300,ip,in_port=eth0,nw_dst= actions=output:4 + // table=0, n_packets=0, n_bytes=0, priority=301,ip,in_port=eth0,nw_dst= actions=output:LOCAL + // table=0, n_packets=0, n_bytes=0, priority=104,ip,in_port=4,dl_src=02:42:ac:12:00:03,nw_src= actions=output:eth0 + // table=0, n_packets=0, n_bytes=0, priority=103,ip,in_port=4,nw_src= actions=drop + // example flows in LGW mode EIP enabled: + // table=0, n_packets=0, n_bytes=0, priority=300,ip,in_port=eth0,nw_dst= actions=output:LOCAL + // table=0, n_packets=0, n_bytes=0, priority=104,ip,in_port=LOCAL,dl_src=02:42:ac:12:00:03,nw_src= actions=output:eth0 + // table=0, n_packets=0, n_bytes=0, priority=103,ip,in_port=4,nw_src= actions=drop + // example flows in SGW mode EIP disabled: + // table=0, n_packets=0, n_bytes=0, priority=300,ip,in_port=eth0,nw_dst= actions=output:4 + // table=0, n_packets=0, n_bytes=0, priority=301,ip,in_port=eth0,nw_dst= actions=output:LOCAL + // table=0, n_packets=0, n_bytes=0, priority=104,ip,in_port=4,dl_src=02:42:ac:12:00:03,nw_src= actions=output:eth0 + // example flows in LGW mode EIP disabled: + // table=0, n_packets=0, n_bytes=0, priority=300,ip,in_port=eth0,nw_dst= actions=output:LOCAL + // table=0, n_packets=0, n_bytes=0, priority=104,ip,in_port=LOCAL,dl_src=02:42:ac:12:00:03,nw_src= actions=output:eth0 for _, netConfig := range b.patchedNetConfigs() { isNetworkAdvertised := netConfig.Advertised.Load() // disableSNATMultipleGWs only applies to default network disableSNATMultipleGWs := netConfig.IsDefaultNetwork() && config.Gateway.DisableSNATMultipleGWs + + if config.OVNKubernetesFeature.EnableEgressIP { + // Due to the fact that ovn-controllers on different nodes apply the changes independently, + // there is a chance that the pod traffic will reach the egress node before it configures the SNAT flows. + // Drop pod traffic that is not SNATed + for _, clusterEntry := range netConfig.Subnets { + cidr := clusterEntry.CIDR + ipv := getIPv(cidr) + // table 0, drop packets coming from pods headed externally that were not SNATed. + dftFlows = append(dftFlows, + fmt.Sprintf("cookie=%s, priority=103, in_port=%s, %s, %s_src=%s, actions=drop", + nodetypes.DefaultOpenFlowCookie, netConfig.OfPortPatch, ipv, ipv, cidr)) + } + } + // skip if MEG is disabled for the default network + // and the network (default or UDN) is not advertised if !disableSNATMultipleGWs && !isNetworkAdvertised { continue } output := netConfig.OfPortPatch - if isNetworkAdvertised && config.Gateway.Mode == config.GatewayModeLocal { + input := netConfig.OfPortPatch + isAdvertisedLGW := isNetworkAdvertised && config.Gateway.Mode == config.GatewayModeLocal + if isAdvertisedLGW { // except if advertised through BGP, go to kernel // TODO: MEG enabled pods should still go through the patch port // but holding this until // https://issues.redhat.com/browse/FDP-646 is fixed, for now we // are assuming MEG & BGP are not used together output = nodetypes.OvsLocalPort + input = nodetypes.OvsLocalPort } - for _, clusterEntry := range netConfig.Subnets { - cidr := clusterEntry.CIDR - ipv := getIPv(cidr) + for _, subnet := range netConfig.NodeSubnets { + ipv := getIPv(subnet) dftFlows = append(dftFlows, - fmt.Sprintf("cookie=%s, priority=15, table=1, %s, %s_dst=%s, "+ + fmt.Sprintf("cookie=%s, priority=300, table=0, in_port=%s, %s, %s_dst=%s, "+ "actions=output:%s", - nodetypes.DefaultOpenFlowCookie, ipv, ipv, cidr, output)) - } - if output == netConfig.OfPortPatch { + nodetypes.DefaultOpenFlowCookie, ofPortPhys, ipv, ipv, subnet, output)) // except node management traffic - for _, subnet := range netConfig.NodeSubnets { - mgmtIP := util.GetNodeManagementIfAddr(subnet) - ipv := getIPv(mgmtIP) + mgmtIP := util.GetNodeManagementIfAddr(subnet) + if mgmtIP == nil { + return nil, fmt.Errorf("unable to determine management IP for subnet %s", subnet.String()) + } + if config.Gateway.Mode != config.GatewayModeLocal { dftFlows = append(dftFlows, - fmt.Sprintf("cookie=%s, priority=16, table=1, %s, %s_dst=%s, "+ + fmt.Sprintf("cookie=%s, priority=301, table=0, in_port=%s, %s, %s_dst=%s, "+ "actions=output:%s", - nodetypes.DefaultOpenFlowCookie, ipv, ipv, mgmtIP.IP, nodetypes.OvsLocalPort), + nodetypes.DefaultOpenFlowCookie, ofPortPhys, ipv, ipv, mgmtIP.IP, nodetypes.OvsLocalPort), ) } + + if (disableSNATMultipleGWs && config.OVNKubernetesFeature.EnableEgressIP) || isNetworkAdvertised { + // MEG and advertised UDN networks requires that local pod traffic can leave the node without SNAT. + // We match on the pod subnets and forward the traffic to the physical interface. + // Select priority 104 for the scenario when both EgressIP and advertised UDN are active: + // 1. Override egressIP drop flows (priority 103) + // 2. Still allow egressIP flows at priority 105 + dftFlows = append(dftFlows, + fmt.Sprintf("cookie=%s, priority=104, in_port=%s, dl_src=%s, %s, %s_src=%s, "+ + "actions=output:%s", + nodetypes.DefaultOpenFlowCookie, input, bridgeMacAddress, ipv, ipv, subnet, ofPortPhys)) + } } } diff --git a/go-controller/pkg/node/default_node_network_controller.go b/go-controller/pkg/node/default_node_network_controller.go index 074006fe70..eb44f638d4 100644 --- a/go-controller/pkg/node/default_node_network_controller.go +++ b/go-controller/pkg/node/default_node_network_controller.go @@ -72,6 +72,9 @@ type BaseNodeNetworkController struct { // network information util.ReconcilableNetInfo + // networkManager used for getting network information + networkManager networkmanager.Interface + // podNADToDPUCDMap tracks the NAD/DPU_ConnectionDetails mapping for all NADs that each pod requests. // Key is pod.UUID; value is nadToDPUCDMap (of map[string]*util.DPUConnectionDetails). Key of nadToDPUCDMap // is nadName; value is DPU_ConnectionDetails when VF representor is successfully configured for that @@ -126,8 +129,6 @@ type DefaultNodeNetworkController struct { apbExternalRouteNodeController *apbroute.ExternalGatewayNodeController - networkManager networkmanager.Interface - cniServer *cni.Server udnHostIsolationManager *UDNHostIsolationManager @@ -139,12 +140,13 @@ type DefaultNodeNetworkController struct { } func newDefaultNodeNetworkController(cnnci *CommonNodeNetworkControllerInfo, stopChan chan struct{}, - wg *sync.WaitGroup, routeManager *routemanager.Controller, ovsClient client.Client) *DefaultNodeNetworkController { + wg *sync.WaitGroup, routeManager *routemanager.Controller, networkManager networkmanager.Interface, ovsClient client.Client) *DefaultNodeNetworkController { c := &DefaultNodeNetworkController{ BaseNodeNetworkController: BaseNodeNetworkController{ CommonNodeNetworkControllerInfo: *cnnci, ReconcilableNetInfo: &util.DefaultNetInfo{}, + networkManager: networkManager, stopChan: stopChan, wg: wg, }, @@ -164,7 +166,7 @@ func NewDefaultNodeNetworkController(cnnci *CommonNodeNetworkControllerInfo, net var err error stopChan := make(chan struct{}) wg := &sync.WaitGroup{} - nc := newDefaultNodeNetworkController(cnnci, stopChan, wg, cnnci.routeManager, ovsClient) + nc := newDefaultNodeNetworkController(cnnci, stopChan, wg, cnnci.routeManager, networkManager, ovsClient) if len(config.Kubernetes.HealthzBindAddress) != 0 { klog.Infof("Enable node proxy healthz server on %s", config.Kubernetes.HealthzBindAddress) @@ -184,8 +186,6 @@ func NewDefaultNodeNetworkController(cnnci *CommonNodeNetworkControllerInfo, net return nil, err } - nc.networkManager = networkManager - nc.initRetryFrameworkForNode() err = setupRemoteNodeNFTSets() diff --git a/go-controller/pkg/node/default_node_network_controller_test.go b/go-controller/pkg/node/default_node_network_controller_test.go index 366ee881d6..50c4ab2769 100644 --- a/go-controller/pkg/node/default_node_network_controller_test.go +++ b/go-controller/pkg/node/default_node_network_controller_test.go @@ -810,7 +810,7 @@ var _ = Describe("Node", func() { Expect(err).NotTo(HaveOccurred()) routeManager := routemanager.NewController() cnnci := NewCommonNodeNetworkControllerInfo(kubeFakeClient, fakeClient.AdminPolicyRouteClient, wf, nil, nodeName, routeManager) - nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil) + nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil, nil) nc.initRetryFrameworkForNode() err = setupRemoteNodeNFTSets() Expect(err).NotTo(HaveOccurred()) @@ -921,7 +921,7 @@ add element inet ovn-kubernetes remote-node-ips-v4 { 169.254.254.61 } Expect(err).NotTo(HaveOccurred()) routeManager := routemanager.NewController() cnnci := NewCommonNodeNetworkControllerInfo(kubeFakeClient, fakeClient.AdminPolicyRouteClient, wf, nil, nodeName, routeManager) - nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil) + nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil, nil) nc.initRetryFrameworkForNode() err = setupRemoteNodeNFTSets() Expect(err).NotTo(HaveOccurred()) @@ -1074,7 +1074,7 @@ add element inet ovn-kubernetes remote-node-ips-v4 { 169.254.253.61 } Expect(err).NotTo(HaveOccurred()) routeManager := routemanager.NewController() cnnci := NewCommonNodeNetworkControllerInfo(kubeFakeClient, fakeClient.AdminPolicyRouteClient, wf, nil, nodeName, routeManager) - nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil) + nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil, nil) nc.initRetryFrameworkForNode() err = setupRemoteNodeNFTSets() Expect(err).NotTo(HaveOccurred()) @@ -1184,7 +1184,7 @@ add element inet ovn-kubernetes remote-node-ips-v6 { 2001:db8:1::4 } Expect(err).NotTo(HaveOccurred()) routeManager := routemanager.NewController() cnnci := NewCommonNodeNetworkControllerInfo(kubeFakeClient, fakeClient.AdminPolicyRouteClient, wf, nil, nodeName, routeManager) - nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil) + nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil, nil) nc.initRetryFrameworkForNode() err = setupRemoteNodeNFTSets() Expect(err).NotTo(HaveOccurred()) @@ -1345,7 +1345,7 @@ add element inet ovn-kubernetes remote-node-ips-v6 { 2002:db8:1::4 } Expect(err).NotTo(HaveOccurred()) routeManager := routemanager.NewController() cnnci := NewCommonNodeNetworkControllerInfo(kubeFakeClient, fakeClient.AdminPolicyRouteClient, wf, nil, nodeName, routeManager) - nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil) + nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil, nil) nc.initRetryFrameworkForNode() err = setupRemoteNodeNFTSets() Expect(err).NotTo(HaveOccurred()) @@ -1466,7 +1466,7 @@ add element inet ovn-kubernetes remote-node-ips-v6 { 2002:db8:1::4 } Expect(err).NotTo(HaveOccurred()) routeManager := routemanager.NewController() cnnci := NewCommonNodeNetworkControllerInfo(kubeFakeClient, fakeClient.AdminPolicyRouteClient, wf, nil, nodeName, routeManager) - nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil) + nc = newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil, nil) nc.initRetryFrameworkForNode() err = setupRemoteNodeNFTSets() Expect(err).NotTo(HaveOccurred()) diff --git a/go-controller/pkg/node/gateway_init_linux_test.go b/go-controller/pkg/node/gateway_init_linux_test.go index 7bbfdbce98..15a48819e4 100644 --- a/go-controller/pkg/node/gateway_init_linux_test.go +++ b/go-controller/pkg/node/gateway_init_linux_test.go @@ -904,7 +904,7 @@ func shareGatewayInterfaceDPUHostTest(app *cli.App, testNS ns.NetNS, uplinkName, ipnet.IP = ip routeManager := routemanager.NewController() cnnci := NewCommonNodeNetworkControllerInfo(kubeFakeClient, fakeClient.AdminPolicyRouteClient, wf, nil, nodeName, routeManager) - nc := newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil) + nc := newDefaultNodeNetworkController(cnnci, stop, wg, routeManager, nil, nil) // must run route manager manually which is usually started with nc.Start() wg.Add(1) go func() { diff --git a/go-controller/pkg/node/gateway_udn_test.go b/go-controller/pkg/node/gateway_udn_test.go index aad3ba8f3c..4611ea97ed 100644 --- a/go-controller/pkg/node/gateway_udn_test.go +++ b/go-controller/pkg/node/gateway_udn_test.go @@ -1143,7 +1143,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() { Expect(udnGateway.AddNetwork()).To(Succeed()) flowMap = udnGateway.gateway.openflowManager.flowCache - Expect(flowMap["DEFAULT"]).To(HaveLen(71)) // 18 UDN Flows, 5 advertisedUDN flows, and 2 packet mark flows (IPv4+IPv6) are added by default + Expect(flowMap["DEFAULT"]).To(HaveLen(73)) // 18 UDN Flows, 5 advertisedUDN flows, and 2 packet mark flows (IPv4+IPv6) are added by default Expect(udnGateway.openflowManager.defaultBridge.GetNetConfigLen()).To(Equal(2)) // default network + UDN network defaultUdnConfig := udnGateway.openflowManager.defaultBridge.GetNetworkConfig("default") bridgeUdnConfig := udnGateway.openflowManager.defaultBridge.GetNetworkConfig("bluenet") @@ -1159,7 +1159,7 @@ var _ = Describe("UserDefinedNetworkGateway", func() { } } } - Expect(udnFlows).To(Equal(14)) + Expect(udnFlows).To(Equal(16)) openflowManagerCheckPorts(udnGateway.openflowManager) for _, svcCIDR := range config.Kubernetes.ServiceCIDRs { diff --git a/go-controller/pkg/node/ovn_test.go b/go-controller/pkg/node/ovn_test.go index 428ffe1770..ed1fe1d181 100644 --- a/go-controller/pkg/node/ovn_test.go +++ b/go-controller/pkg/node/ovn_test.go @@ -88,7 +88,7 @@ func (o *FakeOVNNode) init(ctx context.Context) { Expect(err).NotTo(HaveOccurred()) cnnci := NewCommonNodeNetworkControllerInfo(o.fakeClient.KubeClient, o.fakeClient.AdminPolicyRouteClient, o.watcher, o.recorder, fakeNodeName, routemanager.NewController()) - o.nc = newDefaultNodeNetworkController(cnnci, o.stopChan, o.wg, routemanager.NewController(), nil) + o.nc = newDefaultNodeNetworkController(cnnci, o.stopChan, o.wg, routemanager.NewController(), nil, nil) // watcher is started by nodeControllerManager, not by nodeNetworkController, so start it here. Expect(o.watcher.Start()).To(Succeed()) Expect(o.nc.Init(ctx)).To(Succeed()) diff --git a/go-controller/pkg/node/secondary_node_network_controller.go b/go-controller/pkg/node/secondary_node_network_controller.go index 07e78c8690..e5c4eba83f 100644 --- a/go-controller/pkg/node/secondary_node_network_controller.go +++ b/go-controller/pkg/node/secondary_node_network_controller.go @@ -9,6 +9,7 @@ import ( "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/factory" + "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/networkmanager" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/iprulemanager" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/node/vrfmanager" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types" @@ -31,6 +32,7 @@ type SecondaryNodeNetworkController struct { func NewSecondaryNodeNetworkController( cnnci *CommonNodeNetworkControllerInfo, netInfo util.NetInfo, + networkManager networkmanager.Interface, vrfManager *vrfmanager.Controller, ruleManager *iprulemanager.Controller, defaultNetworkGateway Gateway, @@ -42,6 +44,7 @@ func NewSecondaryNodeNetworkController( ReconcilableNetInfo: util.NewReconcilableNetInfo(netInfo), stopChan: make(chan struct{}), wg: &sync.WaitGroup{}, + networkManager: networkManager, }, } if util.IsNetworkSegmentationSupportEnabled() && snnc.IsPrimaryNetwork() { diff --git a/go-controller/pkg/node/secondary_node_network_controller_test.go b/go-controller/pkg/node/secondary_node_network_controller_test.go index a9d76c280d..bd0fbaab09 100644 --- a/go-controller/pkg/node/secondary_node_network_controller_test.go +++ b/go-controller/pkg/node/secondary_node_network_controller_test.go @@ -85,7 +85,7 @@ var _ = Describe("SecondaryNodeNetworkController", func() { factoryMock.On("GetNodes").Return(nodeList, nil) NetInfo, err := util.ParseNADInfo(nad) Expect(err).NotTo(HaveOccurred()) - controller, err := NewSecondaryNodeNetworkController(&cnnci, NetInfo, nil, nil, &gateway{}) + controller, err := NewSecondaryNodeNetworkController(&cnnci, NetInfo, nil, nil, nil, &gateway{}) Expect(err).NotTo(HaveOccurred()) err = controller.Start(context.Background()) Expect(err).NotTo(HaveOccurred()) @@ -116,7 +116,7 @@ var _ = Describe("SecondaryNodeNetworkController", func() { Expect(err).NotTo(HaveOccurred()) getCreationFakeCommands(fexec, "ovn-k8s-mp3", mgtPortMAC, NetInfo.GetNetworkName(), "worker1", NetInfo.MTU()) ofm := getDummyOpenflowManager() - controller, err := NewSecondaryNodeNetworkController(&cnnci, NetInfo, nil, nil, &gateway{openflowManager: ofm}) + controller, err := NewSecondaryNodeNetworkController(&cnnci, NetInfo, nil, nil, nil, &gateway{openflowManager: ofm}) Expect(err).NotTo(HaveOccurred()) err = controller.Start(context.Background()) Expect(err).To(HaveOccurred()) // we don't have the gateway pieces setup so its expected to fail here @@ -144,7 +144,7 @@ var _ = Describe("SecondaryNodeNetworkController", func() { types.Layer3Topology, "100.128.0.0/16", types.NetworkRoleSecondary) NetInfo, err := util.ParseNADInfo(nad) Expect(err).NotTo(HaveOccurred()) - controller, err := NewSecondaryNodeNetworkController(&cnnci, NetInfo, nil, nil, &gateway{}) + controller, err := NewSecondaryNodeNetworkController(&cnnci, NetInfo, nil, nil, nil, &gateway{}) Expect(err).NotTo(HaveOccurred()) err = controller.Start(context.Background()) Expect(err).NotTo(HaveOccurred()) @@ -420,7 +420,7 @@ var _ = Describe("SecondaryNodeNetworkController: UserDefinedPrimaryNetwork Gate By("creating secondary network controller for user defined primary network") cnnci := CommonNodeNetworkControllerInfo{name: nodeName, watchFactory: &factoryMock} - controller, err := NewSecondaryNodeNetworkController(&cnnci, NetInfo, vrf, ipRulesManager, localGw) + controller, err := NewSecondaryNodeNetworkController(&cnnci, NetInfo, nil, vrf, ipRulesManager, localGw) Expect(err).NotTo(HaveOccurred()) Expect(controller.gateway).To(Not(BeNil())) Expect(controller.gateway.ruleManager).To(Not(BeNil())) diff --git a/go-controller/pkg/ovn/address_set/fake_address_set.go b/go-controller/pkg/ovn/address_set/fake_address_set.go index 2f783b3486..22db3c4a0f 100644 --- a/go-controller/pkg/ovn/address_set/fake_address_set.go +++ b/go-controller/pkg/ovn/address_set/fake_address_set.go @@ -11,6 +11,7 @@ import ( "k8s.io/klog/v2" utilnet "k8s.io/utils/net" + "github.com/ovn-kubernetes/libovsdb/client" "github.com/ovn-kubernetes/libovsdb/ovsdb" "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/config" @@ -124,7 +125,7 @@ func (f *FakeAddressSetFactory) GetAddressSet(dbIDs *libovsdbops.DbObjectIDs) (A if ok { return set, nil } - return nil, fmt.Errorf("error fetching address set") + return nil, fmt.Errorf("error fetching address set: %w", client.ErrNotFound) } func (f *FakeAddressSetFactory) ProcessEachAddressSet(ownerController string, indexT *libovsdbops.ObjectIDsType, iteratorFn AddressSetIterFunc) error { diff --git a/go-controller/pkg/ovn/master_test.go b/go-controller/pkg/ovn/master_test.go index fd9be9c9f1..6d79264a32 100644 --- a/go-controller/pkg/ovn/master_test.go +++ b/go-controller/pkg/ovn/master_test.go @@ -1267,6 +1267,7 @@ var _ = ginkgo.Describe("Default network controller operations", func() { ginkgo.DescribeTable( "reconciles pod network SNATs from syncGateway", func(condition func(*DefaultNetworkController) error, expectedExtraNATs ...*nbdb.NAT) { + config.OVNKubernetesFeature.AdvertisedUDNIsolationMode = config.AdvertisedUDNIsolationModeStrict app.Action = func(ctx *cli.Context) error { // Initialize config from CLI flags (including --init-gateways) _, err := config.InitConfig(ctx, nil, nil) diff --git a/go-controller/pkg/ovn/ovn.go b/go-controller/pkg/ovn/ovn.go index 593c50cbfb..d935bca85f 100644 --- a/go-controller/pkg/ovn/ovn.go +++ b/go-controller/pkg/ovn/ovn.go @@ -397,7 +397,8 @@ func (oc *DefaultNetworkController) syncNodeGateway(node *corev1.Node) error { return fmt.Errorf("error creating gateway for node %s: %v", node.Name, err) } - if util.IsPodNetworkAdvertisedAtNode(oc, node.Name) { + if util.IsPodNetworkAdvertisedAtNode(oc, node.Name) && + config.OVNKubernetesFeature.AdvertisedUDNIsolationMode == config.AdvertisedUDNIsolationModeStrict { return oc.addAdvertisedNetworkIsolation(node.Name) } return oc.deleteAdvertisedNetworkIsolation(node.Name) diff --git a/go-controller/pkg/ovn/secondary_layer2_network_controller.go b/go-controller/pkg/ovn/secondary_layer2_network_controller.go index 0470a2a251..1b7bbf9748 100644 --- a/go-controller/pkg/ovn/secondary_layer2_network_controller.go +++ b/go-controller/pkg/ovn/secondary_layer2_network_controller.go @@ -590,14 +590,13 @@ func (oc *SecondaryLayer2NetworkController) addUpdateLocalNodeEvent(node *corev1 if err != nil { return err } - if !isUDNAdvertised { - if util.IsRouteAdvertisementsEnabled() { - if err = oc.deleteAdvertisedNetworkIsolation(node.Name); err != nil { - return err - } + shouldIsolate := isUDNAdvertised && config.OVNKubernetesFeature.AdvertisedUDNIsolationMode == config.AdvertisedUDNIsolationModeStrict + if shouldIsolate { + if err = oc.addAdvertisedNetworkIsolation(node.Name); err != nil { + return err } } else { - if err = oc.addAdvertisedNetworkIsolation(node.Name); err != nil { + if err = oc.deleteAdvertisedNetworkIsolation(node.Name); err != nil { return err } } diff --git a/go-controller/pkg/ovn/secondary_layer3_network_controller.go b/go-controller/pkg/ovn/secondary_layer3_network_controller.go index e8efc9bcdf..da57187694 100644 --- a/go-controller/pkg/ovn/secondary_layer3_network_controller.go +++ b/go-controller/pkg/ovn/secondary_layer3_network_controller.go @@ -908,14 +908,13 @@ func (oc *SecondaryLayer3NetworkController) addNode(node *corev1.Node) ([]*net.I if err := oc.addOrUpdateUDNNodeSubnetEgressSNAT(hostSubnets, node, isUDNAdvertised); err != nil { return nil, err } - if !isUDNAdvertised { - if util.IsRouteAdvertisementsEnabled() { - if err := oc.deleteAdvertisedNetworkIsolation(node.Name); err != nil { - return nil, err - } + shouldIsolate := isUDNAdvertised && config.OVNKubernetesFeature.AdvertisedUDNIsolationMode == config.AdvertisedUDNIsolationModeStrict + if shouldIsolate { + if err = oc.addAdvertisedNetworkIsolation(node.Name); err != nil { + return nil, err } } else { - if err := oc.addAdvertisedNetworkIsolation(node.Name); err != nil { + if err = oc.deleteAdvertisedNetworkIsolation(node.Name); err != nil { return nil, err } } diff --git a/go-controller/pkg/ovn/udn_isolation.go b/go-controller/pkg/ovn/udn_isolation.go index 0230f665b6..3403b0a9a7 100644 --- a/go-controller/pkg/ovn/udn_isolation.go +++ b/go-controller/pkg/ovn/udn_isolation.go @@ -371,38 +371,45 @@ func (bnc *BaseNetworkController) addAdvertisedNetworkIsolation(nodeName string) // It removes the network CIDRs from the global advertised networks addresset together with the ACLs on the node switch. func (bnc *BaseNetworkController) deleteAdvertisedNetworkIsolation(nodeName string) error { addrSet, err := bnc.addressSetFactory.GetAddressSet(GetAdvertisedNetworkSubnetsAddressSetDBIDs()) - if err != nil { + if err != nil && !errors.Is(err, libovsdbclient.ErrNotFound) { return fmt.Errorf("failed to get advertised subnets addresset %s for network %s: %w", GetAdvertisedNetworkSubnetsAddressSetDBIDs(), bnc.GetNetworkName(), err) } - var cidrs []string - for _, subnet := range bnc.Subnets() { - cidrs = append(cidrs, subnet.CIDR.String()) - } - ops, err := addrSet.DeleteAddressesReturnOps(cidrs) - if err != nil { - return fmt.Errorf("failed to create ovsdb ops for deleting the addresses from %s addresset for network %s: %w", GetAdvertisedNetworkSubnetsAddressSetDBIDs(), bnc.GetNetworkName(), err) + var ops []ovsdb.Operation + if addrSet != nil { + var cidrs []string + for _, subnet := range bnc.Subnets() { + cidrs = append(cidrs, subnet.CIDR.String()) + } + ops, err = addrSet.DeleteAddressesReturnOps(cidrs) + if err != nil { + return fmt.Errorf("failed to create ovsdb ops for deleting the addresses from %s addresset for network %s: %w", GetAdvertisedNetworkSubnetsAddressSetDBIDs(), bnc.GetNetworkName(), err) + } } passACLIDs := GetAdvertisedNetworkSubnetsPassACLdbIDs(bnc.controllerName, bnc.GetNetworkName(), bnc.GetNetworkID()) + dropACLIDs := GetAdvertisedNetworkSubnetsDropACLdbIDs() passACLPredicate := libovsdbops.GetPredicate[*nbdb.ACL](passACLIDs, nil) - passACLs, err := libovsdbops.FindACLsWithPredicate(bnc.nbClient, passACLPredicate) - if err != nil { - return fmt.Errorf("unable to find the pass ACL for advertised network %s: %w", bnc.GetNetworkName(), err) + dropACLPredicate := libovsdbops.GetPredicate[*nbdb.ACL](dropACLIDs, nil) + // Create a combined predicate to find both ACLs in a single lookup + combinedACLPredicate := func(acl *nbdb.ACL) bool { + // Check if ACL matches either pass or drop ACL IDs + return passACLPredicate(acl) || dropACLPredicate(acl) } - dropACLIDs := GetAdvertisedNetworkSubnetsDropACLdbIDs() - dropACLPredicate := libovsdbops.GetPredicate[*nbdb.ACL](dropACLIDs, nil) - dropACLs, err := libovsdbops.FindACLsWithPredicate(bnc.nbClient, dropACLPredicate) + // Find both ACLs in a single lookup + allACLsToRemove, err := libovsdbops.FindACLsWithPredicate(bnc.nbClient, combinedACLPredicate) if err != nil { - return fmt.Errorf("unable to find the drop ACL for advertised network %s: %w", bnc.GetNetworkName(), err) + return fmt.Errorf("unable to find pass and/or drop ACLs for advertised network %s: %w", bnc.GetNetworkName(), err) } // ACLs referenced by the switch will be deleted by db if there are no other references p := func(sw *nbdb.LogicalSwitch) bool { return sw.Name == bnc.GetNetworkScopedSwitchName(nodeName) } - ops, err = libovsdbops.RemoveACLsFromLogicalSwitchesWithPredicateOps(bnc.nbClient, ops, p, append(passACLs, dropACLs...)...) - if err != nil { - return fmt.Errorf("failed to create ovsdb ops for removing network isolation ACLs from the %s switch for network %s: %w", bnc.GetNetworkScopedSwitchName(nodeName), bnc.GetNetworkName(), err) + if len(allACLsToRemove) > 0 { + ops, err = libovsdbops.RemoveACLsFromLogicalSwitchesWithPredicateOps(bnc.nbClient, ops, p, allACLsToRemove...) + if err != nil { + return fmt.Errorf("failed to create ovsdb ops for removing network isolation ACLs from the %s switch for network %s: %w", bnc.GetNetworkScopedSwitchName(nodeName), bnc.GetNetworkName(), err) + } } _, err = libovsdbops.TransactAndCheck(bnc.nbClient, ops) diff --git a/go-controller/pkg/util/pod_annotation.go b/go-controller/pkg/util/pod_annotation.go index 371eec5a62..ba565571bc 100644 --- a/go-controller/pkg/util/pod_annotation.go +++ b/go-controller/pkg/util/pod_annotation.go @@ -227,7 +227,7 @@ func UnmarshalPodAnnotation(annotations map[string]string, nadName string) (*Pod tempA, ok := podNetworks[nadName] if !ok { - return nil, fmt.Errorf("no ovn pod annotation for network %s: %q", + return nil, newAnnotationNotSetError("no ovn pod annotation for NAD %s: %q", nadName, ovnAnnotation) } diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/LICENSE b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/LICENSE new file mode 100644 index 0000000000..8f71f43fee --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/LICENSE @@ -0,0 +1,202 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/NOTICE b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/NOTICE new file mode 100644 index 0000000000..251a004bcb --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/NOTICE @@ -0,0 +1,2 @@ +Copyright 2016 Intel Corporation +Copyright 2021 Multus Authors diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/checkpoint/checkpoint.go b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/checkpoint/checkpoint.go new file mode 100644 index 0000000000..2fc0de5a3d --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/checkpoint/checkpoint.go @@ -0,0 +1,112 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package checkpoint + +import ( + "encoding/json" + "os" + + "gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging" + "gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types" + v1 "k8s.io/api/core/v1" +) + +const ( + checkPointfile = "/var/lib/kubelet/device-plugins/kubelet_internal_checkpoint" +) + +// PodDevicesEntry maps PodUID, resource name and allocated device id +type PodDevicesEntry struct { + PodUID string + ContainerName string + ResourceName string + DeviceIDs map[int64][]string + AllocResp []byte +} + +type checkpointData struct { + PodDeviceEntries []PodDevicesEntry + RegisteredDevices map[string][]string +} + +type checkpointFileData struct { + Data checkpointData + Checksum uint64 +} + +type checkpoint struct { + fileName string + podEntires []PodDevicesEntry +} + +// GetCheckpoint returns an instance of Checkpoint +func GetCheckpoint() (types.ResourceClient, error) { + logging.Debugf("GetCheckpoint(): invoked") + return getCheckpoint(checkPointfile) +} + +func getCheckpoint(filePath string) (types.ResourceClient, error) { + cp := &checkpoint{fileName: filePath} + err := cp.getPodEntries() + if err != nil { + return nil, err + } + logging.Debugf("getCheckpoint: created checkpoint instance with file: %s", filePath) + return cp, nil +} + +// getPodEntries gets all Pod device allocation entries from checkpoint file +func (cp *checkpoint) getPodEntries() error { + + cpd := &checkpointFileData{} + rawBytes, err := os.ReadFile(cp.fileName) + if err != nil { + return logging.Errorf("getPodEntries: error reading file %s\n%v\n", checkPointfile, err) + } + + if err = json.Unmarshal(rawBytes, cpd); err != nil { + return logging.Errorf("getPodEntries: error unmarshalling raw bytes %v", err) + } + + cp.podEntires = cpd.Data.PodDeviceEntries + logging.Debugf("getPodEntries: podEntires %+v", cp.podEntires) + return nil +} + +// GetPodResourceMap returns an instance of a map of ResourceInfo +func (cp *checkpoint) GetPodResourceMap(pod *v1.Pod) (map[string]*types.ResourceInfo, error) { + podID := string(pod.UID) + resourceMap := make(map[string]*types.ResourceInfo) + + if podID == "" { + return nil, logging.Errorf("GetPodResourceMap: invalid Pod cannot be empty") + } + for _, pod := range cp.podEntires { + if pod.PodUID == podID { + entry, ok := resourceMap[pod.ResourceName] + if !ok { + // new entry + entry = &types.ResourceInfo{} + resourceMap[pod.ResourceName] = entry + } + for _, v := range pod.DeviceIDs { + // already exists; append to it + entry.DeviceIDs = append(entry.DeviceIDs, v...) + } + } + } + return resourceMap, nil +} diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/checkpoint/doc.go b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/checkpoint/doc.go new file mode 100644 index 0000000000..879978fa94 --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/checkpoint/doc.go @@ -0,0 +1,17 @@ +// Copyright (c) 2022 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package checkpoint is the package that contains the libraries that manipulates kubelet's +// checkpoint API +package checkpoint diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/kubeletclient/doc.go b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/kubeletclient/doc.go new file mode 100644 index 0000000000..b2098a8529 --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/kubeletclient/doc.go @@ -0,0 +1,17 @@ +// Copyright (c) 2022 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package kubeletclient is the package that contains the kubelet's libraries that +// controls podresource API in kubelet +package kubeletclient diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/kubeletclient/kubeletclient.go b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/kubeletclient/kubeletclient.go new file mode 100644 index 0000000000..d0438f012f --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/kubeletclient/kubeletclient.go @@ -0,0 +1,160 @@ +// Copyright (c) 2019 Intel Corporation +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package kubeletclient + +import ( + "fmt" + "net" + "net/url" + "os" + "path/filepath" + "time" + + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + "gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/checkpoint" + "gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging" + "gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types" + v1 "k8s.io/api/core/v1" + podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1" +) + +const ( + defaultKubeletSocket = "kubelet" // which is defined in k8s.io/kubernetes/pkg/kubelet/apis/podresources + kubeletConnectionTimeout = 10 * time.Second + defaultPodResourcesMaxSize = 1024 * 1024 * 16 // 16 Mb + defaultPodResourcesPath = "/var/lib/kubelet/pod-resources" + unixProtocol = "unix" +) + +// LocalEndpoint returns the full path to a unix socket at the given endpoint +// which is in k8s.io/kubernetes/pkg/kubelet/util +func localEndpoint(path string) *url.URL { + return &url.URL{ + Scheme: unixProtocol, + Path: path + ".sock", + } +} + +// GetResourceClient returns an instance of ResourceClient interface initialized with Pod resource information +func GetResourceClient(kubeletSocket string) (types.ResourceClient, error) { + kubeletSocketURL := localEndpoint(filepath.Join(defaultPodResourcesPath, defaultKubeletSocket)) + + if kubeletSocket != "" { + kubeletSocketURL = &url.URL{ + Scheme: unixProtocol, + Path: kubeletSocket, + } + } + // If Kubelet resource API endpoint exist use that by default + // Or else fallback with checkpoint file + if hasKubeletAPIEndpoint(kubeletSocketURL) { + logging.Debugf("GetResourceClient: using Kubelet resource API endpoint") + return getKubeletClient(kubeletSocketURL) + } + + logging.Debugf("GetResourceClient: using Kubelet device plugin checkpoint") + return checkpoint.GetCheckpoint() +} + +func dial(ctx context.Context, addr string) (net.Conn, error) { + return (&net.Dialer{}).DialContext(ctx, unixProtocol, addr) +} + +func getKubeletResourceClient(kubeletSocketURL *url.URL, timeout time.Duration) (podresourcesapi.PodResourcesListerClient, *grpc.ClientConn, error) { + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + + conn, err := grpc.DialContext(ctx, kubeletSocketURL.Path, grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithContextDialer(dial), + grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaultPodResourcesMaxSize))) + if err != nil { + return nil, nil, fmt.Errorf("error dialing socket %s: %v", kubeletSocketURL.Path, err) + } + return podresourcesapi.NewPodResourcesListerClient(conn), conn, nil +} + +func getKubeletClient(kubeletSocketURL *url.URL) (types.ResourceClient, error) { + newClient := &kubeletClient{} + + client, conn, err := getKubeletResourceClient(kubeletSocketURL, 10*time.Second) + if err != nil { + return nil, logging.Errorf("getKubeletClient: error getting grpc client: %v\n", err) + } + defer conn.Close() + + if err := newClient.getPodResources(client); err != nil { + return nil, logging.Errorf("getKubeletClient: error getting pod resources from client: %v\n", err) + } + + return newClient, nil +} + +type kubeletClient struct { + resources []*podresourcesapi.PodResources +} + +func (rc *kubeletClient) getPodResources(client podresourcesapi.PodResourcesListerClient) error { + + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + resp, err := client.List(ctx, &podresourcesapi.ListPodResourcesRequest{}) + if err != nil { + return logging.Errorf("getPodResources: failed to list pod resources, %v.Get(_) = _, %v", client, err) + } + + rc.resources = resp.PodResources + return nil +} + +// GetPodResourceMap returns an instance of a map of Pod ResourceInfo given a (Pod name, namespace) tuple +func (rc *kubeletClient) GetPodResourceMap(pod *v1.Pod) (map[string]*types.ResourceInfo, error) { + resourceMap := make(map[string]*types.ResourceInfo) + + name := pod.Name + ns := pod.Namespace + + if name == "" || ns == "" { + return nil, logging.Errorf("GetPodResourceMap: Pod name or namespace cannot be empty") + } + + for _, pr := range rc.resources { + if pr.Name == name && pr.Namespace == ns { + for _, cnt := range pr.Containers { + for _, dev := range cnt.Devices { + if rInfo, ok := resourceMap[dev.ResourceName]; ok { + rInfo.DeviceIDs = append(rInfo.DeviceIDs, dev.DeviceIds...) + } else { + resourceMap[dev.ResourceName] = &types.ResourceInfo{DeviceIDs: dev.DeviceIds} + } + } + } + } + } + return resourceMap, nil +} + +func hasKubeletAPIEndpoint(url *url.URL) bool { + // Check for kubelet resource API socket file + if _, err := os.Stat(url.Path); err != nil { + logging.Debugf("hasKubeletAPIEndpoint: error looking up kubelet resource api socket file: %q", err) + return false + } + return true +} diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging/doc.go b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging/doc.go new file mode 100644 index 0000000000..977b06a41d --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging/doc.go @@ -0,0 +1,16 @@ +// Copyright (c) 2022 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package logging is the package that contains logging library. +package logging diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging/logging.go b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging/logging.go new file mode 100644 index 0000000000..4cf001e8dd --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging/logging.go @@ -0,0 +1,188 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package logging + +import ( + "errors" + "fmt" + "io" + "os" + "strings" + "time" + + lumberjack "gopkg.in/natefinch/lumberjack.v2" +) + +// Level type +type Level uint32 + +// PanicLevel...MaxLevel indicates the logging level +const ( + PanicLevel Level = iota + ErrorLevel + VerboseLevel + DebugLevel + MaxLevel + UnknownLevel +) + +var loggingStderr bool +var loggingW io.Writer +var loggingLevel Level +var logger *lumberjack.Logger + +const defaultTimestampFormat = time.RFC3339 + +// LogOptions specifies the configuration of the log +type LogOptions struct { + MaxAge *int `json:"maxAge,omitempty"` + MaxSize *int `json:"maxSize,omitempty"` + MaxBackups *int `json:"maxBackups,omitempty"` + Compress *bool `json:"compress,omitempty"` +} + +// SetLogOptions set the LoggingOptions of NetConf +func SetLogOptions(options *LogOptions) { + // give some default value + logger.MaxSize = 100 + logger.MaxAge = 5 + logger.MaxBackups = 5 + logger.Compress = true + if options != nil { + if options.MaxAge != nil { + logger.MaxAge = *options.MaxAge + } + if options.MaxSize != nil { + logger.MaxSize = *options.MaxSize + } + if options.MaxBackups != nil { + logger.MaxBackups = *options.MaxBackups + } + if options.Compress != nil { + logger.Compress = *options.Compress + } + } + loggingW = logger +} + +func (l Level) String() string { + switch l { + case PanicLevel: + return "panic" + case VerboseLevel: + return "verbose" + case ErrorLevel: + return "error" + case DebugLevel: + return "debug" + } + return "unknown" +} + +func printf(level Level, format string, a ...interface{}) { + header := "%s [%s] " + t := time.Now() + if level > loggingLevel { + return + } + + if loggingStderr { + fmt.Fprintf(os.Stderr, header, t.Format(defaultTimestampFormat), level) + fmt.Fprintf(os.Stderr, format, a...) + fmt.Fprintf(os.Stderr, "\n") + } + + if loggingW != nil { + fmt.Fprintf(loggingW, header, t.Format(defaultTimestampFormat), level) + fmt.Fprintf(loggingW, format, a...) + fmt.Fprintf(loggingW, "\n") + } +} + +// Debugf prints logging if logging level >= debug +func Debugf(format string, a ...interface{}) { + printf(DebugLevel, format, a...) +} + +// Verbosef prints logging if logging level >= verbose +func Verbosef(format string, a ...interface{}) { + printf(VerboseLevel, format, a...) +} + +// Errorf prints logging if logging level >= error +func Errorf(format string, a ...interface{}) error { + printf(ErrorLevel, format, a...) + return fmt.Errorf(format, a...) +} + +// Panicf prints logging plus stack trace. This should be used only for unrecoverable error +func Panicf(format string, a ...interface{}) { + printf(PanicLevel, format, a...) + printf(PanicLevel, "========= Stack trace output ========") + printf(PanicLevel, "%+v", errors.New("Multus Panic")) + printf(PanicLevel, "========= Stack trace output end ========") +} + +// GetLoggingLevel gets current logging level +func GetLoggingLevel() Level { + return loggingLevel +} + +func getLoggingLevel(levelStr string) Level { + switch strings.ToLower(levelStr) { + case "debug": + return DebugLevel + case "verbose": + return VerboseLevel + case "error": + return ErrorLevel + case "panic": + return PanicLevel + } + fmt.Fprintf(os.Stderr, "multus logging: cannot set logging level to %s\n", levelStr) + return UnknownLevel +} + +// SetLogLevel sets logging level +func SetLogLevel(levelStr string) { + level := getLoggingLevel(levelStr) + if level < MaxLevel { + loggingLevel = level + } +} + +// SetLogStderr sets flag for logging stderr output +func SetLogStderr(enable bool) { + loggingStderr = enable +} + +// SetLogFile sets logging file +func SetLogFile(filename string) { + if filename == "" { + return + } + + logger.Filename = filename + loggingW = logger + +} + +func init() { + loggingStderr = true + loggingW = nil + loggingLevel = PanicLevel + logger = &lumberjack.Logger{} +} diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types/conf.go b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types/conf.go new file mode 100644 index 0000000000..36d8d6f9fe --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types/conf.go @@ -0,0 +1,611 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "encoding/json" + "fmt" + "net" + "os" + "strings" + "sync" + + "github.com/containernetworking/cni/libcni" + "github.com/containernetworking/cni/pkg/skel" + cni100 "github.com/containernetworking/cni/pkg/types/100" + "github.com/containernetworking/cni/pkg/version" + nadutils "github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/utils" + "gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging" +) + +const ( + defaultCNIDir = "/var/lib/cni/multus" + defaultConfDir = "/etc/cni/multus/net.d" + defaultBinDir = "/opt/cni/bin" + defaultReadinessIndicatorFile = "" + defaultMultusNamespace = "kube-system" + defaultNonIsolatedNamespace = "default" +) + +// ChrootMutex provides lock to access host filesystem +var ChrootMutex *sync.Mutex + +// LoadDelegateNetConfList reads DelegateNetConf from bytes +func LoadDelegateNetConfList(bytes []byte, delegateConf *DelegateNetConf) error { + logging.Debugf("LoadDelegateNetConfList: %s, %v", string(bytes), delegateConf) + + if err := json.Unmarshal(bytes, &delegateConf.ConfList); err != nil { + return logging.Errorf("LoadDelegateNetConfList: error unmarshalling delegate conflist: %v", err) + } + + if delegateConf.ConfList.Plugins == nil { + return logging.Errorf("LoadDelegateNetConfList: delegate must have the 'type' or 'plugin' field") + } + + if delegateConf.ConfList.Plugins[0].Type == "" { + return logging.Errorf("LoadDelegateNetConfList: a plugin delegate must have the 'type' field") + } + delegateConf.ConfListPlugin = true + delegateConf.Name = delegateConf.ConfList.Name + return nil +} + +// LoadDelegateNetConf converts raw CNI JSON into a DelegateNetConf structure +func LoadDelegateNetConf(bytes []byte, netElement *NetworkSelectionElement, deviceID string, resourceName string) (*DelegateNetConf, error) { + var err error + logging.Debugf("LoadDelegateNetConf: %s, %v, %s", string(bytes), netElement, deviceID) + + delegateConf := &DelegateNetConf{} + if err := json.Unmarshal(bytes, &delegateConf.Conf); err != nil { + return nil, logging.Errorf("LoadDelegateNetConf: error unmarshalling delegate config: %v", err) + } + delegateConf.Name = delegateConf.Conf.Name + + // Do some minimal validation + if delegateConf.Conf.Type == "" { + if err := LoadDelegateNetConfList(bytes, delegateConf); err != nil { + return nil, logging.Errorf("LoadDelegateNetConf: failed with: %v", err) + } + if deviceID != "" { + bytes, err = addDeviceIDInConfList(bytes, deviceID) + if err != nil { + return nil, logging.Errorf("LoadDelegateNetConf: failed to add deviceID in NetConfList bytes: %v", err) + } + delegateConf.ResourceName = resourceName + delegateConf.DeviceID = deviceID + } + if netElement != nil && netElement.CNIArgs != nil { + bytes, err = addCNIArgsInConfList(bytes, netElement.CNIArgs) + if err != nil { + return nil, logging.Errorf("LoadDelegateNetConf(): failed to add cni-args in NetConfList bytes: %v", err) + } + } + } else { + if deviceID != "" { + bytes, err = delegateAddDeviceID(bytes, deviceID) + if err != nil { + return nil, logging.Errorf("LoadDelegateNetConf: failed to add deviceID in NetConf bytes: %v", err) + } + // Save them for housekeeping + delegateConf.ResourceName = resourceName + delegateConf.DeviceID = deviceID + } + if netElement != nil && netElement.CNIArgs != nil { + bytes, err = addCNIArgsInConfig(bytes, netElement.CNIArgs) + if err != nil { + return nil, logging.Errorf("LoadDelegateNetConf(): failed to add cni-args in NetConfList bytes: %v", err) + } + } + } + + if netElement != nil { + if netElement.Name != "" { + // Overwrite CNI config name with net-attach-def name + delegateConf.Name = fmt.Sprintf("%s/%s", netElement.Namespace, netElement.Name) + } + if netElement.InterfaceRequest != "" { + delegateConf.IfnameRequest = netElement.InterfaceRequest + } + if netElement.MacRequest != "" { + delegateConf.MacRequest = netElement.MacRequest + } + if netElement.IPRequest != nil { + delegateConf.IPRequest = netElement.IPRequest + } + if netElement.BandwidthRequest != nil { + delegateConf.BandwidthRequest = netElement.BandwidthRequest + } + if netElement.PortMappingsRequest != nil { + delegateConf.PortMappingsRequest = netElement.PortMappingsRequest + } + if netElement.GatewayRequest != nil { + var list []net.IP + if delegateConf.GatewayRequest != nil { + list = append(*delegateConf.GatewayRequest, *netElement.GatewayRequest...) + } else { + list = *netElement.GatewayRequest + } + delegateConf.GatewayRequest = &list + } + if netElement.InfinibandGUIDRequest != "" { + delegateConf.InfinibandGUIDRequest = netElement.InfinibandGUIDRequest + } + if netElement.DeviceID != "" { + if deviceID != "" { + logging.Debugf("Warning: Both RuntimeConfig and ResourceMap provide deviceID. Ignoring RuntimeConfig") + } else { + delegateConf.DeviceID = netElement.DeviceID + } + } + } + + delegateConf.Bytes = bytes + + return delegateConf, nil +} + +// mergeCNIRuntimeConfig creates CNI runtimeconfig from delegate +func mergeCNIRuntimeConfig(runtimeConfig *RuntimeConfig, delegate *DelegateNetConf) *RuntimeConfig { + logging.Debugf("mergeCNIRuntimeConfig: %v %v", runtimeConfig, delegate) + var mergedRuntimeConfig RuntimeConfig + + if runtimeConfig == nil { + mergedRuntimeConfig = RuntimeConfig{} + } else { + mergedRuntimeConfig = *runtimeConfig + } + + // multus inject RuntimeConfig only in case of non MasterPlugin. + if delegate.MasterPlugin != true { + logging.Debugf("mergeCNIRuntimeConfig: add runtimeConfig for net-attach-def: %v", mergedRuntimeConfig) + if delegate.PortMappingsRequest != nil { + mergedRuntimeConfig.PortMaps = delegate.PortMappingsRequest + } + if delegate.BandwidthRequest != nil { + mergedRuntimeConfig.Bandwidth = delegate.BandwidthRequest + } + if delegate.IPRequest != nil { + mergedRuntimeConfig.IPs = delegate.IPRequest + } + if delegate.MacRequest != "" { + mergedRuntimeConfig.Mac = delegate.MacRequest + } + if delegate.InfinibandGUIDRequest != "" { + mergedRuntimeConfig.InfinibandGUID = delegate.InfinibandGUIDRequest + } + if delegate.DeviceID != "" { + mergedRuntimeConfig.DeviceID = delegate.DeviceID + } + logging.Debugf("mergeCNIRuntimeConfig: add runtimeConfig for net-attach-def: %v", mergedRuntimeConfig) + } + return &mergedRuntimeConfig +} + +// CreateCNIRuntimeConf create CNI RuntimeConf for a delegate. If delegate configuration +// exists, merge data with the runtime config. +func CreateCNIRuntimeConf(args *skel.CmdArgs, k8sArgs *K8sArgs, ifName string, rc *RuntimeConfig, delegate *DelegateNetConf) (*libcni.RuntimeConf, string) { + podName := string(k8sArgs.K8S_POD_NAME) + podNamespace := string(k8sArgs.K8S_POD_NAMESPACE) + podUID := string(k8sArgs.K8S_POD_UID) + sandboxID := string(k8sArgs.K8S_POD_INFRA_CONTAINER_ID) + return newCNIRuntimeConf(args.ContainerID, sandboxID, podName, podNamespace, podUID, args.Netns, ifName, rc, delegate) +} + +// newCNIRuntimeConf creates the CNI `RuntimeConf` for the given ADD / DEL request. +func newCNIRuntimeConf(containerID, sandboxID, podName, podNamespace, podUID, netNs, ifName string, rc *RuntimeConfig, delegate *DelegateNetConf) (*libcni.RuntimeConf, string) { + logging.Debugf("LoadCNIRuntimeConf: %s, %v %v", ifName, rc, delegate) + + delegateRc := delegateRuntimeConfig(containerID, delegate, rc, ifName) + // In part, adapted from K8s pkg/kubelet/dockershim/network/cni/cni.go#buildCNIRuntimeConf + rt := createRuntimeConf(netNs, podNamespace, podName, containerID, sandboxID, podUID, ifName) + + var cniDeviceInfoFile string + + // Populate rt.Args with CNI_ARGS if the rt.Args value is not set + cniArgs := os.Getenv("CNI_ARGS") + if cniArgs != "" { + logging.Debugf("ARGS: %s", cniArgs) + for _, arg := range strings.Split(cniArgs, ";") { + // SplitN to handle = within values, like BLAH=foo=bar + keyval := strings.SplitN(arg, "=", 2) + if len(keyval) != 2 { + logging.Errorf("CreateCNIRuntimeConf: CNI_ARGS %s %s %d is not recognized as CNI arg, skipped", arg, keyval, len(keyval)) + continue + } + + envKey := string(keyval[0]) + envVal := string(keyval[1]) + found := false + for i := range rt.Args { + // Update existing key if its value is empty + if rt.Args[i][0] == envKey && rt.Args[i][1] == "" && envVal != "" { + logging.Debugf("CreateCNIRuntimeConf: add new val: %s", arg) + rt.Args[i][1] = envVal + found = true + break + } + } + if !found { + // Add the new key if it didn't exist yet + rt.Args = append(rt.Args, [2]string{envKey, envVal}) + } + } + } + + if delegateRc != nil { + cniDeviceInfoFile = delegateRc.CNIDeviceInfoFile + capabilityArgs := map[string]interface{}{} + if len(delegateRc.PortMaps) != 0 { + capabilityArgs["portMappings"] = delegateRc.PortMaps + } + if delegateRc.Bandwidth != nil { + capabilityArgs["bandwidth"] = delegateRc.Bandwidth + } + if len(delegateRc.IPs) != 0 { + capabilityArgs["ips"] = delegateRc.IPs + } + if len(delegateRc.Mac) != 0 { + capabilityArgs["mac"] = delegateRc.Mac + } + if len(delegateRc.InfinibandGUID) != 0 { + capabilityArgs["infinibandGUID"] = delegateRc.InfinibandGUID + } + if delegateRc.DeviceID != "" { + capabilityArgs["deviceID"] = delegateRc.DeviceID + } + if delegateRc.CNIDeviceInfoFile != "" { + capabilityArgs["CNIDeviceInfoFile"] = delegateRc.CNIDeviceInfoFile + } + rt.CapabilityArgs = capabilityArgs + } + return rt, cniDeviceInfoFile +} + +// createRuntimeConf creates the CNI `RuntimeConf` for the given ADD / DEL request. +func createRuntimeConf(netNs, podNamespace, podName, containerID, sandboxID, podUID, ifName string) *libcni.RuntimeConf { + return &libcni.RuntimeConf{ + ContainerID: containerID, + NetNS: netNs, + IfName: ifName, + // NOTE: Verbose logging depends on this order, so please keep Args order. + Args: [][2]string{ + {"IgnoreUnknown", "true"}, + {"K8S_POD_NAMESPACE", podNamespace}, + {"K8S_POD_NAME", podName}, + {"K8S_POD_INFRA_CONTAINER_ID", sandboxID}, + {"K8S_POD_UID", podUID}, + }, + } +} + +// delegateRuntimeConfig creates the CNI `RuntimeConf` for the given ADD / DEL request. +func delegateRuntimeConfig(containerID string, delegate *DelegateNetConf, rc *RuntimeConfig, ifName string) *RuntimeConfig { + var delegateRc *RuntimeConfig + + if delegate != nil { + delegateRc = mergeCNIRuntimeConfig(rc, delegate) + if delegateRc.DeviceID != "" { + if delegateRc.CNIDeviceInfoFile != "" { + logging.Debugf("Warning: Existing value of CNIDeviceInfoFile will be overwritten %s", delegateRc.CNIDeviceInfoFile) + } + autoDeviceInfo := fmt.Sprintf("%s-%s_%s", delegate.Name, containerID, ifName) + delegateRc.CNIDeviceInfoFile = nadutils.GetCNIDeviceInfoPath(autoDeviceInfo) + logging.Debugf("Adding auto-generated CNIDeviceInfoFile: %s", delegateRc.CNIDeviceInfoFile) + } + } else { + delegateRc = rc + } + return delegateRc +} + +// GetGatewayFromResult retrieves gateway IP addresses from CNI result +func GetGatewayFromResult(result *cni100.Result) []net.IP { + var gateways []net.IP + + for _, route := range result.Routes { + if mask, _ := route.Dst.Mask.Size(); mask == 0 { + gateways = append(gateways, route.GW) + } + } + return gateways +} + +// GetDefaultNetConf returns NetConf with default variables +func GetDefaultNetConf() *NetConf { + // LogToStderr's default value set to true + return &NetConf{ + BinDir: defaultBinDir, + ConfDir: defaultConfDir, + CNIDir: defaultCNIDir, + LogToStderr: true, + MultusNamespace: defaultMultusNamespace, + NonIsolatedNamespaces: []string{defaultNonIsolatedNamespace}, + ReadinessIndicatorFile: defaultReadinessIndicatorFile, + SystemNamespaces: []string{"kube-system"}, + } + +} + +// LoadNetConf converts inputs (i.e. stdin) to NetConf +func LoadNetConf(bytes []byte) (*NetConf, error) { + netconf := GetDefaultNetConf() + + logging.Debugf("LoadNetConf: %s", string(bytes)) + if err := json.Unmarshal(bytes, netconf); err != nil { + return nil, logging.Errorf("LoadNetConf: failed to load netconf: %v", err) + } + + // Logging + logging.SetLogStderr(netconf.LogToStderr) + logging.SetLogOptions(netconf.LogOptions) + if netconf.LogFile != "" { + logging.SetLogFile(netconf.LogFile) + } + if netconf.LogLevel != "" { + logging.SetLogLevel(netconf.LogLevel) + } + + // Parse previous result + if netconf.RawPrevResult != nil { + resultBytes, err := json.Marshal(netconf.RawPrevResult) + if err != nil { + return nil, logging.Errorf("LoadNetConf: could not serialize prevResult: %v", err) + } + res, err := version.NewResult(netconf.CNIVersion, resultBytes) + if err != nil { + return nil, logging.Errorf("LoadNetConf: could not parse prevResult: %v", err) + } + netconf.RawPrevResult = nil + netconf.PrevResult, err = cni100.NewResultFromResult(res) + if err != nil { + return nil, logging.Errorf("LoadNetConf: could not convert result to current version: %v", err) + } + } + + // Delegates must always be set. If no kubeconfig is present, the + // delegates are executed in-order. If a kubeconfig is present, + // at least one delegate must be present and the first delegate is + // the master plugin. Kubernetes CRD delegates are then appended to + // the existing delegate list and all delegates executed in-order. + + if len(netconf.RawDelegates) == 0 && netconf.ClusterNetwork == "" { + return nil, logging.Errorf("LoadNetConf: at least one delegate/clusterNetwork must be specified") + } + + // setup namespace isolation + if netconf.RawNonIsolatedNamespaces != "" { + // Parse the comma separated list + nonisolated := strings.Split(netconf.RawNonIsolatedNamespaces, ",") + // Cleanup the whitespace + for i, nonv := range nonisolated { + nonisolated[i] = strings.TrimSpace(nonv) + } + netconf.NonIsolatedNamespaces = nonisolated + } + + // get RawDelegates and put delegates field + if netconf.ClusterNetwork == "" { + // for Delegates + if len(netconf.RawDelegates) == 0 { + return nil, logging.Errorf("LoadNetConf: at least one delegate must be specified") + } + for idx, rawConf := range netconf.RawDelegates { + bytes, err := json.Marshal(rawConf) + if err != nil { + return nil, logging.Errorf("LoadNetConf: error marshalling delegate %d config: %v", idx, err) + } + delegateConf, err := LoadDelegateNetConf(bytes, nil, "", "") + if err != nil { + return nil, logging.Errorf("LoadNetConf: failed to load delegate %d config: %v", idx, err) + } + netconf.Delegates = append(netconf.Delegates, delegateConf) + } + netconf.RawDelegates = nil + + // First delegate is always the master plugin + netconf.Delegates[0].MasterPlugin = true + } + + return netconf, nil +} + +// AddDelegates appends the new delegates to the delegates list +func (n *NetConf) AddDelegates(newDelegates []*DelegateNetConf) error { + logging.Debugf("AddDelegates: %v", newDelegates) + n.Delegates = append(n.Delegates, newDelegates...) + return nil +} + +// delegateAddDeviceID injects deviceID information in delegate bytes +func delegateAddDeviceID(inBytes []byte, deviceID string) ([]byte, error) { + var rawConfig map[string]interface{} + var err error + + err = json.Unmarshal(inBytes, &rawConfig) + if err != nil { + return nil, logging.Errorf("delegateAddDeviceID: failed to unmarshal inBytes: %v", err) + } + // Inject deviceID + rawConfig["deviceID"] = deviceID + rawConfig["pciBusID"] = deviceID + configBytes, err := json.Marshal(rawConfig) + if err != nil { + return nil, logging.Errorf("delegateAddDeviceID: failed to re-marshal Spec.Config: %v", err) + } + logging.Debugf("delegateAddDeviceID updated configBytes %s", string(configBytes)) + return configBytes, nil +} + +// addDeviceIDInConfList injects deviceID information in delegate bytes +func addDeviceIDInConfList(inBytes []byte, deviceID string) ([]byte, error) { + var rawConfig map[string]interface{} + var err error + + err = json.Unmarshal(inBytes, &rawConfig) + if err != nil { + return nil, logging.Errorf("addDeviceIDInConfList: failed to unmarshal inBytes: %v", err) + } + + pList, ok := rawConfig["plugins"] + if !ok { + return nil, logging.Errorf("addDeviceIDInConfList: unable to get plugin list") + } + + pMap, ok := pList.([]interface{}) + if !ok { + return nil, logging.Errorf("addDeviceIDInConfList: unable to typecast plugin list") + } + + for idx, plugin := range pMap { + currentPlugin, ok := plugin.(map[string]interface{}) + if !ok { + return nil, logging.Errorf("addDeviceIDInConfList: unable to typecast plugin #%d", idx) + } + // Inject deviceID + currentPlugin["deviceID"] = deviceID + currentPlugin["pciBusID"] = deviceID + } + + configBytes, err := json.Marshal(rawConfig) + if err != nil { + return nil, logging.Errorf("addDeviceIDInConfList: failed to re-marshal: %v", err) + } + logging.Debugf("addDeviceIDInConfList: updated configBytes %s", string(configBytes)) + return configBytes, nil +} + +// injectCNIArgs injects given args to cniConfig +func injectCNIArgs(cniConfig *map[string]interface{}, args *map[string]interface{}) error { + if argsval, ok := (*cniConfig)["args"]; ok { + argsvalmap := argsval.(map[string]interface{}) + if cnival, ok := argsvalmap["cni"]; ok { + cnivalmap := cnival.(map[string]interface{}) + // merge it if conf has args + for key, val := range *args { + cnivalmap[key] = val + } + } else { + argsvalmap["cni"] = *args + } + } else { + argsval := map[string]interface{}{} + argsval["cni"] = *args + (*cniConfig)["args"] = argsval + } + return nil +} + +// addCNIArgsInConfig injects given cniArgs to CNI config in inBytes +func addCNIArgsInConfig(inBytes []byte, cniArgs *map[string]interface{}) ([]byte, error) { + var rawConfig map[string]interface{} + var err error + + err = json.Unmarshal(inBytes, &rawConfig) + if err != nil { + return nil, logging.Errorf("addCNIArgsInConfig(): failed to unmarshal inBytes: %v", err) + } + + injectCNIArgs(&rawConfig, cniArgs) + + configBytes, err := json.Marshal(rawConfig) + if err != nil { + return nil, logging.Errorf("addCNIArgsInConfig(): failed to re-marshal: %v", err) + } + return configBytes, nil +} + +// addCNIArgsInConfList injects given cniArgs to CNI conflist in inBytes +func addCNIArgsInConfList(inBytes []byte, cniArgs *map[string]interface{}) ([]byte, error) { + var rawConfig map[string]interface{} + var err error + + err = json.Unmarshal(inBytes, &rawConfig) + if err != nil { + return nil, logging.Errorf("addCNIArgsInConfList(): failed to unmarshal inBytes: %v", err) + } + + pList, ok := rawConfig["plugins"] + if !ok { + return nil, logging.Errorf("addCNIArgsInConfList(): unable to get plugin list") + } + + pMap, ok := pList.([]interface{}) + if !ok { + return nil, logging.Errorf("addCNIArgsInConfList(): unable to typecast plugin list") + } + + for idx := range pMap { + valMap := pMap[idx].(map[string]interface{}) + injectCNIArgs(&valMap, cniArgs) + } + + configBytes, err := json.Marshal(rawConfig) + if err != nil { + return nil, logging.Errorf("addCNIArgsInConfList(): failed to re-marshal: %v", err) + } + return configBytes, nil +} + +// CheckGatewayConfig check gatewayRequest and mark IsFilter{V4,V6}Gateway flag if +// gw filtering is required +func CheckGatewayConfig(delegates []*DelegateNetConf) error { + + v4Gateways := 0 + v6Gateways := 0 + + // Check the gateway + for _, delegate := range delegates { + if delegate.GatewayRequest != nil { + for _, gw := range *delegate.GatewayRequest { + if gw.To4() != nil { + v4Gateways++ + } else { + v6Gateways++ + } + } + } + } + + if v4Gateways > 1 || v6Gateways > 1 { + return fmt.Errorf("multus does not support ECMP for default-route") + } + + // set filter flag for each delegate + for i, delegate := range delegates { + delegates[i].IsFilterV4Gateway = true + delegates[i].IsFilterV6Gateway = true + if delegate.GatewayRequest != nil { + for _, gw := range *delegate.GatewayRequest { + if gw.To4() != nil { + delegates[i].IsFilterV4Gateway = false + } else { + delegates[i].IsFilterV6Gateway = false + } + } + } + } + return nil +} + +// CheckSystemNamespaces checks whether given namespace is in systemNamespaces or not. +func CheckSystemNamespaces(namespace string, systemNamespaces []string) bool { + for _, nsname := range systemNamespaces { + if namespace == nsname { + return true + } + } + return false +} diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types/doc.go b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types/doc.go new file mode 100644 index 0000000000..fa4e51c5b2 --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types/doc.go @@ -0,0 +1,16 @@ +// Copyright (c) 2022 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package types contains common types in the multus. +package types diff --git a/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types/types.go b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types/types.go new file mode 100644 index 0000000000..1a410db595 --- /dev/null +++ b/go-controller/vendor/gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types/types.go @@ -0,0 +1,183 @@ +// Copyright (c) 2018 Intel Corporation +// Copyright (c) 2021 Multus Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package types + +import ( + "net" + + "gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging" + + "github.com/containernetworking/cni/pkg/types" + cni100 "github.com/containernetworking/cni/pkg/types/100" + v1 "k8s.io/api/core/v1" +) + +// NetConf for cni config file written in json +type NetConf struct { + types.NetConf + + // support chaining for master interface and IP decisions + // occurring prior to running ipvlan plugin + RawPrevResult *map[string]interface{} `json:"prevResult"` + PrevResult *cni100.Result `json:"-"` + + ConfDir string `json:"confDir"` + CNIDir string `json:"cniDir"` + BinDir string `json:"binDir"` + // RawDelegates is private to the NetConf class; use Delegates instead + RawDelegates []map[string]interface{} `json:"delegates"` + // These parameters are exclusive in one config file: + // - Delegates (directly add delegate CNI config into multus CNI config) + // - ClusterNetwork+DefaultNetworks (add CNI config through CRD, directory or file) + Delegates []*DelegateNetConf `json:"-"` + ClusterNetwork string `json:"clusterNetwork"` + DefaultNetworks []string `json:"defaultNetworks"` + Kubeconfig string `json:"kubeconfig"` + LogFile string `json:"logFile"` + LogLevel string `json:"logLevel"` + LogToStderr bool `json:"logToStderr,omitempty"` + LogOptions *logging.LogOptions `json:"logOptions,omitempty"` + RuntimeConfig *RuntimeConfig `json:"runtimeConfig,omitempty"` + // Default network readiness options + ReadinessIndicatorFile string `json:"readinessindicatorfile"` + // Option to isolate the usage of CR's to the namespace in which a pod resides. + NamespaceIsolation bool `json:"namespaceIsolation"` + RawNonIsolatedNamespaces string `json:"globalNamespaces"` + NonIsolatedNamespaces []string `json:"-"` + + // Option to set system namespaces (to avoid to add defaultNetworks) + SystemNamespaces []string `json:"systemNamespaces"` + // Option to set the namespace that multus-cni uses (clusterNetwork/defaultNetworks) + MultusNamespace string `json:"multusNamespace"` + + // Retry delegate DEL message to next when some error + RetryDeleteOnError bool `json:"retryDeleteOnError"` +} + +// RuntimeConfig specifies CNI RuntimeConfig +type RuntimeConfig struct { + PortMaps []*PortMapEntry `json:"portMappings,omitempty"` + Bandwidth *BandwidthEntry `json:"bandwidth,omitempty"` + IPs []string `json:"ips,omitempty"` + Mac string `json:"mac,omitempty"` + InfinibandGUID string `json:"infinibandGUID,omitempty"` + DeviceID string `json:"deviceID,omitempty"` + CNIDeviceInfoFile string `json:"CNIDeviceInfoFile,omitempty"` +} + +// PortMapEntry for CNI PortMapEntry +type PortMapEntry struct { + HostPort int `json:"hostPort"` + ContainerPort int `json:"containerPort"` + Protocol string `json:"protocol,omitempty"` + HostIP string `json:"hostIP,omitempty"` +} + +// BandwidthEntry for CNI BandwidthEntry +type BandwidthEntry struct { + IngressRate int `json:"ingressRate"` + IngressBurst int `json:"ingressBurst"` + + EgressRate int `json:"egressRate"` + EgressBurst int `json:"egressBurst"` +} + +// DelegateNetConf for net-attach-def for pod +type DelegateNetConf struct { + Conf types.NetConf + ConfList types.NetConfList + Name string + IfnameRequest string `json:"ifnameRequest,omitempty"` + MacRequest string `json:"macRequest,omitempty"` + InfinibandGUIDRequest string `json:"infinibandGUIDRequest,omitempty"` + IPRequest []string `json:"ipRequest,omitempty"` + PortMappingsRequest []*PortMapEntry `json:"-"` + BandwidthRequest *BandwidthEntry `json:"-"` + GatewayRequest *[]net.IP `json:"default-route,omitempty"` + IsFilterV4Gateway bool + IsFilterV6Gateway bool + // MasterPlugin is only used internal housekeeping + MasterPlugin bool `json:"-"` + // Conflist plugin is only used internal housekeeping + ConfListPlugin bool `json:"-"` + // DeviceID is only used internal housekeeping + DeviceID string `json:"deviceID,omitempty"` + // ResourceName is only used internal housekeeping + ResourceName string `json:"resourceName,omitempty"` + + // Raw JSON + Bytes []byte +} + +// NetworkSelectionElement represents one element of the JSON format +// Network Attachment Selection Annotation as described in section 4.1.2 +// of the CRD specification. +type NetworkSelectionElement struct { + // Name contains the name of the Network object this element selects + Name string `json:"name"` + // Namespace contains the optional namespace that the network referenced + // by Name exists in + Namespace string `json:"namespace,omitempty"` + // IPRequest contains an optional requested IP address for this network + // attachment + IPRequest []string `json:"ips,omitempty"` + // MacRequest contains an optional requested MAC address for this + // network attachment + MacRequest string `json:"mac,omitempty"` + // InfinibandGUID request contains an optional requested Infiniband GUID address + // for this network attachment + InfinibandGUIDRequest string `json:"infiniband-guid,omitempty"` + // InterfaceRequest contains an optional requested name for the + // network interface this attachment will create in the container + InterfaceRequest string `json:"interface,omitempty"` + // DeprecatedInterfaceRequest is obsolated parameter at pre 3.2. + // This will be removed in 4.0 release. + DeprecatedInterfaceRequest string `json:"interfaceRequest,omitempty"` + // PortMappingsRequest contains an optional requested port mapping + // for the network + PortMappingsRequest []*PortMapEntry `json:"portMappings,omitempty"` + // BandwidthRequest contains an optional requested bandwidth for + // the network + BandwidthRequest *BandwidthEntry `json:"bandwidth,omitempty"` + // DeviceID contains an optional requested deviceID the network + DeviceID string `json:"deviceID,omitempty"` + // CNIArgs contains additional CNI arguments for the network interface + CNIArgs *map[string]interface{} `json:"cni-args"` + // GatewayRequest contains default route IP address for the pod + GatewayRequest *[]net.IP `json:"default-route,omitempty"` +} + +// K8sArgs is the valid CNI_ARGS used for Kubernetes +type K8sArgs struct { + types.CommonArgs + IP net.IP + K8S_POD_NAME types.UnmarshallableString //revive:disable-line + K8S_POD_NAMESPACE types.UnmarshallableString //revive:disable-line + K8S_POD_INFRA_CONTAINER_ID types.UnmarshallableString //revive:disable-line + K8S_POD_UID types.UnmarshallableString //revive:disable-line +} + +// ResourceInfo is struct to hold Pod device allocation information +type ResourceInfo struct { + Index int + DeviceIDs []string +} + +// ResourceClient provides a kubelet Pod resource handle +type ResourceClient interface { + // GetPodResourceMap returns an instance of a map of Pod ResourceInfo given a (Pod name, namespace) tuple + GetPodResourceMap(*v1.Pod) (map[string]*ResourceInfo, error) +} diff --git a/go-controller/vendor/k8s.io/kubelet/LICENSE b/go-controller/vendor/k8s.io/kubelet/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/go-controller/vendor/k8s.io/kubelet/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/go-controller/vendor/k8s.io/kubelet/pkg/apis/podresources/v1/api.pb.go b/go-controller/vendor/k8s.io/kubelet/pkg/apis/podresources/v1/api.pb.go new file mode 100644 index 0000000000..ac0924b2b1 --- /dev/null +++ b/go-controller/vendor/k8s.io/kubelet/pkg/apis/podresources/v1/api.pb.go @@ -0,0 +1,2822 @@ +/* +Copyright The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: api.proto + +package v1 + +import ( + context "context" + fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" + proto "github.com/gogo/protobuf/proto" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" + reflect "reflect" + strings "strings" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +type AllocatableResourcesRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AllocatableResourcesRequest) Reset() { *m = AllocatableResourcesRequest{} } +func (*AllocatableResourcesRequest) ProtoMessage() {} +func (*AllocatableResourcesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{0} +} +func (m *AllocatableResourcesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AllocatableResourcesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AllocatableResourcesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AllocatableResourcesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AllocatableResourcesRequest.Merge(m, src) +} +func (m *AllocatableResourcesRequest) XXX_Size() int { + return m.Size() +} +func (m *AllocatableResourcesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AllocatableResourcesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AllocatableResourcesRequest proto.InternalMessageInfo + +// AllocatableResourcesResponses contains informations about all the devices known by the kubelet +type AllocatableResourcesResponse struct { + Devices []*ContainerDevices `protobuf:"bytes,1,rep,name=devices,proto3" json:"devices,omitempty"` + CpuIds []int64 `protobuf:"varint,2,rep,packed,name=cpu_ids,json=cpuIds,proto3" json:"cpu_ids,omitempty"` + Memory []*ContainerMemory `protobuf:"bytes,3,rep,name=memory,proto3" json:"memory,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AllocatableResourcesResponse) Reset() { *m = AllocatableResourcesResponse{} } +func (*AllocatableResourcesResponse) ProtoMessage() {} +func (*AllocatableResourcesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{1} +} +func (m *AllocatableResourcesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AllocatableResourcesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AllocatableResourcesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AllocatableResourcesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_AllocatableResourcesResponse.Merge(m, src) +} +func (m *AllocatableResourcesResponse) XXX_Size() int { + return m.Size() +} +func (m *AllocatableResourcesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_AllocatableResourcesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_AllocatableResourcesResponse proto.InternalMessageInfo + +func (m *AllocatableResourcesResponse) GetDevices() []*ContainerDevices { + if m != nil { + return m.Devices + } + return nil +} + +func (m *AllocatableResourcesResponse) GetCpuIds() []int64 { + if m != nil { + return m.CpuIds + } + return nil +} + +func (m *AllocatableResourcesResponse) GetMemory() []*ContainerMemory { + if m != nil { + return m.Memory + } + return nil +} + +// ListPodResourcesRequest is the request made to the PodResourcesLister service +type ListPodResourcesRequest struct { + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListPodResourcesRequest) Reset() { *m = ListPodResourcesRequest{} } +func (*ListPodResourcesRequest) ProtoMessage() {} +func (*ListPodResourcesRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{2} +} +func (m *ListPodResourcesRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListPodResourcesRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListPodResourcesRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListPodResourcesRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListPodResourcesRequest.Merge(m, src) +} +func (m *ListPodResourcesRequest) XXX_Size() int { + return m.Size() +} +func (m *ListPodResourcesRequest) XXX_DiscardUnknown() { + xxx_messageInfo_ListPodResourcesRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_ListPodResourcesRequest proto.InternalMessageInfo + +// ListPodResourcesResponse is the response returned by List function +type ListPodResourcesResponse struct { + PodResources []*PodResources `protobuf:"bytes,1,rep,name=pod_resources,json=podResources,proto3" json:"pod_resources,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ListPodResourcesResponse) Reset() { *m = ListPodResourcesResponse{} } +func (*ListPodResourcesResponse) ProtoMessage() {} +func (*ListPodResourcesResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{3} +} +func (m *ListPodResourcesResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ListPodResourcesResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ListPodResourcesResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ListPodResourcesResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_ListPodResourcesResponse.Merge(m, src) +} +func (m *ListPodResourcesResponse) XXX_Size() int { + return m.Size() +} +func (m *ListPodResourcesResponse) XXX_DiscardUnknown() { + xxx_messageInfo_ListPodResourcesResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_ListPodResourcesResponse proto.InternalMessageInfo + +func (m *ListPodResourcesResponse) GetPodResources() []*PodResources { + if m != nil { + return m.PodResources + } + return nil +} + +// PodResources contains information about the node resources assigned to a pod +type PodResources struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Namespace string `protobuf:"bytes,2,opt,name=namespace,proto3" json:"namespace,omitempty"` + Containers []*ContainerResources `protobuf:"bytes,3,rep,name=containers,proto3" json:"containers,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *PodResources) Reset() { *m = PodResources{} } +func (*PodResources) ProtoMessage() {} +func (*PodResources) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{4} +} +func (m *PodResources) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *PodResources) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_PodResources.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *PodResources) XXX_Merge(src proto.Message) { + xxx_messageInfo_PodResources.Merge(m, src) +} +func (m *PodResources) XXX_Size() int { + return m.Size() +} +func (m *PodResources) XXX_DiscardUnknown() { + xxx_messageInfo_PodResources.DiscardUnknown(m) +} + +var xxx_messageInfo_PodResources proto.InternalMessageInfo + +func (m *PodResources) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *PodResources) GetNamespace() string { + if m != nil { + return m.Namespace + } + return "" +} + +func (m *PodResources) GetContainers() []*ContainerResources { + if m != nil { + return m.Containers + } + return nil +} + +// ContainerResources contains information about the resources assigned to a container +type ContainerResources struct { + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + Devices []*ContainerDevices `protobuf:"bytes,2,rep,name=devices,proto3" json:"devices,omitempty"` + CpuIds []int64 `protobuf:"varint,3,rep,packed,name=cpu_ids,json=cpuIds,proto3" json:"cpu_ids,omitempty"` + Memory []*ContainerMemory `protobuf:"bytes,4,rep,name=memory,proto3" json:"memory,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContainerResources) Reset() { *m = ContainerResources{} } +func (*ContainerResources) ProtoMessage() {} +func (*ContainerResources) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{5} +} +func (m *ContainerResources) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerResources) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerResources.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerResources) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerResources.Merge(m, src) +} +func (m *ContainerResources) XXX_Size() int { + return m.Size() +} +func (m *ContainerResources) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerResources.DiscardUnknown(m) +} + +var xxx_messageInfo_ContainerResources proto.InternalMessageInfo + +func (m *ContainerResources) GetName() string { + if m != nil { + return m.Name + } + return "" +} + +func (m *ContainerResources) GetDevices() []*ContainerDevices { + if m != nil { + return m.Devices + } + return nil +} + +func (m *ContainerResources) GetCpuIds() []int64 { + if m != nil { + return m.CpuIds + } + return nil +} + +func (m *ContainerResources) GetMemory() []*ContainerMemory { + if m != nil { + return m.Memory + } + return nil +} + +// ContainerMemory contains information about memory and hugepages assigned to a container +type ContainerMemory struct { + MemoryType string `protobuf:"bytes,1,opt,name=memory_type,json=memoryType,proto3" json:"memory_type,omitempty"` + Size_ uint64 `protobuf:"varint,2,opt,name=size,proto3" json:"size,omitempty"` + Topology *TopologyInfo `protobuf:"bytes,3,opt,name=topology,proto3" json:"topology,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContainerMemory) Reset() { *m = ContainerMemory{} } +func (*ContainerMemory) ProtoMessage() {} +func (*ContainerMemory) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{6} +} +func (m *ContainerMemory) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerMemory) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerMemory.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerMemory) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerMemory.Merge(m, src) +} +func (m *ContainerMemory) XXX_Size() int { + return m.Size() +} +func (m *ContainerMemory) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerMemory.DiscardUnknown(m) +} + +var xxx_messageInfo_ContainerMemory proto.InternalMessageInfo + +func (m *ContainerMemory) GetMemoryType() string { + if m != nil { + return m.MemoryType + } + return "" +} + +func (m *ContainerMemory) GetSize_() uint64 { + if m != nil { + return m.Size_ + } + return 0 +} + +func (m *ContainerMemory) GetTopology() *TopologyInfo { + if m != nil { + return m.Topology + } + return nil +} + +// ContainerDevices contains information about the devices assigned to a container +type ContainerDevices struct { + ResourceName string `protobuf:"bytes,1,opt,name=resource_name,json=resourceName,proto3" json:"resource_name,omitempty"` + DeviceIds []string `protobuf:"bytes,2,rep,name=device_ids,json=deviceIds,proto3" json:"device_ids,omitempty"` + Topology *TopologyInfo `protobuf:"bytes,3,opt,name=topology,proto3" json:"topology,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *ContainerDevices) Reset() { *m = ContainerDevices{} } +func (*ContainerDevices) ProtoMessage() {} +func (*ContainerDevices) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{7} +} +func (m *ContainerDevices) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ContainerDevices) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_ContainerDevices.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *ContainerDevices) XXX_Merge(src proto.Message) { + xxx_messageInfo_ContainerDevices.Merge(m, src) +} +func (m *ContainerDevices) XXX_Size() int { + return m.Size() +} +func (m *ContainerDevices) XXX_DiscardUnknown() { + xxx_messageInfo_ContainerDevices.DiscardUnknown(m) +} + +var xxx_messageInfo_ContainerDevices proto.InternalMessageInfo + +func (m *ContainerDevices) GetResourceName() string { + if m != nil { + return m.ResourceName + } + return "" +} + +func (m *ContainerDevices) GetDeviceIds() []string { + if m != nil { + return m.DeviceIds + } + return nil +} + +func (m *ContainerDevices) GetTopology() *TopologyInfo { + if m != nil { + return m.Topology + } + return nil +} + +// Topology describes hardware topology of the resource +type TopologyInfo struct { + Nodes []*NUMANode `protobuf:"bytes,1,rep,name=nodes,proto3" json:"nodes,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *TopologyInfo) Reset() { *m = TopologyInfo{} } +func (*TopologyInfo) ProtoMessage() {} +func (*TopologyInfo) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{8} +} +func (m *TopologyInfo) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *TopologyInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_TopologyInfo.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *TopologyInfo) XXX_Merge(src proto.Message) { + xxx_messageInfo_TopologyInfo.Merge(m, src) +} +func (m *TopologyInfo) XXX_Size() int { + return m.Size() +} +func (m *TopologyInfo) XXX_DiscardUnknown() { + xxx_messageInfo_TopologyInfo.DiscardUnknown(m) +} + +var xxx_messageInfo_TopologyInfo proto.InternalMessageInfo + +func (m *TopologyInfo) GetNodes() []*NUMANode { + if m != nil { + return m.Nodes + } + return nil +} + +// NUMA representation of NUMA node +type NUMANode struct { + ID int64 `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *NUMANode) Reset() { *m = NUMANode{} } +func (*NUMANode) ProtoMessage() {} +func (*NUMANode) Descriptor() ([]byte, []int) { + return fileDescriptor_00212fb1f9d3bf1c, []int{9} +} +func (m *NUMANode) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NUMANode) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NUMANode.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *NUMANode) XXX_Merge(src proto.Message) { + xxx_messageInfo_NUMANode.Merge(m, src) +} +func (m *NUMANode) XXX_Size() int { + return m.Size() +} +func (m *NUMANode) XXX_DiscardUnknown() { + xxx_messageInfo_NUMANode.DiscardUnknown(m) +} + +var xxx_messageInfo_NUMANode proto.InternalMessageInfo + +func (m *NUMANode) GetID() int64 { + if m != nil { + return m.ID + } + return 0 +} + +func init() { + proto.RegisterType((*AllocatableResourcesRequest)(nil), "v1.AllocatableResourcesRequest") + proto.RegisterType((*AllocatableResourcesResponse)(nil), "v1.AllocatableResourcesResponse") + proto.RegisterType((*ListPodResourcesRequest)(nil), "v1.ListPodResourcesRequest") + proto.RegisterType((*ListPodResourcesResponse)(nil), "v1.ListPodResourcesResponse") + proto.RegisterType((*PodResources)(nil), "v1.PodResources") + proto.RegisterType((*ContainerResources)(nil), "v1.ContainerResources") + proto.RegisterType((*ContainerMemory)(nil), "v1.ContainerMemory") + proto.RegisterType((*ContainerDevices)(nil), "v1.ContainerDevices") + proto.RegisterType((*TopologyInfo)(nil), "v1.TopologyInfo") + proto.RegisterType((*NUMANode)(nil), "v1.NUMANode") +} + +func init() { proto.RegisterFile("api.proto", fileDescriptor_00212fb1f9d3bf1c) } + +var fileDescriptor_00212fb1f9d3bf1c = []byte{ + // 539 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xc1, 0x6e, 0xd3, 0x40, + 0x10, 0xed, 0xda, 0x21, 0x6d, 0xa6, 0x29, 0x54, 0x0b, 0x22, 0x26, 0x4d, 0xdd, 0xc8, 0x5c, 0x22, + 0x01, 0xae, 0x1a, 0x04, 0xf7, 0xd2, 0x48, 0x28, 0x12, 0x8d, 0x60, 0x55, 0xae, 0x44, 0x8e, 0xbd, + 0x0d, 0x96, 0x12, 0xef, 0xe2, 0x5d, 0x47, 0x84, 0x13, 0x07, 0x3e, 0x80, 0x03, 0x67, 0xfe, 0x83, + 0x3f, 0xe8, 0x91, 0x23, 0x47, 0x1a, 0x7e, 0x04, 0xed, 0xda, 0x4e, 0x9d, 0x26, 0x01, 0xf5, 0xe4, + 0xd9, 0x79, 0x33, 0xb3, 0x6f, 0xe6, 0x8d, 0x17, 0x2a, 0x1e, 0x0f, 0x5d, 0x1e, 0x33, 0xc9, 0xb0, + 0x31, 0x39, 0xaa, 0x3f, 0x19, 0x86, 0xf2, 0x7d, 0x32, 0x70, 0x7d, 0x36, 0x3e, 0x1c, 0xb2, 0x21, + 0x3b, 0xd4, 0xd0, 0x20, 0x39, 0xd7, 0x27, 0x7d, 0xd0, 0x56, 0x9a, 0xe2, 0xec, 0xc3, 0xde, 0xf1, + 0x68, 0xc4, 0x7c, 0x4f, 0x7a, 0x83, 0x11, 0x25, 0x54, 0xb0, 0x24, 0xf6, 0xa9, 0x20, 0xf4, 0x43, + 0x42, 0x85, 0x74, 0xbe, 0x21, 0x68, 0xac, 0xc6, 0x05, 0x67, 0x91, 0xa0, 0xd8, 0x85, 0xcd, 0x80, + 0x4e, 0x42, 0x9f, 0x0a, 0x0b, 0x35, 0xcd, 0xd6, 0x76, 0xfb, 0x9e, 0x3b, 0x39, 0x72, 0x4f, 0x58, + 0x24, 0xbd, 0x30, 0xa2, 0x71, 0x27, 0xc5, 0x48, 0x1e, 0x84, 0x6b, 0xb0, 0xe9, 0xf3, 0xa4, 0x1f, + 0x06, 0xc2, 0x32, 0x9a, 0x66, 0xcb, 0x24, 0x65, 0x9f, 0x27, 0xdd, 0x40, 0xe0, 0x47, 0x50, 0x1e, + 0xd3, 0x31, 0x8b, 0xa7, 0x96, 0xa9, 0xeb, 0xdc, 0x5d, 0xa8, 0x73, 0xaa, 0x21, 0x92, 0x85, 0x38, + 0x0f, 0xa0, 0xf6, 0x2a, 0x14, 0xf2, 0x35, 0x0b, 0x96, 0x18, 0xbf, 0x01, 0x6b, 0x19, 0xca, 0xc8, + 0x3e, 0x83, 0x1d, 0xce, 0x82, 0x7e, 0x9c, 0x03, 0x19, 0xe5, 0x5d, 0x75, 0xd5, 0x42, 0x42, 0x95, + 0x17, 0x4e, 0xce, 0x47, 0xa8, 0x16, 0x51, 0x8c, 0xa1, 0x14, 0x79, 0x63, 0x6a, 0xa1, 0x26, 0x6a, + 0x55, 0x88, 0xb6, 0x71, 0x03, 0x2a, 0xea, 0x2b, 0xb8, 0xe7, 0x53, 0xcb, 0xd0, 0xc0, 0x95, 0x03, + 0x3f, 0x07, 0xf0, 0xf3, 0x56, 0x44, 0xd6, 0xe0, 0xfd, 0x85, 0x06, 0xaf, 0xee, 0x2e, 0x44, 0x3a, + 0xdf, 0x11, 0xe0, 0xe5, 0x90, 0x95, 0x04, 0x0a, 0x42, 0x18, 0x37, 0x14, 0xc2, 0x5c, 0x23, 0x44, + 0xe9, 0xff, 0x42, 0x48, 0xb8, 0x73, 0x0d, 0xc2, 0x07, 0xb0, 0x9d, 0x82, 0x7d, 0x39, 0xe5, 0x39, + 0x47, 0x48, 0x5d, 0x67, 0x53, 0x4e, 0x15, 0x7b, 0x11, 0x7e, 0x4a, 0xa7, 0x54, 0x22, 0xda, 0xc6, + 0x8f, 0x61, 0x4b, 0x32, 0xce, 0x46, 0x6c, 0xa8, 0xf4, 0x47, 0xb9, 0x28, 0x67, 0x99, 0xaf, 0x1b, + 0x9d, 0x33, 0x32, 0x8f, 0x70, 0xbe, 0x20, 0xd8, 0xbd, 0xde, 0x19, 0x7e, 0x08, 0x3b, 0xb9, 0xb0, + 0xfd, 0xc2, 0x74, 0xaa, 0xb9, 0xb3, 0xa7, 0xa6, 0xb4, 0x0f, 0x90, 0x0e, 0x60, 0xbe, 0x81, 0x15, + 0x52, 0x49, 0x3d, 0xaa, 0xf7, 0x9b, 0xd1, 0x68, 0x43, 0xb5, 0x88, 0x60, 0x07, 0x6e, 0x45, 0x2c, + 0x98, 0xaf, 0x55, 0x55, 0xa5, 0xf6, 0xde, 0x9e, 0x1e, 0xf7, 0x58, 0x40, 0x49, 0x0a, 0x39, 0x75, + 0xd8, 0xca, 0x5d, 0xf8, 0x36, 0x18, 0xdd, 0x8e, 0xa6, 0x69, 0x12, 0xa3, 0xdb, 0x69, 0xff, 0x40, + 0x80, 0x8b, 0x8b, 0xa6, 0xf6, 0x98, 0xc6, 0xf8, 0x04, 0x4a, 0xca, 0xc2, 0x7b, 0xaa, 0xde, 0x9a, + 0xb5, 0xaf, 0x37, 0x56, 0x83, 0xe9, 0xe2, 0x3b, 0x1b, 0xf8, 0x1d, 0xd4, 0x5e, 0x52, 0xb9, 0xea, + 0x57, 0xc6, 0x07, 0x2a, 0xf5, 0x1f, 0x8f, 0x40, 0xbd, 0xb9, 0x3e, 0x20, 0xaf, 0xff, 0xa2, 0x71, + 0x71, 0x69, 0xa3, 0x5f, 0x97, 0xf6, 0xc6, 0xe7, 0x99, 0x8d, 0x2e, 0x66, 0x36, 0xfa, 0x39, 0xb3, + 0xd1, 0xef, 0x99, 0x8d, 0xbe, 0xfe, 0xb1, 0x37, 0x06, 0x65, 0xfd, 0xd8, 0x3c, 0xfd, 0x1b, 0x00, + 0x00, 0xff, 0xff, 0x43, 0x46, 0x5d, 0x7f, 0xac, 0x04, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// PodResourcesListerClient is the client API for PodResourcesLister service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type PodResourcesListerClient interface { + List(ctx context.Context, in *ListPodResourcesRequest, opts ...grpc.CallOption) (*ListPodResourcesResponse, error) + GetAllocatableResources(ctx context.Context, in *AllocatableResourcesRequest, opts ...grpc.CallOption) (*AllocatableResourcesResponse, error) +} + +type podResourcesListerClient struct { + cc *grpc.ClientConn +} + +func NewPodResourcesListerClient(cc *grpc.ClientConn) PodResourcesListerClient { + return &podResourcesListerClient{cc} +} + +func (c *podResourcesListerClient) List(ctx context.Context, in *ListPodResourcesRequest, opts ...grpc.CallOption) (*ListPodResourcesResponse, error) { + out := new(ListPodResourcesResponse) + err := c.cc.Invoke(ctx, "/v1.PodResourcesLister/List", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *podResourcesListerClient) GetAllocatableResources(ctx context.Context, in *AllocatableResourcesRequest, opts ...grpc.CallOption) (*AllocatableResourcesResponse, error) { + out := new(AllocatableResourcesResponse) + err := c.cc.Invoke(ctx, "/v1.PodResourcesLister/GetAllocatableResources", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// PodResourcesListerServer is the server API for PodResourcesLister service. +type PodResourcesListerServer interface { + List(context.Context, *ListPodResourcesRequest) (*ListPodResourcesResponse, error) + GetAllocatableResources(context.Context, *AllocatableResourcesRequest) (*AllocatableResourcesResponse, error) +} + +// UnimplementedPodResourcesListerServer can be embedded to have forward compatible implementations. +type UnimplementedPodResourcesListerServer struct { +} + +func (*UnimplementedPodResourcesListerServer) List(ctx context.Context, req *ListPodResourcesRequest) (*ListPodResourcesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method List not implemented") +} +func (*UnimplementedPodResourcesListerServer) GetAllocatableResources(ctx context.Context, req *AllocatableResourcesRequest) (*AllocatableResourcesResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method GetAllocatableResources not implemented") +} + +func RegisterPodResourcesListerServer(s *grpc.Server, srv PodResourcesListerServer) { + s.RegisterService(&_PodResourcesLister_serviceDesc, srv) +} + +func _PodResourcesLister_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListPodResourcesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PodResourcesListerServer).List(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1.PodResourcesLister/List", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PodResourcesListerServer).List(ctx, req.(*ListPodResourcesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _PodResourcesLister_GetAllocatableResources_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AllocatableResourcesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(PodResourcesListerServer).GetAllocatableResources(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/v1.PodResourcesLister/GetAllocatableResources", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(PodResourcesListerServer).GetAllocatableResources(ctx, req.(*AllocatableResourcesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _PodResourcesLister_serviceDesc = grpc.ServiceDesc{ + ServiceName: "v1.PodResourcesLister", + HandlerType: (*PodResourcesListerServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "List", + Handler: _PodResourcesLister_List_Handler, + }, + { + MethodName: "GetAllocatableResources", + Handler: _PodResourcesLister_GetAllocatableResources_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "api.proto", +} + +func (m *AllocatableResourcesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AllocatableResourcesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AllocatableResourcesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *AllocatableResourcesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AllocatableResourcesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AllocatableResourcesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Memory) > 0 { + for iNdEx := len(m.Memory) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Memory[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.CpuIds) > 0 { + dAtA2 := make([]byte, len(m.CpuIds)*10) + var j1 int + for _, num1 := range m.CpuIds { + num := uint64(num1) + for num >= 1<<7 { + dAtA2[j1] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j1++ + } + dAtA2[j1] = uint8(num) + j1++ + } + i -= j1 + copy(dAtA[i:], dAtA2[:j1]) + i = encodeVarintApi(dAtA, i, uint64(j1)) + i-- + dAtA[i] = 0x12 + } + if len(m.Devices) > 0 { + for iNdEx := len(m.Devices) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Devices[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *ListPodResourcesRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListPodResourcesRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListPodResourcesRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *ListPodResourcesResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ListPodResourcesResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ListPodResourcesResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.PodResources) > 0 { + for iNdEx := len(m.PodResources) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.PodResources[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *PodResources) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *PodResources) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *PodResources) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Containers) > 0 { + for iNdEx := len(m.Containers) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Containers[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + } + if len(m.Namespace) > 0 { + i -= len(m.Namespace) + copy(dAtA[i:], m.Namespace) + i = encodeVarintApi(dAtA, i, uint64(len(m.Namespace))) + i-- + dAtA[i] = 0x12 + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintApi(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ContainerResources) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ContainerResources) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerResources) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Memory) > 0 { + for iNdEx := len(m.Memory) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Memory[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } + if len(m.CpuIds) > 0 { + dAtA4 := make([]byte, len(m.CpuIds)*10) + var j3 int + for _, num1 := range m.CpuIds { + num := uint64(num1) + for num >= 1<<7 { + dAtA4[j3] = uint8(uint64(num)&0x7f | 0x80) + num >>= 7 + j3++ + } + dAtA4[j3] = uint8(num) + j3++ + } + i -= j3 + copy(dAtA[i:], dAtA4[:j3]) + i = encodeVarintApi(dAtA, i, uint64(j3)) + i-- + dAtA[i] = 0x1a + } + if len(m.Devices) > 0 { + for iNdEx := len(m.Devices) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Devices[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintApi(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ContainerMemory) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ContainerMemory) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerMemory) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Topology != nil { + { + size, err := m.Topology.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if m.Size_ != 0 { + i = encodeVarintApi(dAtA, i, uint64(m.Size_)) + i-- + dAtA[i] = 0x10 + } + if len(m.MemoryType) > 0 { + i -= len(m.MemoryType) + copy(dAtA[i:], m.MemoryType) + i = encodeVarintApi(dAtA, i, uint64(len(m.MemoryType))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *ContainerDevices) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ContainerDevices) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ContainerDevices) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Topology != nil { + { + size, err := m.Topology.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + } + if len(m.DeviceIds) > 0 { + for iNdEx := len(m.DeviceIds) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.DeviceIds[iNdEx]) + copy(dAtA[i:], m.DeviceIds[iNdEx]) + i = encodeVarintApi(dAtA, i, uint64(len(m.DeviceIds[iNdEx]))) + i-- + dAtA[i] = 0x12 + } + } + if len(m.ResourceName) > 0 { + i -= len(m.ResourceName) + copy(dAtA[i:], m.ResourceName) + i = encodeVarintApi(dAtA, i, uint64(len(m.ResourceName))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *TopologyInfo) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *TopologyInfo) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *TopologyInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Nodes) > 0 { + for iNdEx := len(m.Nodes) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Nodes[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintApi(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +func (m *NUMANode) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *NUMANode) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NUMANode) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ID != 0 { + i = encodeVarintApi(dAtA, i, uint64(m.ID)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintApi(dAtA []byte, offset int, v uint64) int { + offset -= sovApi(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *AllocatableResourcesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *AllocatableResourcesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Devices) > 0 { + for _, e := range m.Devices { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + if len(m.CpuIds) > 0 { + l = 0 + for _, e := range m.CpuIds { + l += sovApi(uint64(e)) + } + n += 1 + sovApi(uint64(l)) + l + } + if len(m.Memory) > 0 { + for _, e := range m.Memory { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + return n +} + +func (m *ListPodResourcesRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *ListPodResourcesResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.PodResources) > 0 { + for _, e := range m.PodResources { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + return n +} + +func (m *PodResources) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + l = len(m.Namespace) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if len(m.Containers) > 0 { + for _, e := range m.Containers { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + return n +} + +func (m *ContainerResources) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Name) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if len(m.Devices) > 0 { + for _, e := range m.Devices { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + if len(m.CpuIds) > 0 { + l = 0 + for _, e := range m.CpuIds { + l += sovApi(uint64(e)) + } + n += 1 + sovApi(uint64(l)) + l + } + if len(m.Memory) > 0 { + for _, e := range m.Memory { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + return n +} + +func (m *ContainerMemory) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.MemoryType) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if m.Size_ != 0 { + n += 1 + sovApi(uint64(m.Size_)) + } + if m.Topology != nil { + l = m.Topology.Size() + n += 1 + l + sovApi(uint64(l)) + } + return n +} + +func (m *ContainerDevices) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.ResourceName) + if l > 0 { + n += 1 + l + sovApi(uint64(l)) + } + if len(m.DeviceIds) > 0 { + for _, s := range m.DeviceIds { + l = len(s) + n += 1 + l + sovApi(uint64(l)) + } + } + if m.Topology != nil { + l = m.Topology.Size() + n += 1 + l + sovApi(uint64(l)) + } + return n +} + +func (m *TopologyInfo) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Nodes) > 0 { + for _, e := range m.Nodes { + l = e.Size() + n += 1 + l + sovApi(uint64(l)) + } + } + return n +} + +func (m *NUMANode) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.ID != 0 { + n += 1 + sovApi(uint64(m.ID)) + } + return n +} + +func sovApi(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozApi(x uint64) (n int) { + return sovApi(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *AllocatableResourcesRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&AllocatableResourcesRequest{`, + `}`, + }, "") + return s +} +func (this *AllocatableResourcesResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForDevices := "[]*ContainerDevices{" + for _, f := range this.Devices { + repeatedStringForDevices += strings.Replace(f.String(), "ContainerDevices", "ContainerDevices", 1) + "," + } + repeatedStringForDevices += "}" + repeatedStringForMemory := "[]*ContainerMemory{" + for _, f := range this.Memory { + repeatedStringForMemory += strings.Replace(f.String(), "ContainerMemory", "ContainerMemory", 1) + "," + } + repeatedStringForMemory += "}" + s := strings.Join([]string{`&AllocatableResourcesResponse{`, + `Devices:` + repeatedStringForDevices + `,`, + `CpuIds:` + fmt.Sprintf("%v", this.CpuIds) + `,`, + `Memory:` + repeatedStringForMemory + `,`, + `}`, + }, "") + return s +} +func (this *ListPodResourcesRequest) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ListPodResourcesRequest{`, + `}`, + }, "") + return s +} +func (this *ListPodResourcesResponse) String() string { + if this == nil { + return "nil" + } + repeatedStringForPodResources := "[]*PodResources{" + for _, f := range this.PodResources { + repeatedStringForPodResources += strings.Replace(f.String(), "PodResources", "PodResources", 1) + "," + } + repeatedStringForPodResources += "}" + s := strings.Join([]string{`&ListPodResourcesResponse{`, + `PodResources:` + repeatedStringForPodResources + `,`, + `}`, + }, "") + return s +} +func (this *PodResources) String() string { + if this == nil { + return "nil" + } + repeatedStringForContainers := "[]*ContainerResources{" + for _, f := range this.Containers { + repeatedStringForContainers += strings.Replace(f.String(), "ContainerResources", "ContainerResources", 1) + "," + } + repeatedStringForContainers += "}" + s := strings.Join([]string{`&PodResources{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Namespace:` + fmt.Sprintf("%v", this.Namespace) + `,`, + `Containers:` + repeatedStringForContainers + `,`, + `}`, + }, "") + return s +} +func (this *ContainerResources) String() string { + if this == nil { + return "nil" + } + repeatedStringForDevices := "[]*ContainerDevices{" + for _, f := range this.Devices { + repeatedStringForDevices += strings.Replace(f.String(), "ContainerDevices", "ContainerDevices", 1) + "," + } + repeatedStringForDevices += "}" + repeatedStringForMemory := "[]*ContainerMemory{" + for _, f := range this.Memory { + repeatedStringForMemory += strings.Replace(f.String(), "ContainerMemory", "ContainerMemory", 1) + "," + } + repeatedStringForMemory += "}" + s := strings.Join([]string{`&ContainerResources{`, + `Name:` + fmt.Sprintf("%v", this.Name) + `,`, + `Devices:` + repeatedStringForDevices + `,`, + `CpuIds:` + fmt.Sprintf("%v", this.CpuIds) + `,`, + `Memory:` + repeatedStringForMemory + `,`, + `}`, + }, "") + return s +} +func (this *ContainerMemory) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ContainerMemory{`, + `MemoryType:` + fmt.Sprintf("%v", this.MemoryType) + `,`, + `Size_:` + fmt.Sprintf("%v", this.Size_) + `,`, + `Topology:` + strings.Replace(this.Topology.String(), "TopologyInfo", "TopologyInfo", 1) + `,`, + `}`, + }, "") + return s +} +func (this *ContainerDevices) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&ContainerDevices{`, + `ResourceName:` + fmt.Sprintf("%v", this.ResourceName) + `,`, + `DeviceIds:` + fmt.Sprintf("%v", this.DeviceIds) + `,`, + `Topology:` + strings.Replace(this.Topology.String(), "TopologyInfo", "TopologyInfo", 1) + `,`, + `}`, + }, "") + return s +} +func (this *TopologyInfo) String() string { + if this == nil { + return "nil" + } + repeatedStringForNodes := "[]*NUMANode{" + for _, f := range this.Nodes { + repeatedStringForNodes += strings.Replace(f.String(), "NUMANode", "NUMANode", 1) + "," + } + repeatedStringForNodes += "}" + s := strings.Join([]string{`&TopologyInfo{`, + `Nodes:` + repeatedStringForNodes + `,`, + `}`, + }, "") + return s +} +func (this *NUMANode) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&NUMANode{`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, + `}`, + }, "") + return s +} +func valueToStringApi(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *AllocatableResourcesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AllocatableResourcesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AllocatableResourcesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AllocatableResourcesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AllocatableResourcesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AllocatableResourcesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Devices", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Devices = append(m.Devices, &ContainerDevices{}) + if err := m.Devices[len(m.Devices)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType == 0 { + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.CpuIds = append(m.CpuIds, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.CpuIds) == 0 { + m.CpuIds = make([]int64, 0, elementCount) + } + for iNdEx < postIndex { + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.CpuIds = append(m.CpuIds, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field CpuIds", wireType) + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Memory = append(m.Memory, &ContainerMemory{}) + if err := m.Memory[len(m.Memory)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListPodResourcesRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListPodResourcesRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListPodResourcesRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ListPodResourcesResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ListPodResourcesResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ListPodResourcesResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field PodResources", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.PodResources = append(m.PodResources, &PodResources{}) + if err := m.PodResources[len(m.PodResources)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *PodResources) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: PodResources: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: PodResources: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Namespace", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Namespace = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Containers", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Containers = append(m.Containers, &ContainerResources{}) + if err := m.Containers[len(m.Containers)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ContainerResources) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ContainerResources: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ContainerResources: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Devices", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Devices = append(m.Devices, &ContainerDevices{}) + if err := m.Devices[len(m.Devices)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType == 0 { + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.CpuIds = append(m.CpuIds, v) + } else if wireType == 2 { + var packedLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + packedLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if packedLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + packedLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + var elementCount int + var count int + for _, integer := range dAtA[iNdEx:postIndex] { + if integer < 128 { + count++ + } + } + elementCount = count + if elementCount != 0 && len(m.CpuIds) == 0 { + m.CpuIds = make([]int64, 0, elementCount) + } + for iNdEx < postIndex { + var v int64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.CpuIds = append(m.CpuIds, v) + } + } else { + return fmt.Errorf("proto: wrong wireType = %d for field CpuIds", wireType) + } + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Memory", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Memory = append(m.Memory, &ContainerMemory{}) + if err := m.Memory[len(m.Memory)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ContainerMemory) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ContainerMemory: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ContainerMemory: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MemoryType", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MemoryType = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Size_", wireType) + } + m.Size_ = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Size_ |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Topology", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Topology == nil { + m.Topology = &TopologyInfo{} + } + if err := m.Topology.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *ContainerDevices) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ContainerDevices: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ContainerDevices: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ResourceName", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ResourceName = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field DeviceIds", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.DeviceIds = append(m.DeviceIds, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Topology", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Topology == nil { + m.Topology = &TopologyInfo{} + } + if err := m.Topology.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *TopologyInfo) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: TopologyInfo: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: TopologyInfo: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Nodes", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthApi + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthApi + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Nodes = append(m.Nodes, &NUMANode{}) + if err := m.Nodes[len(m.Nodes)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NUMANode) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: NUMANode: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NUMANode: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType) + } + m.ID = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowApi + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ID |= int64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipApi(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthApi + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipApi(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowApi + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthApi + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupApi + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthApi + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthApi = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowApi = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupApi = fmt.Errorf("proto: unexpected end of group") +) diff --git a/go-controller/vendor/k8s.io/kubelet/pkg/apis/podresources/v1/api.proto b/go-controller/vendor/k8s.io/kubelet/pkg/apis/podresources/v1/api.proto new file mode 100644 index 0000000000..add2aad487 --- /dev/null +++ b/go-controller/vendor/k8s.io/kubelet/pkg/apis/podresources/v1/api.proto @@ -0,0 +1,78 @@ +// To regenerate api.pb.go run hack/update-generated-pod-resources.sh +syntax = "proto3"; + +package v1; + +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; + +option (gogoproto.goproto_stringer_all) = false; +option (gogoproto.stringer_all) = true; +option (gogoproto.goproto_getters_all) = true; +option (gogoproto.marshaler_all) = true; +option (gogoproto.sizer_all) = true; +option (gogoproto.unmarshaler_all) = true; +option (gogoproto.goproto_unrecognized_all) = false; + + +// PodResourcesLister is a service provided by the kubelet that provides information about the +// node resources consumed by pods and containers on the node +service PodResourcesLister { + rpc List(ListPodResourcesRequest) returns (ListPodResourcesResponse) {} + rpc GetAllocatableResources(AllocatableResourcesRequest) returns (AllocatableResourcesResponse) {} +} + +message AllocatableResourcesRequest {} + +// AllocatableResourcesResponses contains informations about all the devices known by the kubelet +message AllocatableResourcesResponse { + repeated ContainerDevices devices = 1; + repeated int64 cpu_ids = 2; + repeated ContainerMemory memory = 3; +} + +// ListPodResourcesRequest is the request made to the PodResourcesLister service +message ListPodResourcesRequest {} + +// ListPodResourcesResponse is the response returned by List function +message ListPodResourcesResponse { + repeated PodResources pod_resources = 1; +} + +// PodResources contains information about the node resources assigned to a pod +message PodResources { + string name = 1; + string namespace = 2; + repeated ContainerResources containers = 3; +} + +// ContainerResources contains information about the resources assigned to a container +message ContainerResources { + string name = 1; + repeated ContainerDevices devices = 2; + repeated int64 cpu_ids = 3; + repeated ContainerMemory memory = 4; +} + +// ContainerMemory contains information about memory and hugepages assigned to a container +message ContainerMemory { + string memory_type = 1; + uint64 size = 2; + TopologyInfo topology = 3; +} + +// ContainerDevices contains information about the devices assigned to a container +message ContainerDevices { + string resource_name = 1; + repeated string device_ids = 2; + TopologyInfo topology = 3; +} + +// Topology describes hardware topology of the resource +message TopologyInfo { + repeated NUMANode nodes = 1; +} + +// NUMA representation of NUMA node +message NUMANode { + int64 ID = 1; +} diff --git a/go-controller/vendor/modules.txt b/go-controller/vendor/modules.txt index bc1ed25ce6..9a8ff2e37f 100644 --- a/go-controller/vendor/modules.txt +++ b/go-controller/vendor/modules.txt @@ -701,6 +701,12 @@ gopkg.in/gcfg.v1/types # gopkg.in/inf.v0 v0.9.1 ## explicit gopkg.in/inf.v0 +# gopkg.in/k8snetworkplumbingwg/multus-cni.v4 v4.0.2 +## explicit; go 1.18 +gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/checkpoint +gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/kubeletclient +gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/logging +gopkg.in/k8snetworkplumbingwg/multus-cni.v4/pkg/types # gopkg.in/natefinch/lumberjack.v2 v2.2.1 ## explicit; go 1.13 gopkg.in/natefinch/lumberjack.v2 @@ -1228,6 +1234,9 @@ k8s.io/kube-openapi/pkg/schemaconv k8s.io/kube-openapi/pkg/spec3 k8s.io/kube-openapi/pkg/util/proto k8s.io/kube-openapi/pkg/validation/spec +# k8s.io/kubelet v0.22.8 +## explicit; go 1.16 +k8s.io/kubelet/pkg/apis/podresources/v1 # k8s.io/kubernetes v1.33.3 ## explicit; go 1.24.0 k8s.io/kubernetes/pkg/api/v1/pod diff --git a/helm/ovn-kubernetes/charts/ovnkube-control-plane/templates/ovnkube-control-plane.yaml b/helm/ovn-kubernetes/charts/ovnkube-control-plane/templates/ovnkube-control-plane.yaml index 84082cf2bd..48b9fd36f5 100644 --- a/helm/ovn-kubernetes/charts/ovnkube-control-plane/templates/ovnkube-control-plane.yaml +++ b/helm/ovn-kubernetes/charts/ovnkube-control-plane/templates/ovnkube-control-plane.yaml @@ -128,6 +128,8 @@ spec: value: {{ default "" .Values.global.enableNetworkSegmentation | quote }} - name: OVN_PRE_CONF_UDN_ADDR_ENABLE value: {{ default "" .Values.global.enablePreconfiguredUDNAddresses | quote }} + - name: OVN_ADVERTISED_UDN_ISOLATION_MODE + value: {{ default "strict" .Values.global.advertisedUDNIsolationMode | quote }} - name: OVN_HYBRID_OVERLAY_NET_CIDR value: {{ default "" .Values.global.hybridOverlayNetCidr | quote }} - name: OVN_DISABLE_SNAT_MULTIPLE_GWS diff --git a/helm/ovn-kubernetes/charts/ovnkube-master/templates/deployment-ovnkube-master.yaml b/helm/ovn-kubernetes/charts/ovnkube-master/templates/deployment-ovnkube-master.yaml index 98cfd97a65..b91c2ab65c 100644 --- a/helm/ovn-kubernetes/charts/ovnkube-master/templates/deployment-ovnkube-master.yaml +++ b/helm/ovn-kubernetes/charts/ovnkube-master/templates/deployment-ovnkube-master.yaml @@ -236,6 +236,8 @@ spec: value: {{ hasKey .Values.global "enableMultiNetwork" | ternary .Values.global.enableMultiNetwork false | quote }} - name: OVN_NETWORK_SEGMENTATION_ENABLE value: {{ default "" .Values.global.enableNetworkSegmentation | quote }} + - name: OVN_ADVERTISED_UDN_ISOLATION_MODE + value: {{ default "strict" .Values.global.advertisedUDNIsolationMode | quote }} - name: OVN_EGRESSSERVICE_ENABLE value: {{ default "" .Values.global.enableEgressService | quote }} - name: OVN_HYBRID_OVERLAY_NET_CIDR diff --git a/helm/ovn-kubernetes/charts/ovnkube-node/templates/ovnkube-node.yaml b/helm/ovn-kubernetes/charts/ovnkube-node/templates/ovnkube-node.yaml index a5350d29fc..f0bd81f391 100644 --- a/helm/ovn-kubernetes/charts/ovnkube-node/templates/ovnkube-node.yaml +++ b/helm/ovn-kubernetes/charts/ovnkube-node/templates/ovnkube-node.yaml @@ -231,6 +231,8 @@ spec: value: {{ default "" .Values.global.enableNetworkSegmentation | quote }} - name: OVN_PRE_CONF_UDN_ADDR_ENABLE value: {{ default "" .Values.global.enablePreconfiguredUDNAddresses | quote }} + - name: OVN_ADVERTISED_UDN_ISOLATION_MODE + value: {{ default "strict" .Values.global.advertisedUDNIsolationMode | quote }} - name: OVN_ENABLE_INTERCONNECT value: {{ hasKey .Values.global "enableInterconnect" | ternary .Values.global.enableInterconnect false | quote }} - name: OVN_ENABLE_MULTI_EXTERNAL_GATEWAY diff --git a/helm/ovn-kubernetes/charts/ovnkube-single-node-zone/templates/ovnkube-single-node-zone.yaml b/helm/ovn-kubernetes/charts/ovnkube-single-node-zone/templates/ovnkube-single-node-zone.yaml index 4872a9663a..abb2d38133 100644 --- a/helm/ovn-kubernetes/charts/ovnkube-single-node-zone/templates/ovnkube-single-node-zone.yaml +++ b/helm/ovn-kubernetes/charts/ovnkube-single-node-zone/templates/ovnkube-single-node-zone.yaml @@ -281,6 +281,9 @@ spec: name: run-systemd subPath: private readOnly: true + - mountPath: /var/run/k8s.cni.cncf.io/devinfo/dp + name: host-devinfo-dp + readOnly: true resources: requests: cpu: 100m @@ -416,6 +419,8 @@ spec: value: {{ default "" .Values.global.enableNetworkSegmentation | quote }} - name: OVN_PRE_CONF_UDN_ADDR_ENABLE value: {{ default "" .Values.global.enablePreconfiguredUDNAddresses | quote }} + - name: OVN_ADVERTISED_UDN_ISOLATION_MODE + value: {{ default "strict" .Values.global.advertisedUDNIsolationMode | quote }} - name: OVNKUBE_NODE_MGMT_PORT_NETDEV value: {{ default "" .Values.global.nodeMgmtPortNetdev | quote }} - name: OVN_EMPTY_LB_EVENTS @@ -600,5 +605,8 @@ spec: - name: run-systemd hostPath: path: /run/systemd + - name: host-devinfo-dp + hostPath: + path: /var/run/k8s.cni.cncf.io/devinfo/dp tolerations: - operator: "Exists" diff --git a/helm/ovn-kubernetes/charts/ovnkube-zone-controller/templates/ovnkube-zone-controller.yaml b/helm/ovn-kubernetes/charts/ovnkube-zone-controller/templates/ovnkube-zone-controller.yaml index 0451cd7660..2cb6f2b86b 100644 --- a/helm/ovn-kubernetes/charts/ovnkube-zone-controller/templates/ovnkube-zone-controller.yaml +++ b/helm/ovn-kubernetes/charts/ovnkube-zone-controller/templates/ovnkube-zone-controller.yaml @@ -315,6 +315,8 @@ spec: value: {{ default "" .Values.global.enableNetworkSegmentation | quote }} - name: OVN_PRE_CONF_UDN_ADDR_ENABLE value: {{ default "" .Values.global.enablePreconfiguredUDNAddresses | quote }} + - name: OVN_ADVERTISED_UDN_ISOLATION_MODE + value: {{ default "strict" .Values.global.advertisedUDNIsolationMode | quote }} - name: OVN_HYBRID_OVERLAY_NET_CIDR value: {{ default "" .Values.global.hybridOverlayNetCidr | quote }} - name: OVN_DISABLE_SNAT_MULTIPLE_GWS diff --git a/test/e2e/e2e.go b/test/e2e/e2e.go index 4d81005bff..3a1923efce 100644 --- a/test/e2e/e2e.go +++ b/test/e2e/e2e.go @@ -2051,7 +2051,7 @@ var _ = ginkgo.Describe("e2e br-int flow monitoring export validation", func() { }) -func getNodePodCIDRs(nodeName string) (string, string, error) { +func getNodePodCIDRs(nodeName, netName string) (string, string, error) { // retrieve the pod cidr for the worker node jsonFlag := "jsonpath='{.metadata.annotations.k8s\\.ovn\\.org/node-subnets}'" kubectlOut, err := e2ekubectl.RunKubectl("default", "get", "node", nodeName, "-o", jsonFlag) @@ -2066,7 +2066,7 @@ func getNodePodCIDRs(nodeName string) (string, string, error) { ssSubnets := make(map[string]string) if err := json.Unmarshal([]byte(annotation), &ssSubnets); err == nil { // If only one subnet, determine if it's v4 or v6 - if subnet, ok := ssSubnets["default"]; ok { + if subnet, ok := ssSubnets[netName]; ok { if strings.Contains(subnet, ":") { ipv6CIDR = subnet } else { @@ -2078,7 +2078,7 @@ func getNodePodCIDRs(nodeName string) (string, string, error) { dsSubnets := make(map[string][]string) if err := json.Unmarshal([]byte(annotation), &dsSubnets); err == nil { - if subnets, ok := dsSubnets["default"]; ok && len(subnets) > 0 { + if subnets, ok := dsSubnets[netName]; ok && len(subnets) > 0 { // Classify each subnet as IPv4 or IPv6 for _, subnet := range subnets { if strings.Contains(subnet, ":") { @@ -2091,7 +2091,7 @@ func getNodePodCIDRs(nodeName string) (string, string, error) { } } - return "", "", fmt.Errorf("could not parse annotation %q", annotation) + return "", "", fmt.Errorf("could not parse annotation %q for network %s", annotation, netName) } var _ = ginkgo.Describe("e2e delete databases", func() { diff --git a/test/e2e/external_gateways.go b/test/e2e/external_gateways.go index c3b2f12198..628866910b 100644 --- a/test/e2e/external_gateways.go +++ b/test/e2e/external_gateways.go @@ -194,7 +194,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { framework.Logf("Annotating the external gateway test namespace to a container gw: %s ", exGWIpAlt1) e2ekubectl.RunKubectlOrDie(f.Namespace.Name, annotateArgs...) - podCIDR, _, err := getNodePodCIDRs(node.Name) + podCIDR, _, err := getNodePodCIDRs(node.Name, "default") if err != nil { framework.Failf("Error retrieving the pod cidr from %s %v", node.Name, err) } @@ -401,7 +401,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { nodeIP = primaryNetworkInf.IPv6 } framework.Logf("the pod side node is %s and the source node ip is %s", workerNodeInfo.name, nodeIP) - podCIDR, _, err := getNodePodCIDRs(workerNodeInfo.name) + podCIDR, _, err := getNodePodCIDRs(workerNodeInfo.name, "default") if err != nil { framework.Failf("Error retrieving the pod cidr from %s %v", workerNodeInfo.name, err) } @@ -857,7 +857,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { podConnEntriesWithMACLabelsSet := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, macAddressGW) gomega.Expect(podConnEntriesWithMACLabelsSet).To(gomega.Equal(2)) totalPodConnEntries := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, nil) - gomega.Expect(totalPodConnEntries).To(gomega.Equal(6)) // total conntrack entries for this pod/protocol + gomega.Expect(totalPodConnEntries).To(gomega.Equal(4)) // total conntrack entries for this pod/protocol ginkgo.By("Remove second external gateway IP from the app namespace annotation") annotateNamespaceForGateway(f.Namespace.Name, false, addresses.gatewayIPs[0]) @@ -867,7 +867,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { totalPodConnEntries = pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, nil) gomega.Expect(podConnEntriesWithMACLabelsSet).To(gomega.Equal(1)) // we still have the conntrack entry for the remaining gateway - gomega.Expect(totalPodConnEntries).To(gomega.Equal(5)) // 6-1 + gomega.Expect(totalPodConnEntries).To(gomega.Equal(3)) // 4-1 ginkgo.By("Remove first external gateway IP from the app namespace annotation") annotateNamespaceForGateway(f.Namespace.Name, false, "") @@ -877,7 +877,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { totalPodConnEntries = pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, nil) gomega.Expect(podConnEntriesWithMACLabelsSet).To(gomega.Equal(0)) // we don't have any remaining gateways left - gomega.Expect(totalPodConnEntries).To(gomega.Equal(4)) // 6-2 + gomega.Expect(totalPodConnEntries).To(gomega.Equal(2)) // 4-2 }, ginkgo.Entry("IPV4 udp", &addressesv4, "udp"), @@ -938,7 +938,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { podConnEntriesWithMACLabelsSet := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, macAddressGW) gomega.Expect(podConnEntriesWithMACLabelsSet).To(gomega.Equal(2)) totalPodConnEntries := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, nil) - gomega.Expect(totalPodConnEntries).To(gomega.Equal(6)) // total conntrack entries for this pod/protocol + gomega.Expect(totalPodConnEntries).To(gomega.Equal(4)) // total conntrack entries for this pod/protocol cleanUpFn := handleGatewayPodRemoval(f, removalType, gatewayPodName2, servingNamespace, addresses.gatewayIPs[1], true) if cleanUpFn != nil { @@ -960,7 +960,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { totalPodConnEntries = pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, nil) gomega.Expect(podConnEntriesWithMACLabelsSet).To(gomega.Equal(1)) // we still have the conntrack entry for the remaining gateway - gomega.Expect(totalPodConnEntries).To(gomega.Equal(5)) // 6-1 + gomega.Expect(totalPodConnEntries).To(gomega.Equal(3)) // 4-1 ginkgo.By("Remove first external gateway pod's routing-namespace annotation") annotatePodForGateway(gatewayPodName1, servingNamespace, "", addresses.gatewayIPs[0], false) @@ -980,7 +980,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { totalPodConnEntries = pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, nil) gomega.Expect(podConnEntriesWithMACLabelsSet).To(gomega.Equal(0)) // we don't have any remaining gateways left - gomega.Expect(totalPodConnEntries).To(gomega.Equal(4)) // 6-2 + gomega.Expect(totalPodConnEntries).To(gomega.Equal(2)) // 4-2 }, ginkgo.Entry("IPV4 udp + pod annotation update", &addressesv4, "udp", GatewayUpdate), ginkgo.Entry("IPV4 tcp + pod annotation update", &addressesv4, "tcp", GatewayUpdate), @@ -1960,7 +1960,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { ginkgo.By("Check if conntrack entries for ECMP routes are created for the 2 external gateways") nodeName := getPod(f, srcPodName).Spec.NodeName podConnEntriesWithMACLabelsSet := 2 - totalPodConnEntries := 6 + totalPodConnEntries := 4 gomega.Eventually(func() int { return pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, macAddressGW) }, time.Minute, 5).Should(gomega.Equal(podConnEntriesWithMACLabelsSet)) @@ -1970,7 +1970,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { updateAPBExternalRouteCRWithStaticHop(defaultPolicyName, f.Namespace.Name, false, addresses.gatewayIPs[0]) podConnEntriesWithMACLabelsSet = 1 // we still have the conntrack entry for the remaining gateway - totalPodConnEntries = 5 // 6-1 + totalPodConnEntries = 3 // 4-1 gomega.Eventually(func() int { n := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, macAddressGW) @@ -1985,7 +1985,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { ginkgo.By("Check if conntrack entries for ECMP routes are removed for the deleted external gateway if traffic is UDP") podConnEntriesWithMACLabelsSet = 0 // we don't have any remaining gateways left - totalPodConnEntries = 4 // 6-2 + totalPodConnEntries = 2 // 4-2 gomega.Eventually(func() int { n := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, macAddressGW) @@ -2038,7 +2038,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { ginkgo.By("Check if conntrack entries for ECMP routes are created for the 2 external gateways") nodeName := getPod(f, srcPodName).Spec.NodeName podConnEntriesWithMACLabelsSet := 2 // TCP - totalPodConnEntries := 6 // TCP + totalPodConnEntries := 4 // TCP gomega.Eventually(func() int { n := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, macAddressGW) @@ -2055,7 +2055,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { ginkgo.By("Check if conntrack entries for ECMP routes are removed for the deleted external gateway if traffic is UDP") podConnEntriesWithMACLabelsSet = 1 // we still have the conntrack entry for the remaining gateway - totalPodConnEntries = 5 // 6-1 + totalPodConnEntries = 3 // 4-1 gomega.Eventually(func() int { n := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, macAddressGW) @@ -2072,7 +2072,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { ginkgo.By("Check if conntrack entries for ECMP routes are removed for the deleted external gateway if traffic is UDP") podConnEntriesWithMACLabelsSet = 0 //we don't have any remaining gateways left - totalPodConnEntries = 4 + totalPodConnEntries = 2 gomega.Eventually(func() int { n := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, macAddressGW) klog.Infof("Number of entries with macAddressGW %s:%d", macAddressGW, n) @@ -2776,9 +2776,9 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { } nodeName := getPod(f, srcPodName).Spec.NodeName - expectedTotalEntries := 6 + expectedTotalEntries := 4 expectedMACEntries := 2 - ginkgo.By("Check to ensure initial conntrack entries are 2 mac address label, and 6 total entries") + ginkgo.By("Check to ensure initial conntrack entries are 2 mac address label, and 4 total entries") gomega.Eventually(func() int { n := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, macAddressGW) klog.Infof("Number of entries with macAddressGW %s:%d", macAddressGW, n) @@ -2848,7 +2848,7 @@ var _ = ginkgo.Describe("External Gateway", feature.ExternalGateway, func() { podConnEntriesWithMACLabelsSet := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, macAddressGW) gomega.Expect(podConnEntriesWithMACLabelsSet).To(gomega.Equal(2)) totalPodConnEntries := pokeConntrackEntries(nodeName, addresses.srcPodIP, protocol, nil) - gomega.Expect(totalPodConnEntries).To(gomega.Equal(6)) // total conntrack entries for this pod/protocol + gomega.Expect(totalPodConnEntries).To(gomega.Equal(4)) // total conntrack entries for this pod/protocol checkAPBExternalRouteStatus(defaultPolicyName) }, ginkgo.Entry("IPV4 udp", &addressesv4, "udp"), diff --git a/test/e2e/route_advertisements.go b/test/e2e/route_advertisements.go index 36c0c5c950..5f9e53a8cc 100644 --- a/test/e2e/route_advertisements.go +++ b/test/e2e/route_advertisements.go @@ -20,6 +20,7 @@ import ( apitypes "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/types" udnv1 "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/userdefinednetwork/v1" udnclientset "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/userdefinednetwork/v1/apis/clientset/versioned" + "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/types" "github.com/ovn-org/ovn-kubernetes/test/e2e/deploymentconfig" "github.com/ovn-org/ovn-kubernetes/test/e2e/feature" "github.com/ovn-org/ovn-kubernetes/test/e2e/images" @@ -61,18 +62,7 @@ var _ = ginkgo.Describe("BGP: Pod to external server when default podNetwork is f := wrappedTestFramework("pod2external-route-advertisements") ginkgo.BeforeEach(func() { - serverContainerIPs = []string{} - bgpNetwork, err := infraprovider.Get().GetNetwork(bgpExternalNetworkName) - framework.ExpectNoError(err, "must get bgpnet network") - bgpServer := infraapi.ExternalContainer{Name: serverContainerName} - networkInterface, err := infraprovider.Get().GetExternalContainerNetworkInterface(bgpServer, bgpNetwork) - framework.ExpectNoError(err, "container %s attached to network %s must contain network info", serverContainerName, bgpExternalNetworkName) - if isIPv4Supported(f.ClientSet) && len(networkInterface.IPv4) > 0 { - serverContainerIPs = append(serverContainerIPs, networkInterface.IPv4) - } - if isIPv6Supported(f.ClientSet) && len(networkInterface.IPv6) > 0 { - serverContainerIPs = append(serverContainerIPs, networkInterface.IPv6) - } + serverContainerIPs = getBGPServerContainerIPs(f) framework.Logf("The external server IPs are: %+v", serverContainerIPs) providerPrimaryNetwork, err := infraprovider.Get().PrimaryNetwork() framework.ExpectNoError(err, "provider primary network must be available") @@ -184,33 +174,7 @@ var _ = ginkgo.Describe("BGP: Pod to external server when default podNetwork is //10.244.2.0/24 nhid 25 via 172.18.0.4 dev eth0 proto bgp metric 20 for _, serverContainerIP := range serverContainerIPs { for _, node := range nodes.Items { - podv4CIDR, podv6CIDR, err := getNodePodCIDRs(node.Name) - if err != nil { - framework.Failf("Error retrieving the pod cidr from %s %v", node.Name, err) - } - framework.Logf("the pod cidr for node %s-%s is %s", node.Name, podv4CIDR, podv6CIDR) - ipVer := "" - podCIDR := podv4CIDR - nodeIP := e2enode.GetAddressesByTypeAndFamily(&node, corev1.NodeInternalIP, corev1.IPv4Protocol) - if utilnet.IsIPv6String(serverContainerIP) { - ipVer = " -6" - podCIDR = podv6CIDR - // BGP by default uses LLA as nexthops in its routes - nodeIPv6LLA, err := GetNodeIPv6LinkLocalAddressForEth0(node.Name) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) - nodeIP = []string{nodeIPv6LLA} - } - gomega.Expect(len(nodeIP)).To(gomega.BeNumerically(">", 0)) - framework.Logf("the nodeIP for node %s is %+v", node.Name, nodeIP) - externalContainer := infraapi.ExternalContainer{Name: routerContainerName} - bgpRouteCommand := strings.Split(fmt.Sprintf("ip%s route show %s", ipVer, podCIDR), " ") - framework.Logf("Checking for node %s's route for pod subnet %s", node.Name, podCIDR) - gomega.Eventually(func() bool { - routes, err := infraprovider.Get().ExecExternalContainerCommand(externalContainer, bgpRouteCommand) - framework.ExpectNoError(err, "failed to get BGP routes from intermediary router") - framework.Logf("Routes in FRR %s", routes) - return strings.Contains(routes, nodeIP[0]) - }, 30*time.Second).Should(gomega.BeTrue()) + checkL3NodePodRoute(node, serverContainerIP, routerContainerName, types.DefaultNetworkName) } } @@ -381,6 +345,12 @@ var _ = ginkgo.Describe("BGP: Pod to external server when CUDN network is advert externalServerV4CIDR, externalServerV6CIDR, err := bgpNetwork.IPv4IPv6Subnets() framework.ExpectNoError(err, "must get BGP network subnets") framework.Logf("the network cidrs to be imported are v4=%s and v6=%s", externalServerV4CIDR, externalServerV6CIDR) + var nodeIPv6LLA string + if isDualStackCluster(nodes) { + var err error + nodeIPv6LLA, err = GetNodeIPv6LinkLocalAddressForEth0(routerContainerName) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + } for _, node := range nodes.Items { ipVer := "" bgpRouteCommand := strings.Split(fmt.Sprintf("ip%s route show %s", ipVer, externalServerV4CIDR), " ") @@ -393,8 +363,6 @@ var _ = ginkgo.Describe("BGP: Pod to external server when CUDN network is advert }, 30*time.Second).Should(gomega.BeTrue()) if isDualStackCluster(nodes) { ipVer = " -6" - nodeIPv6LLA, err := GetNodeIPv6LinkLocalAddressForEth0(routerContainerName) - gomega.Expect(err).NotTo(gomega.HaveOccurred()) bgpRouteCommand := strings.Split(fmt.Sprintf("ip%s route show %s", ipVer, externalServerV6CIDR), " ") framework.Logf("Checking for server's route in node %s", node.Name) gomega.Eventually(func() bool { @@ -534,7 +502,7 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks" ipFamilyV6 ) - f := wrappedTestFramework("bpp-network-isolation") + f := wrappedTestFramework("bgp-network-isolation") f.SkipNamespaceCreation = true var udnNamespaceA, udnNamespaceB *corev1.Namespace var nodes *corev1.NodeList @@ -721,6 +689,20 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks" } return condition.Reason }, 30*time.Second, time.Second).Should(gomega.Equal("Accepted")) + + ginkgo.By("ensure routes from UDNs are learned by the external FRR router") + serverContainerIPs := getBGPServerContainerIPs(f) + for _, serverContainerIP := range serverContainerIPs { + for _, node := range nodes.Items { + if cudnA.Spec.Network.Topology == udnv1.NetworkTopologyLayer3 { + checkL3NodePodRoute(node, serverContainerIP, routerContainerName, types.CUDNPrefix+cudnATemplate.Name) + checkL3NodePodRoute(node, serverContainerIP, routerContainerName, types.CUDNPrefix+cudnBTemplate.Name) + } else { + checkL2NodePodRoute(node, serverContainerIP, routerContainerName, cudnATemplate.Spec.Network.Layer2.Subnets) + checkL2NodePodRoute(node, serverContainerIP, routerContainerName, cudnBTemplate.Spec.Network.Layer2.Subnets) + } + } + } }) ginkgo.AfterEach(func() { @@ -860,7 +842,7 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks" framework.ExpectNoError(err) return clientPod.Name, clientPod.Namespace, net.JoinHostPort(srvPodStatus.IPs[ipFamilyIndex].IP.String(), "8080") + "/clientip", clientPodStatus.IPs[ipFamilyIndex].IP.String(), false }), - ginkgo.Entry("pod to pod on different networks and same node should not work", + ginkgo.Entry("pod to pod connectivity on different networks and same node", func(ipFamilyIndex int) (clientName string, clientNamespace string, dst string, expectedOutput string, expectErr bool) { // podsNetA[2] and podNetB are on the same node clientPod := podsNetA[2] @@ -868,10 +850,29 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks" srvPodStatus, err := getPodAnnotationForAttachment(srvPod, namespacedName(srvPod.Namespace, cudnBTemplate.Name)) framework.ExpectNoError(err) - return clientPod.Name, clientPod.Namespace, net.JoinHostPort(srvPodStatus.IPs[ipFamilyIndex].IP.String(), "8080") + "/clientip", curlConnectionTimeoutCode, true + var ( + curlOutput string + curlErr bool + ) + // Test behavior depends on the ADVERTISED_UDN_ISOLATION_MODE environment variable: + // - "loose": Pod connectivity is allowed, test expects success + // - anything else (including unset): Treated as "strict", pod connectivity is blocked + if os.Getenv("ADVERTISED_UDN_ISOLATION_MODE") == "loose" { + clientPodStatus, err := getPodAnnotationForAttachment(clientPod, namespacedName(clientPod.Namespace, cudnATemplate.Name)) + framework.ExpectNoError(err) + + // With the above underlay routing configuration client pod can reach server pod. + curlOutput = clientPodStatus.IPs[ipFamilyIndex].IP.String() + curlErr = false + } else { + curlOutput = curlConnectionTimeoutCode + curlErr = true + } + return clientPod.Name, clientPod.Namespace, net.JoinHostPort(srvPodStatus.IPs[ipFamilyIndex].IP.String(), "8080") + "/clientip", + curlOutput, curlErr }), - ginkgo.Entry("pod to pod on different networks and different nodes should not work", + ginkgo.Entry("pod to pod connectivity on different networks and different nodes", func(ipFamilyIndex int) (clientName string, clientNamespace string, dst string, expectedOutput string, expectErr bool) { // podsNetA[0] and podNetB are on different nodes clientPod := podsNetA[0] @@ -879,7 +880,21 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks" srvPodStatus, err := getPodAnnotationForAttachment(srvPod, namespacedName(srvPod.Namespace, cudnBTemplate.Name)) framework.ExpectNoError(err) - return clientPod.Name, clientPod.Namespace, net.JoinHostPort(srvPodStatus.IPs[ipFamilyIndex].IP.String(), "8080") + "/clientip", curlConnectionTimeoutCode, true + var ( + curlOutput string + curlErr bool + ) + if os.Getenv("ADVERTISED_UDN_ISOLATION_MODE") == "loose" { + clientPodStatus, err := getPodAnnotationForAttachment(clientPod, namespacedName(clientPod.Namespace, cudnATemplate.Name)) + framework.ExpectNoError(err) + + curlOutput = clientPodStatus.IPs[ipFamilyIndex].IP.String() + curlErr = false + } else { + curlOutput = curlConnectionTimeoutCode + curlErr = true + } + return clientPod.Name, clientPod.Namespace, net.JoinHostPort(srvPodStatus.IPs[ipFamilyIndex].IP.String(), "8080") + "/clientip", curlOutput, curlErr }), ginkgo.Entry("pod in the default network should not be able to access an advertised UDN pod on the same node", func(ipFamilyIndex int) (clientName string, clientNamespace string, dst string, expectedOutput string, expectErr bool) { @@ -911,7 +926,16 @@ var _ = ginkgo.DescribeTableSubtree("BGP: isolation between advertised networks" }), ginkgo.Entry("pod in the UDN should not be able to access a default network service", func(ipFamilyIndex int) (clientName string, clientNamespace string, dst string, expectedOutput string, expectErr bool) { - return podsNetA[0].Name, podsNetA[0].Namespace, net.JoinHostPort(svcNetDefault.Spec.ClusterIPs[ipFamilyIndex], "8080") + "/clientip", curlConnectionTimeoutCode, true + err := true + out := curlConnectionTimeoutCode + if cudnATemplate.Spec.Network.Topology == udnv1.NetworkTopologyLayer2 { + // FIXME: prevent looping of traffic in L2 UDNs + // bad behaviour: packet is looping from management port -> breth0 -> GR -> management port -> breth0 and so on + // which is a never ending loop + // this causes curl timeout with code 7 host unreachable instead of code 28 + out = "" + } + return podsNetA[0].Name, podsNetA[0].Namespace, net.JoinHostPort(svcNetDefault.Spec.ClusterIPs[ipFamilyIndex], "8080") + "/clientip", out, err }), ginkgo.Entry("pod in the UDN should be able to access kapi in default network service", func(ipFamilyIndex int) (clientName string, clientNamespace string, dst string, expectedOutput string, expectErr bool) { @@ -2308,3 +2332,89 @@ func createRouteAdvertisements( return nil } + +// getBGPServerContainerIPs retrieves the IP addresses of the BGP server container. +func getBGPServerContainerIPs(f *framework.Framework) (serverContainerIPs []string) { + bgpNetwork, err := infraprovider.Get().GetNetwork(bgpExternalNetworkName) // pre-created network + framework.ExpectNoError(err, "must get bgpnet network") + bgpServer := infraapi.ExternalContainer{Name: serverContainerName} + networkInterface, err := infraprovider.Get().GetExternalContainerNetworkInterface(bgpServer, bgpNetwork) + framework.ExpectNoError(err, "container %s attached to network %s must contain network info", serverContainerName, bgpExternalNetworkName) + if isIPv4Supported(f.ClientSet) && len(networkInterface.IPv4) > 0 { + serverContainerIPs = append(serverContainerIPs, networkInterface.IPv4) + } + if isIPv6Supported(f.ClientSet) && len(networkInterface.IPv6) > 0 { + serverContainerIPs = append(serverContainerIPs, networkInterface.IPv6) + } + return +} + +// checkL3NodePodRoute checks that the BGP route for the given node's pod subnet is present in the FRR router. +// It takes the node to check, a serverContainerIP to determine the IP family in use, and the router container name. +func checkL3NodePodRoute(node corev1.Node, serverContainerIP, routerContainerName, netName string) { + var podv4CIDR, podv6CIDR string + + gomega.Eventually(func() error { + var err error + podv4CIDR, podv6CIDR, err = getNodePodCIDRs(node.Name, netName) + return err + }, 5*time.Second).Should(gomega.Succeed(), "failed to get pod CIDR for node %s, network %s", node.Name, netName) + + framework.Logf("The pod CIDRs for node %s are: v4=%s, v6=%s", node.Name, podv4CIDR, podv6CIDR) + isIPv6 := utilnet.IsIPv6String(serverContainerIP) + podCIDR := podv4CIDR + if isIPv6 { + podCIDR = podv6CIDR + } + gomega.Expect(podCIDR).NotTo(gomega.BeEmpty(), + "pod CIDR for family (isIPv6=%t) missing for node %s on network %s", isIPv6, node.Name, netName) + + checkRouteInFRR(node, podCIDR, routerContainerName, isIPv6) +} + +// checkL2NodePodRoute checks that BGP routes for the given CIDRs are present in the FRR router. +func checkL2NodePodRoute(node corev1.Node, serverContainerIP, routerContainerName string, cidrs udnv1.DualStackCIDRs) { + isServerIPv6 := utilnet.IsIPv6String(serverContainerIP) + for _, podCIDR := range cidrs { + isPodCIDRv6 := utilnet.IsIPv6CIDRString(string(podCIDR)) + // Skip checking if the CIDR family does not match the serverContainerIP family. + if isServerIPv6 != isPodCIDRv6 { + continue + } + checkRouteInFRR(node, string(podCIDR), routerContainerName, isPodCIDRv6) + } +} + +// checkRouteInFRR verifies that a route for a given podCIDR exists in the specified FRR container, +// with the correct node as its nexthop. +func checkRouteInFRR(node corev1.Node, podCIDR, routerContainerName string, isIPv6 bool) { + var ( + ipVer string + nodeIP []string + err error + ) + + if isIPv6 { + ipVer = " -6" + // BGP uses the link-local address as the nexthop for IPv6 routes by default. + var nodeIPv6LLA string + nodeIPv6LLA, err = GetNodeIPv6LinkLocalAddressForEth0(node.Name) + gomega.Expect(err).NotTo(gomega.HaveOccurred()) + nodeIP = []string{nodeIPv6LLA} + } else { + nodeIP = e2enode.GetAddressesByTypeAndFamily(&node, corev1.NodeInternalIP, corev1.IPv4Protocol) + } + gomega.Expect(len(nodeIP)).To(gomega.BeNumerically(">", 0), "must find a valid nexthop IP for node %s", node.Name) + framework.Logf("Using nexthop %s for node %s", nodeIP[0], node.Name) + + externalContainer := infraapi.ExternalContainer{Name: routerContainerName} + bgpRouteCommand := strings.Split(fmt.Sprintf("ip%s route show %s", ipVer, podCIDR), " ") + framework.Logf("Checking on router %s for node %s's route to pod subnet %s", routerContainerName, node.Name, podCIDR) + + gomega.Eventually(func() bool { + routes, err := infraprovider.Get().ExecExternalContainerCommand(externalContainer, bgpRouteCommand) + framework.ExpectNoError(err, "failed to get BGP routes from intermediary router") + framework.Logf("Routes in FRR for %s: %s", podCIDR, routes) + return strings.Contains(routes, nodeIP[0]) + }, 30*time.Second).Should(gomega.BeTrue(), "route for %s via %s not found on %s", podCIDR, nodeIP[0], routerContainerName) +} diff --git a/test/scripts/e2e-kind.sh b/test/scripts/e2e-kind.sh index 7112d9ebec..6b426b342d 100755 --- a/test/scripts/e2e-kind.sh +++ b/test/scripts/e2e-kind.sh @@ -44,9 +44,6 @@ service.kubernetes.io/headless # TO BE FIXED BY https://github.com/kubernetes/kubernetes/pull/95351 should resolve connection reset issue #74839 -# TO BE FIXED BY https://github.com/kubernetes/kubernetes/pull/129049 -Services should be able to switch session affinity for NodePort service - # api flakes sig-api-machinery