diff --git a/go.mod b/go.mod index 40e1d0063a19..e190b7e288e3 100644 --- a/go.mod +++ b/go.mod @@ -23,9 +23,10 @@ require ( github.com/google/goexpect v0.0.0-20210430020637-ab937bf7fd6f github.com/google/uuid v1.6.0 github.com/h2non/gock v1.2.0 - github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.3.0 + github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.6.0 github.com/lestrrat/go-jsschema v0.0.0-20181205002244-5c81c58ffcc3 github.com/lithammer/dedent v1.1.0 + github.com/metallb/frr-k8s v0.0.15 github.com/onsi/ginkgo/v2 v2.21.0 github.com/onsi/gomega v1.35.1 github.com/opencontainers/go-digest v1.0.0 @@ -35,6 +36,7 @@ require ( github.com/openshift/client-go v0.0.0-20250131180035-f7ec47e2d87a github.com/openshift/cluster-network-operator v0.0.0-20240708200319-1cd8678b38fb github.com/openshift/library-go v0.0.0-20250129210218-fe56c2cf5d70 + github.com/ovn-org/ovn-kubernetes/go-controller v0.0.0-20250118001652-a8b9c3c31417 github.com/pborman/uuid v1.2.0 github.com/pkg/errors v0.9.1 github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.74.0 @@ -239,7 +241,7 @@ require ( github.com/sergi/go-diff v1.2.0 // indirect github.com/smartystreets/assertions v1.1.0 // indirect github.com/soheilhy/cmux v0.1.5 // indirect - github.com/spf13/afero v1.9.2 // indirect + github.com/spf13/afero v1.9.5 // indirect github.com/spf13/cast v1.5.1 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/src-d/gcfg v1.4.0 // indirect @@ -306,7 +308,7 @@ require ( sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 // indirect sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.0.29 // indirect sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.0.16 // indirect - sigs.k8s.io/controller-runtime v0.18.4 // indirect + sigs.k8s.io/controller-runtime v0.19.0 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 // indirect sigs.k8s.io/kustomize/api v0.18.0 // indirect diff --git a/go.sum b/go.sum index 1be18f27db59..44fe68c62658 100644 --- a/go.sum +++ b/go.sum @@ -480,8 +480,8 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.3.0 h1:MjRRgZyTGo90G+UrwlDQjU+uG4Z7By65qvQxGoILT/8= -github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.3.0/go.mod h1:nqCI7aelBJU61wiBeeZWJ6oi4bJy5nrjkM6lWIMA4j0= +github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.6.0 h1:BT3ghAY0q7lWib9rz+tVXDFkm27dJV6SLCn7TunZwo4= +github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.6.0/go.mod h1:wxt2YWRVItDtaQmVSmaN5ubE2L1c9CiNoHQwSJnM8Ko= github.com/karrick/godirwalk v1.17.0 h1:b4kY7nqDdioR/6qnbHQyDvmA17u5G1cZ6J+CZXwSWoI= github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd h1:Coekwdh0v2wtGp9Gmz1Ze3eVRAWJMLokvN3QjdzCHLY= @@ -530,6 +530,8 @@ github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0 github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 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/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/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= @@ -659,6 +661,8 @@ github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20241205171354-8006f302fd12 h1:AKx/ github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20241205171354-8006f302fd12/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0= +github.com/ovn-org/ovn-kubernetes/go-controller v0.0.0-20250118001652-a8b9c3c31417 h1:7k+dokKFfpICbkpX5TvvpFbKTFsl/6YQd46EpY2JNhc= +github.com/ovn-org/ovn-kubernetes/go-controller v0.0.0-20250118001652-a8b9c3c31417/go.mod h1:9LxDV3rAHlGHAYtVrT62y/fqfIxc5RrDiYi9RVeD0gg= 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= @@ -721,8 +725,8 @@ github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9 github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= +github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= @@ -865,7 +869,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= @@ -1327,8 +1330,8 @@ sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.0.29 h1:qiifAaaBqV3d/EcN9dKJaJI sigs.k8s.io/cloud-provider-azure/pkg/azclient v0.0.29/go.mod h1:ZFAt0qF1kR+w8nBVJK56s6CFvLrlosN1i2c+Sxb7LBk= sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.0.16 h1:Fm/Yjv4nXjUtJ90uXKSKwPwaTWYuDFMhDNNOd77PlOg= sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.0.16/go.mod h1:+kl90flu4+WCP6HBGVYbKVQR+5ztDzUNrWJz8rsnvRU= -sigs.k8s.io/controller-runtime v0.18.4 h1:87+guW1zhvuPLh1PHybKdYFLU0YJp4FhJRmiHvm5BZw= -sigs.k8s.io/controller-runtime v0.18.4/go.mod h1:TVoGrfdpbA9VRFaRnKgk9P5/atA0pMwq+f+msb9M8Sg= +sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q= +sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/kube-storage-version-migrator v0.0.6-0.20230721195810-5c8923c5ff96 h1:PFWFSkpArPNJxFX4ZKWAk9NSeRoZaXschn+ULa4xVek= diff --git a/test/extended/networking/egressip_helpers.go b/test/extended/networking/egressip_helpers.go index e6da27d8c9f5..3e0ccef8939b 100644 --- a/test/extended/networking/egressip_helpers.go +++ b/test/extended/networking/egressip_helpers.go @@ -434,7 +434,7 @@ const ( // The resulting lines will be something like: // 10.128.2.15.36749 /f8f721fa-53c9-444f-bc96-69c7388fcb5a tcpCaptureScript = `#!/bin/bash -tcpdump -nne -i %s -l -s 0 'port %d and tcp[((tcp[12:1] & 0xf0) >> 2):4] = 0x47455420' | awk '{print $10 " " $(NF-1)}' +tcpdump -nn -i %s -l -s 0 -A 'tcp and port %d' | awk '/IP / || /IP6 / {ip=$3} /GET \// {print ip, $2}' ` // The udpCaptureScript runs tcpdump with option -xx and then decodes the hexadecimal information. @@ -636,7 +636,7 @@ func scanPacketSnifferDaemonSetPodLogs(oc *exutil.CLI, ds *appsv1.DaemonSet, tar req := clientset.CoreV1().Pods(pod.Namespace).GetLogs(pod.Name, &logOptions) logs, err := req.Stream(context.TODO()) if err != nil { - return nil, fmt.Errorf("Error in opening log stream") + return nil, fmt.Errorf("Error in opening log stream: %v", err) } defer logs.Close() diff --git a/test/extended/networking/route_advertisements.go b/test/extended/networking/route_advertisements.go new file mode 100644 index 000000000000..c5586eebeb1e --- /dev/null +++ b/test/extended/networking/route_advertisements.go @@ -0,0 +1,763 @@ +package networking + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "net" + "os" + "regexp" + "strings" + "time" + + g "github.com/onsi/ginkgo/v2" + o "github.com/onsi/gomega" + + frrapi "github.com/metallb/frr-k8s/api/v1beta1" + ratypes "github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1" + v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + meta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes" + "k8s.io/kubernetes/test/e2e/framework" + e2epod "k8s.io/kubernetes/test/e2e/framework/pod" + "k8s.io/kubernetes/test/e2e/framework/skipper" + admissionapi "k8s.io/pod-security-admission/api" + + configv1 "github.com/openshift/api/config/v1" + + exutil "github.com/openshift/origin/test/extended/util" +) + +const ( + // for all tests + bgpNamespacePrefix = "bgp" + bgpAgentPodName = "bgp-prober-pod" + targetProtocol = "http" + serverPort = 8000 + + // We run an agnhost container in the dev-scripts host + v4ExternalIP = "172.20.0.100" + v4ExternalCIDR = "172.20.0.0/16" + + v6ExternalIP = "2001:db8:2::100" + v6ExternalCIDR = "2001:db8:2::/64" + + frrNamespace = "openshift-frr-k8s" + raLabel = "k8s.ovn.org/route-advertisements" + + userDefinedNetworkIPv4Subnet = "203.203.0.0/16" + userDefinedNetworkIPv6Subnet = "2014:100:200::0/60" + cudnName = "udn1" +) + +type response struct { + Responses []string `json:"responses"` +} + +var _ = g.Describe("[sig-network][OCPFeatureGate:RouteAdvertisements][Feature:RouteAdvertisements][apigroup:operator.openshift.io]", func() { + oc := exutil.NewCLIWithPodSecurityLevel(bgpNamespacePrefix, admissionapi.LevelPrivileged) + InOVNKubernetesContext(func() { + var ( + networkPlugin string + + f *framework.Framework + clientset kubernetes.Interface + tmpDirBGP string + + workerNodesOrdered []corev1.Node + workerNodesOrderedNames []string + advertisedPodsNodes []string + externalNodeName string + targetNamespace string + snifferNamespace string + cloudType configv1.PlatformType + deployName string + routeName string + packetSnifferDaemonSet *v1.DaemonSet + podList *corev1.PodList + v4PodIPSet map[string]string + v6PodIPSet map[string]string + clusterIPFamily IPFamily + ) + + g.BeforeEach(func() { + g.By("Verifying that this cluster uses a network plugin that is supported for this test") + networkPlugin = networkPluginName() + if networkPlugin != OVNKubernetesPluginName && + networkPlugin != OpenshiftSDNPluginName { + skipper.Skipf("This cluster neither uses OVNKubernetes nor OpenShiftSDN") + } + + g.By("Creating a temp directory") + var err error + tmpDirBGP, err = os.MkdirTemp("", "bgp-e2e") + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("Getting the kubernetes clientset") + f = oc.KubeFramework() + clientset = f.ClientSet + targetNamespace = f.Namespace.Name + + g.By("Determining the cloud infrastructure type") + infra, err := oc.AdminConfigClient().ConfigV1().Infrastructures().Get(context.Background(), "cluster", metav1.GetOptions{}) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("Verifying that the platform is baremetal") + if infra.Spec.PlatformSpec.Type != configv1.BareMetalPlatformType { + skipper.Skipf("This cloud platform (%s) is not supported for this test", cloudType) + } + + // The RouteAdvertisements feature must be enabled by featuregate. + // Otherwise, skip this test. + g.By("Verifying that the RouteAdvertisements feature is enabled by featuregate") + isBGPSupported := false + featureGate, err := oc.AdminConfigClient().ConfigV1().FeatureGates().Get(context.Background(), "cluster", metav1.GetOptions{}) + o.Expect(err).NotTo(o.HaveOccurred()) + for _, feature := range featureGate.Status.FeatureGates[0].Enabled { + if feature.Name == "RouteAdvertisements" { + isBGPSupported = true + break + } + } + if !isBGPSupported { + skipper.Skipf("The RouteAdvertisements feature is not enabled by featuregate") + } + + g.By("Verifying that the RouteAdvertisements is enabled in the cluster") + networkOperator, err := oc.AdminOperatorClient().OperatorV1().Networks().Get(context.Background(), "cluster", metav1.GetOptions{}) + o.Expect(err).NotTo(o.HaveOccurred()) + + if networkOperator.Spec.AdditionalRoutingCapabilities == nil || + networkOperator.Spec.DefaultNetwork.OVNKubernetesConfig == nil || + networkOperator.Spec.DefaultNetwork.OVNKubernetesConfig.RouteAdvertisements != "Enabled" { + skipper.Skipf("The RouteAdvertisements feature is not enabled in the network.operator CR") + } + + g.By("Getting all worker nodes in alphabetical order") + // Get all worker nodes, order them alphabetically with stable + // sort order. + workerNodesOrdered, err = getWorkerNodesOrdered(clientset) + o.Expect(err).NotTo(o.HaveOccurred()) + for _, s := range workerNodesOrdered { + workerNodesOrderedNames = append(workerNodesOrderedNames, s.Name) + } + if len(workerNodesOrdered) < 3 { + skipper.Skipf("This test requires a minimum of 3 worker nodes. However, this environment has %d worker nodes.", len(workerNodesOrdered)) + } + + g.By("Selecting a node to act as as an external host") + o.Expect(len(workerNodesOrderedNames)).Should(o.BeNumerically(">", 1)) + externalNodeName = workerNodesOrderedNames[0] + advertisedPodsNodes = workerNodesOrderedNames[1:] + + g.By("Creating a project for the prober pod") + // Create a target project and assign source and target namespace + // to variables for later use. + snifferNamespace = oc.SetupProject() + + clusterIPFamily = getIPFamilyForCluster(f) + }) + + // Do not check for errors in g.AfterEach as the other cleanup steps will fail, otherwise. + g.AfterEach(func() { + g.By("Removing the temp directory") + os.RemoveAll(tmpDirBGP) + }) + + g.Context("[PodNetwork] Advertising the default network [apigroup:user.openshift.io][apigroup:security.openshift.io]", func() { + g.JustBeforeEach(func() { + g.By("Setup packet sniffer at nodes") + var err error + packetSnifferDaemonSet, err = setupPacketSniffer(oc, clientset, snifferNamespace, advertisedPodsNodes, networkPlugin) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("Ensure the RouteAdvertisements is accepted") + waitForRouteAdvertisements(oc, "default") + + g.By("Makes sure the FRR configuration is generated for each node") + for _, nodeName := range workerNodesOrderedNames { + frr, err := getGeneratedFrrConfigurationForNode(oc, nodeName, "default") + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(frr).NotTo(o.BeNil()) + } + + g.By("Deploy the test pods") + deployName, routeName, podList, err = setupTestDeployment(oc, clientset, targetNamespace, advertisedPodsNodes) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(len(podList.Items)).To(o.Equal(len(advertisedPodsNodes))) + + g.By("Extract test pod IPs") + v4PodIPSet, v6PodIPSet = extractPodIPs(podList) + }) + + g.It("pods should communicate with external host without being SNATed", func() { + g.By("Checking that routes are advertised to each node") + for _, nodeName := range workerNodesOrderedNames { + verifyLearnedBgpRoutesForNode(oc, nodeName, "default") + } + + numberOfRequestsToSend := 10 + g.By(fmt.Sprintf("Sending requests from prober and making sure that %d requests with search string and PodIP %v were seen", numberOfRequestsToSend, v4PodIPSet)) + + if clusterIPFamily == DualStack || clusterIPFamily == IPv4 { + g.By("sending to IPv4 external host") + spawnProberSendEgressIPTrafficCheckLogs(oc, snifferNamespace, probePodName, routeName, targetProtocol, v4ExternalIP, serverPort, numberOfRequestsToSend, numberOfRequestsToSend, packetSnifferDaemonSet, v4PodIPSet) + } + if clusterIPFamily == DualStack || clusterIPFamily == IPv6 { + // [TODO] enable IPv6 test once OCPBUGS-52194 is fixed + // g.By("sending to IPv6 external host") + // spawnProberSendEgressIPTrafficCheckLogs(oc, snifferNamespace, probePodName, routeName, targetProtocol, v6ExternalIP, serverPort, numberOfRequestsToSend, numberOfRequestsToSend, packetSnifferDaemonSet, v6PodIPSet) + } + }) + + g.It("External host should be able to query route advertised pods by the pod IP", func() { + g.By("Launching an agent pod") + nodeSelection := e2epod.NodeSelection{} + e2epod.SetAffinity(&nodeSelection, externalNodeName) + proberPod := createProberPod(oc, snifferNamespace, bgpAgentPodName) + + if clusterIPFamily == DualStack || clusterIPFamily == IPv4 { + g.By("checking the external host to pod traffic works for IPv4") + for podIP := range v4PodIPSet { + checkExternalResponse(oc, proberPod, podIP, v4ExternalIP, serverPort, packetSnifferDaemonSet, targetProtocol) + } + } + + if clusterIPFamily == DualStack || clusterIPFamily == IPv6 { + // [TODO] enable IPv6 test once OCPBUGS-52194 is fixed + // g.By("checking the external host to pod traffic works for IPv6") + // for podIP := range v6PodIPSet { + // checkExternalResponse(oc, proberPod, podIP, v6ExternalIP, serverPort, packetSnifferDaemonSet, targetProtocol) + // } + } + }) + }) + + g.Context("[PodNetwork] Advertising a cluster user defined network [Serial][apigroup:user.openshift.io][apigroup:security.openshift.io]", func() { + g.BeforeEach(func() { + var err error + g.By("Setup packet sniffer at nodes") + packetSnifferDaemonSet, err = setupPacketSniffer(oc, clientset, snifferNamespace, advertisedPodsNodes, networkPlugin) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("Create a namespace with UDPN") + ns, err := f.CreateNamespace(context.TODO(), f.BaseName, map[string]string{ + "e2e-framework": f.BaseName, + RequiredUDNNamespaceLabel: "", + }) + o.Expect(err).NotTo(o.HaveOccurred()) + err = udnWaitForOpenShift(oc, ns.Name) + o.Expect(err).NotTo(o.HaveOccurred()) + targetNamespace = ns.Name + f.Namespace = ns + + g.By("Creating a cluster user defined network") + nc := &networkAttachmentConfigParams{ + name: cudnName, + topology: "layer3", + role: "primary", + namespace: targetNamespace, + } + nc.cidr = correctCIDRFamily(oc, userDefinedNetworkIPv4Subnet, userDefinedNetworkIPv6Subnet) + cudnManifest := generateClusterUserDefinedNetworkManifest(nc) + cleanup, err := createManifest(targetNamespace, cudnManifest) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Eventually(clusterUserDefinedNetworkReadyFunc(oc.AdminDynamicClient(), cudnName), 60*time.Second, time.Second).Should(o.Succeed()) + + g.By("Labeling the UDN for advertisement") + _, err = runOcWithRetry(oc.AsAdmin(), "label", "clusteruserdefinednetworks", "-n", targetNamespace, cudnName, "advertise=true") + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("Create the route advertisement for UDN") + raManifest := newRouteAdvertisementsManifest(cudnName, true, false) + err = applyManifest(targetNamespace, raManifest) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By("Ensure the RouteAdvertisements is accepted") + waitForRouteAdvertisements(oc, cudnName) + + g.By("Makes sure the FRR configuration is generated for each node") + for _, nodeName := range workerNodesOrderedNames { + frr, err := getGeneratedFrrConfigurationForNode(oc, nodeName, cudnName) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(frr).NotTo(o.BeNil()) + } + + g.By("Deploy the test pods") + deployName, routeName, podList, err = setupTestDeployment(oc, clientset, targetNamespace, advertisedPodsNodes) + o.Expect(err).NotTo(o.HaveOccurred()) + o.Expect(len(podList.Items)).To(o.Equal(len(advertisedPodsNodes))) + + g.By("Extract test pod UDN IPs") + v4PodIPSet, v6PodIPSet = extractPodUdnIPs(podList, nc, targetNamespace, clientset) + + g.DeferCleanup(func() { + runOcWithRetry(oc.AsAdmin(), "delete", "deploy", deployName) + runOcWithRetry(oc.AsAdmin(), "delete", "pod", "--all") + runOcWithRetry(oc.AsAdmin(), "delete", "ra", cudnName) + runOcWithRetry(oc.AsAdmin(), "delete", "clusteruserdefinednetwork", cudnName) + cleanup() + }) + }) + + g.AfterEach(func() { + }) + + g.It("pods should communicate with external host without being SNATed", func() { + g.By("Checking that BGP routes to the PodNetwork are learned by other nodes") + g.By("Checking that routes are advertised to each node") + for _, nodeName := range workerNodesOrderedNames { + verifyLearnedBgpRoutesForNode(oc, nodeName, cudnName) + } + + numberOfRequestsToSend := 10 + g.By(fmt.Sprintf("Sending requests from prober and making sure that %d requests with search string and PodIP %v were seen", numberOfRequestsToSend, v4PodIPSet)) + + svcUrl := fmt.Sprintf("%s-0-service:%d", targetNamespace, serverPort) + if clusterIPFamily == DualStack || clusterIPFamily == IPv4 { + g.By("sending to IPv4 external host") + spawnProberSendEgressIPTrafficCheckLogs(oc, targetNamespace, probePodName, svcUrl, targetProtocol, v4ExternalIP, serverPort, numberOfRequestsToSend, numberOfRequestsToSend, packetSnifferDaemonSet, v4PodIPSet) + } + if clusterIPFamily == DualStack || clusterIPFamily == IPv6 { + g.By("sending to IPv6 external host") + spawnProberSendEgressIPTrafficCheckLogs(oc, targetNamespace, probePodName, svcUrl, targetProtocol, v6ExternalIP, serverPort, numberOfRequestsToSend, numberOfRequestsToSend, packetSnifferDaemonSet, v6PodIPSet) + } + }) + + g.It("External host should be able to query route advertised pods by the pod IP", func() { + g.By("Launching an agent pod") + nodeSelection := e2epod.NodeSelection{} + e2epod.SetAffinity(&nodeSelection, externalNodeName) + proberPod := createProberPod(oc, targetNamespace, bgpAgentPodName) + + if clusterIPFamily == DualStack || clusterIPFamily == IPv4 { + g.By("checking the external host to pod traffic works for IPv4") + for podIP := range v4PodIPSet { + checkExternalResponse(oc, proberPod, podIP, v4ExternalIP, serverPort, packetSnifferDaemonSet, targetProtocol) + } + } + + if clusterIPFamily == DualStack || clusterIPFamily == IPv6 { + g.By("checking the external host to pod traffic works for IPv6") + for podIP := range v6PodIPSet { + checkExternalResponse(oc, proberPod, podIP, v6ExternalIP, serverPort, packetSnifferDaemonSet, targetProtocol) + } + } + }) + }) + }) +}) + +// getRouteAdvertisements uses the dynamic admin client to return a pointer to +// an existing RouteAdvertisements, or error. +func getRouteAdvertisements(oc *exutil.CLI, name string) (*ratypes.RouteAdvertisements, error) { + dynamic := oc.AdminDynamicClient() + unstructured, err := dynamic.Resource(ratypes.SchemeGroupVersion.WithResource("routeadvertisements")).Namespace("").Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + return nil, err + } + ra := &ratypes.RouteAdvertisements{} + err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructured.UnstructuredContent(), ra) + if err != nil { + return nil, err + } + return ra, nil +} + +// getGeneratedFrrConfigurationForNode returns the FRR configuration for the node +func getGeneratedFrrConfigurationForNode(oc *exutil.CLI, nodeName, raName string) (*frrapi.FRRConfiguration, error) { + dynamic := oc.AdminDynamicClient() + unstructuredList, err := dynamic.Resource(frrapi.SchemeGroupVersion.WithResource("frrconfigurations")).Namespace("").List(context.TODO(), metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", raLabel, raName), + }) + if err != nil { + return nil, err + } + frrList := &frrapi.FRRConfigurationList{} + err = runtime.DefaultUnstructuredConverter.FromUnstructured(unstructuredList.UnstructuredContent(), frrList) + if err != nil { + return nil, err + } + for _, frr := range frrList.Items { + if frr.Spec.NodeSelector.MatchLabels["kubernetes.io/hostname"] == nodeName { + return &frr, nil + } + } + return nil, fmt.Errorf("FRR configuration for node %s not found", nodeName) +} + +func getNodeSubnets(oc *exutil.CLI, network string) (map[string][]net.IPNet, error) { + // Run the oc command to get node subnets + out, err := runOcWithRetry(oc.AsAdmin(), "get", "nodes", "-o", + `jsonpath={range .items[*]}{.metadata.name}{"\t"}{.metadata.annotations.k8s\.ovn\.org/node-subnets}{"\n"}{end}`) + if err != nil { + return nil, fmt.Errorf("failed to get node subnets: %v", err) + } + + // Create map to store results + nodeSubnets := make(map[string][]net.IPNet) + + // Parse each line + scanner := bufio.NewScanner(strings.NewReader(out)) + for scanner.Scan() { + line := scanner.Text() + if line == "" { + continue + } + + // Split line into node name and subnet JSON + parts := strings.SplitN(line, "\t", 2) + if len(parts) != 2 { + continue + } + nodeName := parts[0] + subnetJSON := parts[1] + + // Parse the JSON subnet data + var subnetMap map[string][]string + if err := json.Unmarshal([]byte(subnetJSON), &subnetMap); err != nil { + return nil, fmt.Errorf("failed to parse subnet JSON for node %s: %v", nodeName, err) + } + + // Extract subnets for the specified network + if subnets, ok := subnetMap[network]; ok { + ipNets := make([]net.IPNet, 0, len(subnets)) + for _, subnet := range subnets { + _, ipNet, err := net.ParseCIDR(subnet) + if err != nil { + return nil, fmt.Errorf("failed to parse CIDR %q for node %s: %v", subnet, nodeName, err) + } + ipNets = append(ipNets, *ipNet) + } + nodeSubnets[nodeName] = ipNets + } + } + + if err := scanner.Err(); err != nil { + return nil, fmt.Errorf("error scanning output: %v", err) + } + + return nodeSubnets, nil +} + +// getLearnedBgpRoutesByNode returns the BGP routes learned by the node +func getLearnedBgpRoutesByNode(oc *exutil.CLI, nodeName string) (map[string]string, map[string]string, error) { + var podName string + var out string + var err error + var v4bgpRoutes, v6bgpRoutes map[string]string + + out, err = runOcWithRetry(oc.AsAdmin(), "get", + "pods", + "-o", "name", + "-n", frrNamespace, + "--field-selector", fmt.Sprintf("spec.nodeName=%s", nodeName), + "-l", "app=frr-k8s") + if err != nil { + return nil, nil, err + } + outReader := bufio.NewScanner(strings.NewReader(out)) + re := regexp.MustCompile("^pod/(.*)") + for outReader.Scan() { + match := re.FindSubmatch([]byte(outReader.Text())) + if len(match) != 2 { + continue + } + podName = string(match[1]) + break + } + if podName == "" { + return nil, nil, fmt.Errorf("could not find valid frr pod on node %q", nodeName) + } + out, err = adminExecInPod(oc, frrNamespace, podName, "frr", "ip route show proto bgp") + if err != nil { + return nil, nil, err + } + v4bgpRoutes = parseRoutes(out) + + out, err = adminExecInPod(oc, frrNamespace, podName, "frr", "ip -6 route show proto bgp") + if err != nil { + return nil, nil, err + } + v6bgpRoutes = parseRoutes(out) + + return v4bgpRoutes, v6bgpRoutes, nil +} + +func parseRoutes(routeOutput string) map[string]string { + routes := make(map[string]string) + scanner := bufio.NewScanner(strings.NewReader(routeOutput)) + + for scanner.Scan() { + line := scanner.Text() + // Extract CIDR and via address using regex for both IPv4 and IPv6 + re := regexp.MustCompile(`([\d\.]+/\d+|[a-fA-F0-9:]+/\d+).*via\s+([a-fA-F0-9:.]+)`) + matches := re.FindStringSubmatch(line) + if len(matches) == 3 { + cidr := matches[1] // e.g., "10.128.0.0/23" or "fd01:0:0:1::/64" + via := matches[2] // e.g., "192.168.111.22" or "fd2e:6f44:5dd8:c956::16" + + routes[cidr] = via + } + } + return routes +} + +func newRouteAdvertisementsManifest(name string, podNetwork, egressip bool) string { + advertisements := []string{} + if podNetwork { + advertisements = append(advertisements, "PodNetwork") + } + if egressip { + advertisements = append(advertisements, "EgressIP") + } + return fmt.Sprintf(` +apiVersion: k8s.ovn.org/v1 +kind: RouteAdvertisements +metadata: + name: %s +spec: + advertisements: [%s] + networkSelector: + matchLabels: + advertise: "true" +`, name, strings.Join(advertisements, ",")) +} + +// verifyLearnedBgpRoutesForNode encapsulates the verification of learned BGP routes for a node. +func verifyLearnedBgpRoutesForNode(oc *exutil.CLI, nodeName string, network string) { + var lastErr error + nodeSubnets, err := getNodeSubnets(oc, network) + o.Expect(err).NotTo(o.HaveOccurred()) + + g.By(fmt.Sprintf("Checking routes for node %s in network %s", nodeName, network)) + o.Eventually(func() bool { + bgpV4Routes, bgpV6Routes, err := getLearnedBgpRoutesByNode(oc, nodeName) + if err != nil { + lastErr = fmt.Errorf("failed to get BGP routes: %v", err) + return false + } + + if !verifyExternalRoutes(bgpV4Routes, bgpV6Routes) { + lastErr = fmt.Errorf("missing external routes") + return false + } + + if !verifyNodeSubnetRoutes(nodeName, nodeSubnets, bgpV4Routes, bgpV6Routes) { + lastErr = fmt.Errorf("missing node subnet routes") + return false + } + + return true + }, timeOut, interval).Should(o.BeTrue(), func() string { + return fmt.Sprintf("Route verification failed for node %s: %v", nodeName, lastErr) + }) +} + +func verifyExternalRoutes(v4Routes, v6Routes map[string]string) bool { + if _, ok := v4Routes[v4ExternalCIDR]; !ok { + framework.Logf("Missing v4 external route %s", v4ExternalCIDR) + return false + } + if _, ok := v6Routes[v6ExternalCIDR]; !ok { + framework.Logf("Missing v6 external route %s", v6ExternalCIDR) + return false + } + return true +} + +func verifyNodeSubnetRoutes(nodeName string, nodeSubnets map[string][]net.IPNet, v4Routes, v6Routes map[string]string) bool { + for node, subnets := range nodeSubnets { + if node == nodeName { + continue + } + for _, subnet := range subnets { + if subnet.IP.To4() != nil { + if _, ok := v4Routes[subnet.String()]; !ok { + framework.Logf("Missing v4 route for node %s subnet %s", node, subnet.String()) + return false + } + } else { + if _, ok := v6Routes[subnet.String()]; !ok { + framework.Logf("Missing v6 route for node %s subnet %s", node, subnet.String()) + // [TODO] enable IPv6 test once OCPBUGS-52194 is fixed + // return false + } + } + } + } + return true +} + +func checkExternalResponse(oc *exutil.CLI, proberPod *corev1.Pod, podIP, ExternalIP string, serverPort int, packetSnifferDaemonSet *v1.DaemonSet, targetProtocol string) { + g.By("Sending request from the external host to the PodIPs") + request := fmt.Sprintf("dial?protocol=http&host=%s&port=%d&request=clientip", podIP, serverPort) + // Determine URL format based on whether ExternalIP is IPv4 or IPv6. + ip := net.ParseIP(ExternalIP) + var url string + if ip != nil && ip.To4() != nil { + url = fmt.Sprintf("%s:%d/%s", ExternalIP, serverPort, request) + } else { + url = fmt.Sprintf("[%s]:%d/%s", ExternalIP, serverPort, request) + } + + g.By("Making sure that external host IP presents in the sniffer packet log") + var lastErr error + o.Eventually(func() bool { + output, err := runOcWithRetry(oc.AsAdmin(), "exec", proberPod.Name, "--", "curl", "-s", url) + if err != nil { + lastErr = fmt.Errorf("failed to execute curl command: %v", err) + return false + } + framework.Logf("output: %s prober IP: %s", output, ExternalIP) + + g.By("Making sure that external host IP presents in the response") + var resp response + if err := json.Unmarshal([]byte(output), &resp); err != nil { + lastErr = fmt.Errorf("failed to unmarshal response: %v", err) + return false + } + + if len(resp.Responses) == 0 { + lastErr = fmt.Errorf("no responses received") + return false + } + + if !strings.Contains(resp.Responses[0], ExternalIP) { + lastErr = fmt.Errorf("response does not contain external IP %s", ExternalIP) + return false + } + + found, err := scanPacketSnifferDaemonSetPodLogs(oc, packetSnifferDaemonSet, targetProtocol, ExternalIP) + if err != nil { + lastErr = fmt.Errorf("failed to scan packet sniffer logs: %v", err) + return false + } + + if len(found) == 0 { + lastErr = fmt.Errorf("no matching packets found in sniffer logs") + return false + } + + return true + }, timeOut, interval).Should(o.BeTrue(), func() string { + return fmt.Sprintf("Failed to verify external response: %v", lastErr) + }) +} + +// Add these helper functions after the imports + +func setupPacketSniffer(oc *exutil.CLI, clientset kubernetes.Interface, snifferNamespace string, advertisedPodsNodes []string, networkPlugin string) (*v1.DaemonSet, error) { + // Add SCC privileged + _, err := runOcWithRetry(oc.AsAdmin(), "adm", "policy", "add-scc-to-user", "privileged", + fmt.Sprintf("system:serviceaccount:%s:default", snifferNamespace)) + if err != nil { + return nil, err + } + + // Find interface for packet sniffing + packetSnifferInterface, err := findPacketSnifferInterface(oc, networkPlugin, advertisedPodsNodes) + if err != nil { + return nil, err + } + framework.Logf("Using interface %s for packet captures", packetSnifferInterface) + + // Create packet sniffer daemonset + packetSnifferDaemonSet, err := createPacketSnifferDaemonSet(oc, snifferNamespace, advertisedPodsNodes, targetProtocol, serverPort, packetSnifferInterface) + if err != nil { + return nil, err + } + + return packetSnifferDaemonSet, nil +} + +func waitForRouteAdvertisements(oc *exutil.CLI, name string) { + o.Eventually(func() bool { + ra, err := getRouteAdvertisements(oc, name) + if err != nil { + return false + } + condition := meta.FindStatusCondition(ra.Status.Conditions, "Accepted") + if condition == nil { + return false + } + return condition.Status == metav1.ConditionTrue + }, 3*timeOut, interval).Should(o.BeTrue()) +} + +func setupTestDeployment(oc *exutil.CLI, clientset kubernetes.Interface, targetNamespace string, advertisedPodsNodes []string) (string, string, *corev1.PodList, error) { + ingressDomain, err := getIngressDomain(oc) + if err != nil { + return "", "", nil, err + } + + deployName, routeName, err := createAgnhostDeploymentAndIngressRoute(oc, targetNamespace, "", + ingressDomain, len(advertisedPodsNodes), advertisedPodsNodes) + if err != nil { + return "", "", nil, err + } + + podList, err := clientset.CoreV1().Pods(targetNamespace).List(context.TODO(), + metav1.ListOptions{LabelSelector: fmt.Sprintf("app=%s", deployName)}) + if err != nil { + return "", "", nil, err + } + + return deployName, routeName, podList, nil +} + +func extractPodIPs(podList *corev1.PodList) (map[string]string, map[string]string) { + v4PodIPSet := make(map[string]string) + v6PodIPSet := make(map[string]string) + + for _, pod := range podList.Items { + for _, ip := range pod.Status.PodIPs { + IP := net.ParseIP(ip.IP) + if IP == nil { + continue + } + if IP.To4() != nil { + v4PodIPSet[ip.IP] = pod.Spec.NodeName + } else { + v6PodIPSet[ip.IP] = pod.Spec.NodeName + } + } + } + return v4PodIPSet, v6PodIPSet +} + +func extractPodUdnIPs(podList *corev1.PodList, nc *networkAttachmentConfigParams, namespace string, clientset kubernetes.Interface) (map[string]string, map[string]string) { + var err error + v4PodIPSet := make(map[string]string) + v6PodIPSet := make(map[string]string) + g.By("Extract test pod UDN IPs") + var udnIP string + for _, pod := range podList.Items { + for i, cidr := range strings.Split(nc.cidr, ",") { + if cidr != "" { + g.By("asserting the pod has an UDN IP from the configured range") + udnIP, err = podIPsForUserDefinedPrimaryNetwork( + clientset, + namespace, + pod.Name, + namespacedName(namespace, nc.name), + i, + ) + o.Expect(err).NotTo(o.HaveOccurred()) + + ip := net.ParseIP(udnIP) + o.Expect(ip).NotTo(o.BeNil()) + if ip.To4() != nil { + v4PodIPSet[udnIP] = pod.Spec.NodeName + } else { + v6PodIPSet[udnIP] = pod.Spec.NodeName + } + } + } + } + return v4PodIPSet, v6PodIPSet +} diff --git a/test/extended/util/annotate/generated/zz_generated.annotations.go b/test/extended/util/annotate/generated/zz_generated.annotations.go index f7ea3d5cc7ba..bf5d0f83fd45 100644 --- a/test/extended/util/annotate/generated/zz_generated.annotations.go +++ b/test/extended/util/annotate/generated/zz_generated.annotations.go @@ -1725,6 +1725,14 @@ var Annotations = map[string]string{ "[sig-network][OCPFeatureGate:PersistentIPsForVirtualization][Feature:Layer2LiveMigration] Kubevirt Virtual Machines when using openshift ovn-kubernetes with user defined networks and persistent ips configured created using [OCPFeatureGate:NetworkSegmentation] UserDefinedNetwork [Suite:openshift/network/virtualization] should keep ip when the VMI attached to a secondary UDN is migrated between nodes": "", + "[sig-network][OCPFeatureGate:RouteAdvertisements][Feature:RouteAdvertisements][apigroup:operator.openshift.io] when using openshift ovn-kubernetes [PodNetwork] Advertising a cluster user defined network [Serial][apigroup:user.openshift.io][apigroup:security.openshift.io] External host should be able to query route advertised pods by the pod IP": " [Suite:openshift/conformance/serial]", + + "[sig-network][OCPFeatureGate:RouteAdvertisements][Feature:RouteAdvertisements][apigroup:operator.openshift.io] when using openshift ovn-kubernetes [PodNetwork] Advertising a cluster user defined network [Serial][apigroup:user.openshift.io][apigroup:security.openshift.io] pods should communicate with external host without being SNATed": " [Suite:openshift/conformance/serial]", + + "[sig-network][OCPFeatureGate:RouteAdvertisements][Feature:RouteAdvertisements][apigroup:operator.openshift.io] when using openshift ovn-kubernetes [PodNetwork] Advertising the default network [apigroup:user.openshift.io][apigroup:security.openshift.io] External host should be able to query route advertised pods by the pod IP": " [Suite:openshift/conformance/parallel]", + + "[sig-network][OCPFeatureGate:RouteAdvertisements][Feature:RouteAdvertisements][apigroup:operator.openshift.io] when using openshift ovn-kubernetes [PodNetwork] Advertising the default network [apigroup:user.openshift.io][apigroup:security.openshift.io] pods should communicate with external host without being SNATed": " [Suite:openshift/conformance/parallel]", + "[sig-network][OCPFeatureGate:RouteExternalCertificate][Feature:Router][apigroup:route.openshift.io] with invalid setup the router should not support external certificate if inline certificate is also present": " [Suite:openshift/conformance/parallel]", "[sig-network][OCPFeatureGate:RouteExternalCertificate][Feature:Router][apigroup:route.openshift.io] with invalid setup the router should not support external certificate if the route termination type is Passthrough": " [Suite:openshift/conformance/parallel]", diff --git a/vendor/github.com/k8snetworkplumbingwg/network-attachment-definition-client/NOTICE b/vendor/github.com/k8snetworkplumbingwg/network-attachment-definition-client/NOTICE new file mode 100644 index 000000000000..3e2901b3a69d --- /dev/null +++ b/vendor/github.com/k8snetworkplumbingwg/network-attachment-definition-client/NOTICE @@ -0,0 +1 @@ +Copyright 2018 Kubernetes Network Plumbing Working Group diff --git a/vendor/github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1/types.go b/vendor/github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1/types.go index 31ada4928235..7e202ed8d052 100644 --- a/vendor/github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1/types.go +++ b/vendor/github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1/types.go @@ -1,6 +1,8 @@ package v1 import ( + "encoding/json" + "errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "net" ) @@ -43,7 +45,7 @@ const ( DeviceInfoTypeVHostUser = "vhost-user" DeviceInfoTypeMemif = "memif" DeviceInfoTypeVDPA = "vdpa" - DeviceInfoVersion = "1.0.0" + DeviceInfoVersion = "1.1.0" ) // DeviceInfo contains the information of the device associated @@ -58,18 +60,20 @@ type DeviceInfo struct { } type PciDevice struct { - PciAddress string `json:"pci-address,omitempty"` - Vhostnet string `json:"vhost-net,omitempty"` - RdmaDevice string `json:"rdma-device,omitempty"` - PfPciAddress string `json:"pf-pci-address,omitempty"` + PciAddress string `json:"pci-address,omitempty"` + Vhostnet string `json:"vhost-net,omitempty"` + RdmaDevice string `json:"rdma-device,omitempty"` + PfPciAddress string `json:"pf-pci-address,omitempty"` + RepresentorDevice string `json:"representor-device,omitempty"` } type VdpaDevice struct { - ParentDevice string `json:"parent-device,omitempty"` - Driver string `json:"driver,omitempty"` - Path string `json:"path,omitempty"` - PciAddress string `json:"pci-address,omitempty"` - PfPciAddress string `json:"pf-pci-address,omitempty"` + ParentDevice string `json:"parent-device,omitempty"` + Driver string `json:"driver,omitempty"` + Path string `json:"path,omitempty"` + PciAddress string `json:"pci-address,omitempty"` + PfPciAddress string `json:"pf-pci-address,omitempty"` + RepresentorDevice string `json:"representor-device,omitempty"` } const ( @@ -106,6 +110,7 @@ type NetworkStatus struct { Default bool `json:"default,omitempty"` DNS DNS `json:"dns,omitempty"` DeviceInfo *DeviceInfo `json:"device-info,omitempty"` + Gateway []string `json:"gateway,omitempty"` } // PortMapEntry for CNI PortMapEntry @@ -159,6 +164,23 @@ type NetworkSelectionElement struct { CNIArgs *map[string]interface{} `json:"cni-args,omitempty"` // GatewayRequest contains default route IP address for the pod GatewayRequest []net.IP `json:"default-route,omitempty"` + // IPAMClaimReference container the IPAMClaim name where the IPs for this + // attachment will be located. + IPAMClaimReference string `json:"ipam-claim-reference,omitempty"` +} + +func (nse *NetworkSelectionElement) UnmarshalJSON(b []byte) error { + type networkSelectionElement NetworkSelectionElement + + var netSelectionElement networkSelectionElement + if err := json.Unmarshal(b, &netSelectionElement); err != nil { + return err + } + if len(netSelectionElement.IPRequest) > 0 && netSelectionElement.IPAMClaimReference != "" { + return TooManyIPSources + } + *nse = NetworkSelectionElement(netSelectionElement) + return nil } const ( @@ -175,3 +197,5 @@ type NoK8sNetworkError struct { } func (e *NoK8sNetworkError) Error() string { return string(e.Message) } + +var TooManyIPSources = errors.New("cannot provide a static IP and a reference of an IPAM claim in the same network selection element") diff --git a/vendor/github.com/metallb/frr-k8s/LICENSE b/vendor/github.com/metallb/frr-k8s/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/vendor/github.com/metallb/frr-k8s/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/vendor/github.com/metallb/frr-k8s/api/v1beta1/frr_node_state_types.go b/vendor/github.com/metallb/frr-k8s/api/v1beta1/frr_node_state_types.go new file mode 100644 index 000000000000..61e5dd2b6531 --- /dev/null +++ b/vendor/github.com/metallb/frr-k8s/api/v1beta1/frr_node_state_types.go @@ -0,0 +1,61 @@ +/* +Copyright 2023. + +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 v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// FRRNodeStateSpec defines the desired state of FRRNodeState. +type FRRNodeStateSpec struct { +} + +// FRRNodeStateStatus defines the observed state of FRRNodeState. +type FRRNodeStateStatus struct { + // RunningConfig represents the current FRR running config, which is the configuration the FRR instance is currently running with. + RunningConfig string `json:"runningConfig,omitempty"` + // LastConversionResult is the status of the last translation between the `FRRConfiguration`s resources and FRR's configuration, contains "success" or an error. + LastConversionResult string `json:"lastConversionResult,omitempty"` + // LastReloadResult represents the status of the last configuration update operation by FRR, contains "success" or an error. + LastReloadResult string `json:"lastReloadResult,omitempty"` +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//+kubebuilder:resource:scope=Cluster + +// FRRNodeState exposes the status of the FRR instance running on each node. +type FRRNodeState struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec FRRNodeStateSpec `json:"spec,omitempty"` + Status FRRNodeStateStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// FRRNodeStateList contains a list of FRRNodeStatus. +type FRRNodeStateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []FRRNodeState `json:"items"` +} + +func init() { + SchemeBuilder.Register(&FRRNodeState{}, &FRRNodeStateList{}) +} diff --git a/vendor/github.com/metallb/frr-k8s/api/v1beta1/frrconfiguration_types.go b/vendor/github.com/metallb/frr-k8s/api/v1beta1/frrconfiguration_types.go new file mode 100644 index 000000000000..421f38a1e936 --- /dev/null +++ b/vendor/github.com/metallb/frr-k8s/api/v1beta1/frrconfiguration_types.go @@ -0,0 +1,391 @@ +/* +Copyright 2023. + +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 v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// FRRConfigurationSpec defines the desired state of FRRConfiguration. +type FRRConfigurationSpec struct { + // BGP is the configuration related to the BGP protocol. + // +optional + BGP BGPConfig `json:"bgp,omitempty"` + + // Raw is a snippet of raw frr configuration that gets appended to the + // one rendered translating the type safe API. + // +optional + Raw RawConfig `json:"raw,omitempty"` + // NodeSelector limits the nodes that will attempt to apply this config. + // When specified, the configuration will be considered only on nodes + // whose labels match the specified selectors. + // When it is not specified all nodes will attempt to apply this config. + // +optional + NodeSelector metav1.LabelSelector `json:"nodeSelector,omitempty"` +} + +// RawConfig is a snippet of raw frr configuration that gets appended to the +// rendered configuration. +type RawConfig struct { + // Priority is the order with this configuration is appended to the + // bottom of the rendered configuration. A higher value means the + // raw config is appended later in the configuration file. + Priority int `json:"priority,omitempty"` + + // Config is a raw FRR configuration to be appended to the configuration + // rendered via the k8s api. + Config string `json:"rawConfig,omitempty"` +} + +// BGPConfig is the configuration related to the BGP protocol. +type BGPConfig struct { + // Routers is the list of routers we want FRR to configure (one per VRF). + // +optional + Routers []Router `json:"routers"` + // BFDProfiles is the list of bfd profiles to be used when configuring the neighbors. + // +optional + BFDProfiles []BFDProfile `json:"bfdProfiles,omitempty"` +} + +// Router represent a neighbor router we want FRR to connect to. +type Router struct { + // ASN is the AS number to use for the local end of the session. + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=4294967295 + ASN uint32 `json:"asn"` + // ID is the BGP router ID + // +optional + ID string `json:"id,omitempty"` + // VRF is the host vrf used to establish sessions from this router. + // +optional + VRF string `json:"vrf,omitempty"` + // Neighbors is the list of neighbors we want to establish BGP sessions with. + // +optional + Neighbors []Neighbor `json:"neighbors,omitempty"` + // Prefixes is the list of prefixes we want to advertise from this router instance. + // +optional + Prefixes []string `json:"prefixes,omitempty"` + + // Imports is the list of imported VRFs we want for this router / vrf. + // +optional + Imports []Import `json:"imports,omitempty"` +} + +// Import represents the possible imported VRFs to a given router. +type Import struct { + // Vrf is the vrf we want to import from + // +optional + VRF string `json:"vrf,omitempty"` +} + +// Neighbor represents a BGP Neighbor we want FRR to connect to. +type Neighbor struct { + // ASN is the AS number to use for the local end of the session. + // ASN and DynamicASN are mutually exclusive and one of them must be specified. + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=4294967295 + // +optional + ASN uint32 `json:"asn,omitempty"` + + // DynamicASN detects the AS number to use for the local end of the session + // without explicitly setting it via the ASN field. Limited to: + // internal - if the neighbor's ASN is different than the router's the connection is denied. + // external - if the neighbor's ASN is the same as the router's the connection is denied. + // ASN and DynamicASN are mutually exclusive and one of them must be specified. + // +kubebuilder:validation:Enum=internal;external + // +optional + DynamicASN DynamicASNMode `json:"dynamicASN,omitempty"` + + // SourceAddress is the IPv4 or IPv6 source address to use for the BGP + // session to this neighbour, may be specified as either an IP address + // directly or as an interface name + // +optional + SourceAddress string `json:"sourceaddress,omitempty"` + + // Address is the IP address to establish the session with. + Address string `json:"address"` + + // Port is the port to dial when establishing the session. + // Defaults to 179. + // +optional + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=16384 + Port *uint16 `json:"port,omitempty"` + + // Password to be used for establishing the BGP session. + // Password and PasswordSecret are mutually exclusive. + // +optional + Password string `json:"password,omitempty"` + + // PasswordSecret is name of the authentication secret for the neighbor. + // the secret must be of type "kubernetes.io/basic-auth", and created in the + // same namespace as the frr-k8s daemon. The password is stored in the + // secret as the key "password". + // Password and PasswordSecret are mutually exclusive. + // +optional + PasswordSecret SecretReference `json:"passwordSecret,omitempty"` + + // HoldTime is the requested BGP hold time, per RFC4271. + // Defaults to 180s. + // +optional + HoldTime *metav1.Duration `json:"holdTime,omitempty"` + + // KeepaliveTime is the requested BGP keepalive time, per RFC4271. + // Defaults to 60s. + // +optional + KeepaliveTime *metav1.Duration `json:"keepaliveTime,omitempty"` + + // Requested BGP connect time, controls how long BGP waits between connection attempts to a neighbor. + // +kubebuilder:validation:XValidation:message="connect time should be between 1 seconds to 65535",rule="duration(self).getSeconds() >= 1 && duration(self).getSeconds() <= 65535" + // +kubebuilder:validation:XValidation:message="connect time should contain a whole number of seconds",rule="duration(self).getMilliseconds() % 1000 == 0" + // +optional + ConnectTime *metav1.Duration `json:"connectTime,omitempty"` + + // EBGPMultiHop indicates if the BGPPeer is multi-hops away. + // +optional + EBGPMultiHop bool `json:"ebgpMultiHop,omitempty"` + + // BFDProfile is the name of the BFD Profile to be used for the BFD session associated + // to the BGP session. If not set, the BFD session won't be set up. + // +optional + BFDProfile string `json:"bfdProfile,omitempty"` + + // EnableGracefulRestart allows BGP peer to continue to forward data packets along + // known routes while the routing protocol information is being restored. If + // the session is already established, the configuration will have effect + // after reconnecting to the peer + // +optional + EnableGracefulRestart bool `json:"enableGracefulRestart,omitempty"` + + // ToAdvertise represents the list of prefixes to advertise to the given neighbor + // and the associated properties. + // +optional + ToAdvertise Advertise `json:"toAdvertise,omitempty"` + + // ToReceive represents the list of prefixes to receive from the given neighbor. + // +optional + ToReceive Receive `json:"toReceive,omitempty"` + + // To set if we want to disable MP BGP that will separate IPv4 and IPv6 route exchanges into distinct BGP sessions. + // +optional + // +kubebuilder:default:=false + DisableMP bool `json:"disableMP,omitempty"` +} + +// Advertise represents a list of prefixes to advertise to the given neighbor. + +type Advertise struct { + + // Allowed is is the list of prefixes allowed to be propagated to + // this neighbor. They must match the prefixes defined in the router. + Allowed AllowedOutPrefixes `json:"allowed,omitempty"` + + // PrefixesWithLocalPref is a list of prefixes that are associated to a local + // preference when being advertised. The prefixes associated to a given local pref + // must be in the prefixes allowed to be advertised. + // +optional + PrefixesWithLocalPref []LocalPrefPrefixes `json:"withLocalPref,omitempty"` + + // PrefixesWithCommunity is a list of prefixes that are associated to a + // bgp community when being advertised. The prefixes associated to a given local pref + // must be in the prefixes allowed to be advertised. + // +optional + PrefixesWithCommunity []CommunityPrefixes `json:"withCommunity,omitempty"` +} + +// Receive represents a list of prefixes to receive from the given neighbor. +type Receive struct { + // Allowed is the list of prefixes allowed to be received from + // this neighbor. + // +optional + Allowed AllowedInPrefixes `json:"allowed,omitempty"` +} + +// PrefixSelector is a filter of prefixes to receive. +type PrefixSelector struct { + // +kubebuilder:validation:Format="cidr" + Prefix string `json:"prefix,omitempty"` + // The prefix length modifier. This selector accepts any matching prefix with length + // less or equal the given value. + // +kubebuilder:validation:Maximum:=128 + // +kubebuilder:validation:Minimum:=1 + LE uint32 `json:"le,omitempty"` + // The prefix length modifier. This selector accepts any matching prefix with length + // greater or equal the given value. + // +kubebuilder:validation:Maximum:=128 + // +kubebuilder:validation:Minimum:=1 + GE uint32 `json:"ge,omitempty"` +} + +type AllowedInPrefixes struct { + Prefixes []PrefixSelector `json:"prefixes,omitempty"` + // Mode is the mode to use when handling the prefixes. + // When set to "filtered", only the prefixes in the given list will be allowed. + // When set to "all", all the prefixes configured on the router will be allowed. + // +kubebuilder:default:=filtered + Mode AllowMode `json:"mode,omitempty"` +} + +type AllowedOutPrefixes struct { + Prefixes []string `json:"prefixes,omitempty"` + // Mode is the mode to use when handling the prefixes. + // When set to "filtered", only the prefixes in the given list will be allowed. + // When set to "all", all the prefixes configured on the router will be allowed. + // +kubebuilder:default:=filtered + Mode AllowMode `json:"mode,omitempty"` +} + +// LocalPrefPrefixes is a list of prefixes associated to a local preference. +type LocalPrefPrefixes struct { + // Prefixes is the list of prefixes associated to the local preference. + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:Format="cidr" + Prefixes []string `json:"prefixes,omitempty"` + // LocalPref is the local preference associated to the prefixes. + LocalPref uint32 `json:"localPref,omitempty"` +} + +// CommunityPrefixes is a list of prefixes associated to a community. +type CommunityPrefixes struct { + // Prefixes is the list of prefixes associated to the community. + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:Format="cidr" + Prefixes []string `json:"prefixes,omitempty"` + // Community is the community associated to the prefixes. + Community string `json:"community,omitempty"` +} + +// BFDProfile is the configuration related to the BFD protocol associated +// to a BGP session. +type BFDProfile struct { + // The name of the BFD Profile to be referenced in other parts + // of the configuration. + Name string `json:"name"` + + // The minimum interval that this system is capable of + // receiving control packets in milliseconds. + // Defaults to 300ms. + // +kubebuilder:validation:Maximum:=60000 + // +kubebuilder:validation:Minimum:=10 + // +optional + ReceiveInterval *uint32 `json:"receiveInterval,omitempty"` + + // The minimum transmission interval (less jitter) + // that this system wants to use to send BFD control packets in + // milliseconds. Defaults to 300ms + // +kubebuilder:validation:Maximum:=60000 + // +kubebuilder:validation:Minimum:=10 + // +optional + TransmitInterval *uint32 `json:"transmitInterval,omitempty"` + + // Configures the detection multiplier to determine + // packet loss. The remote transmission interval will be multiplied + // by this value to determine the connection loss detection timer. + // +kubebuilder:validation:Maximum:=255 + // +kubebuilder:validation:Minimum:=2 + // +optional + DetectMultiplier *uint32 `json:"detectMultiplier,omitempty"` + + // Configures the minimal echo receive transmission + // interval that this system is capable of handling in milliseconds. + // Defaults to 50ms + // +kubebuilder:validation:Maximum:=60000 + // +kubebuilder:validation:Minimum:=10 + // +optional + EchoInterval *uint32 `json:"echoInterval,omitempty"` + + // Enables or disables the echo transmission mode. + // This mode is disabled by default, and not supported on multi + // hops setups. + // +optional + EchoMode *bool `json:"echoMode,omitempty"` + + // Mark session as passive: a passive session will not + // attempt to start the connection and will wait for control packets + // from peer before it begins replying. + // +optional + PassiveMode *bool `json:"passiveMode,omitempty"` + + // For multi hop sessions only: configure the minimum + // expected TTL for an incoming BFD control packet. + // +kubebuilder:validation:Maximum:=254 + // +kubebuilder:validation:Minimum:=1 + // +optional + MinimumTTL *uint32 `json:"minimumTtl,omitempty"` +} + +// FRRConfigurationStatus defines the observed state of FRRConfiguration. +type FRRConfigurationStatus struct { + // INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + // Important: Run "make" to regenerate code after modifying this file +} + +//+kubebuilder:object:root=true +//+kubebuilder:subresource:status +//nolint +//+genclient + +// FRRConfiguration is a piece of FRR configuration. +type FRRConfiguration struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec FRRConfigurationSpec `json:"spec,omitempty"` + Status FRRConfigurationStatus `json:"status,omitempty"` +} + +//+kubebuilder:object:root=true + +// FRRConfigurationList contains a list of FRRConfiguration. +type FRRConfigurationList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []FRRConfiguration `json:"items"` +} + +//nolint +//+structType=atomic + +// SecretReference represents a Secret Reference. It has enough information to retrieve secret +// in any namespace. +type SecretReference struct { + // name is unique within a namespace to reference a secret resource. + // +optional + Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"` + // namespace defines the space within which the secret name must be unique. + // +optional + Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,opt,name=namespace"` +} + +func init() { + SchemeBuilder.Register(&FRRConfiguration{}, &FRRConfigurationList{}) +} + +// +kubebuilder:validation:Enum=all;filtered +type AllowMode string + +const ( + AllowAll AllowMode = "all" + AllowRestricted AllowMode = "filtered" +) + +type DynamicASNMode string + +const ( + InternalASNMode DynamicASNMode = "internal" + ExternalASNMode DynamicASNMode = "external" +) diff --git a/vendor/github.com/metallb/frr-k8s/api/v1beta1/groupversion_info.go b/vendor/github.com/metallb/frr-k8s/api/v1beta1/groupversion_info.go new file mode 100644 index 000000000000..987dda84a05d --- /dev/null +++ b/vendor/github.com/metallb/frr-k8s/api/v1beta1/groupversion_info.go @@ -0,0 +1,42 @@ +/* +Copyright 2023. + +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 v1alpha1 contains API Schema definitions for the frrk8s v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=frrk8s.metallb.io +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects. + GroupVersion = schema.GroupVersion{Group: "frrk8s.metallb.io", Version: "v1beta1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme. + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) + +var SchemeGroupVersion = schema.GroupVersion{Group: "frrk8s.metallb.io", Version: "v1beta1"} + +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/vendor/github.com/metallb/frr-k8s/api/v1beta1/zz_generated.deepcopy.go b/vendor/github.com/metallb/frr-k8s/api/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 000000000000..982781e7a57e --- /dev/null +++ b/vendor/github.com/metallb/frr-k8s/api/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,528 @@ +//go:build !ignore_autogenerated + +// SPDX-License-Identifier:Apache-2.0 + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Advertise) DeepCopyInto(out *Advertise) { + *out = *in + in.Allowed.DeepCopyInto(&out.Allowed) + if in.PrefixesWithLocalPref != nil { + in, out := &in.PrefixesWithLocalPref, &out.PrefixesWithLocalPref + *out = make([]LocalPrefPrefixes, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.PrefixesWithCommunity != nil { + in, out := &in.PrefixesWithCommunity, &out.PrefixesWithCommunity + *out = make([]CommunityPrefixes, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Advertise. +func (in *Advertise) DeepCopy() *Advertise { + if in == nil { + return nil + } + out := new(Advertise) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AllowedInPrefixes) DeepCopyInto(out *AllowedInPrefixes) { + *out = *in + if in.Prefixes != nil { + in, out := &in.Prefixes, &out.Prefixes + *out = make([]PrefixSelector, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllowedInPrefixes. +func (in *AllowedInPrefixes) DeepCopy() *AllowedInPrefixes { + if in == nil { + return nil + } + out := new(AllowedInPrefixes) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AllowedOutPrefixes) DeepCopyInto(out *AllowedOutPrefixes) { + *out = *in + if in.Prefixes != nil { + in, out := &in.Prefixes, &out.Prefixes + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AllowedOutPrefixes. +func (in *AllowedOutPrefixes) DeepCopy() *AllowedOutPrefixes { + if in == nil { + return nil + } + out := new(AllowedOutPrefixes) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BFDProfile) DeepCopyInto(out *BFDProfile) { + *out = *in + if in.ReceiveInterval != nil { + in, out := &in.ReceiveInterval, &out.ReceiveInterval + *out = new(uint32) + **out = **in + } + if in.TransmitInterval != nil { + in, out := &in.TransmitInterval, &out.TransmitInterval + *out = new(uint32) + **out = **in + } + if in.DetectMultiplier != nil { + in, out := &in.DetectMultiplier, &out.DetectMultiplier + *out = new(uint32) + **out = **in + } + if in.EchoInterval != nil { + in, out := &in.EchoInterval, &out.EchoInterval + *out = new(uint32) + **out = **in + } + if in.EchoMode != nil { + in, out := &in.EchoMode, &out.EchoMode + *out = new(bool) + **out = **in + } + if in.PassiveMode != nil { + in, out := &in.PassiveMode, &out.PassiveMode + *out = new(bool) + **out = **in + } + if in.MinimumTTL != nil { + in, out := &in.MinimumTTL, &out.MinimumTTL + *out = new(uint32) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BFDProfile. +func (in *BFDProfile) DeepCopy() *BFDProfile { + if in == nil { + return nil + } + out := new(BFDProfile) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BGPConfig) DeepCopyInto(out *BGPConfig) { + *out = *in + if in.Routers != nil { + in, out := &in.Routers, &out.Routers + *out = make([]Router, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.BFDProfiles != nil { + in, out := &in.BFDProfiles, &out.BFDProfiles + *out = make([]BFDProfile, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BGPConfig. +func (in *BGPConfig) DeepCopy() *BGPConfig { + if in == nil { + return nil + } + out := new(BGPConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CommunityPrefixes) DeepCopyInto(out *CommunityPrefixes) { + *out = *in + if in.Prefixes != nil { + in, out := &in.Prefixes, &out.Prefixes + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommunityPrefixes. +func (in *CommunityPrefixes) DeepCopy() *CommunityPrefixes { + if in == nil { + return nil + } + out := new(CommunityPrefixes) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FRRConfiguration) DeepCopyInto(out *FRRConfiguration) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FRRConfiguration. +func (in *FRRConfiguration) DeepCopy() *FRRConfiguration { + if in == nil { + return nil + } + out := new(FRRConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *FRRConfiguration) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FRRConfigurationList) DeepCopyInto(out *FRRConfigurationList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]FRRConfiguration, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FRRConfigurationList. +func (in *FRRConfigurationList) DeepCopy() *FRRConfigurationList { + if in == nil { + return nil + } + out := new(FRRConfigurationList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *FRRConfigurationList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FRRConfigurationSpec) DeepCopyInto(out *FRRConfigurationSpec) { + *out = *in + in.BGP.DeepCopyInto(&out.BGP) + out.Raw = in.Raw + in.NodeSelector.DeepCopyInto(&out.NodeSelector) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FRRConfigurationSpec. +func (in *FRRConfigurationSpec) DeepCopy() *FRRConfigurationSpec { + if in == nil { + return nil + } + out := new(FRRConfigurationSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FRRConfigurationStatus) DeepCopyInto(out *FRRConfigurationStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FRRConfigurationStatus. +func (in *FRRConfigurationStatus) DeepCopy() *FRRConfigurationStatus { + if in == nil { + return nil + } + out := new(FRRConfigurationStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FRRNodeState) DeepCopyInto(out *FRRNodeState) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + out.Status = in.Status +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FRRNodeState. +func (in *FRRNodeState) DeepCopy() *FRRNodeState { + if in == nil { + return nil + } + out := new(FRRNodeState) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *FRRNodeState) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FRRNodeStateList) DeepCopyInto(out *FRRNodeStateList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]FRRNodeState, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FRRNodeStateList. +func (in *FRRNodeStateList) DeepCopy() *FRRNodeStateList { + if in == nil { + return nil + } + out := new(FRRNodeStateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *FRRNodeStateList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FRRNodeStateSpec) DeepCopyInto(out *FRRNodeStateSpec) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FRRNodeStateSpec. +func (in *FRRNodeStateSpec) DeepCopy() *FRRNodeStateSpec { + if in == nil { + return nil + } + out := new(FRRNodeStateSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FRRNodeStateStatus) DeepCopyInto(out *FRRNodeStateStatus) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FRRNodeStateStatus. +func (in *FRRNodeStateStatus) DeepCopy() *FRRNodeStateStatus { + if in == nil { + return nil + } + out := new(FRRNodeStateStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Import) DeepCopyInto(out *Import) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Import. +func (in *Import) DeepCopy() *Import { + if in == nil { + return nil + } + out := new(Import) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LocalPrefPrefixes) DeepCopyInto(out *LocalPrefPrefixes) { + *out = *in + if in.Prefixes != nil { + in, out := &in.Prefixes, &out.Prefixes + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LocalPrefPrefixes. +func (in *LocalPrefPrefixes) DeepCopy() *LocalPrefPrefixes { + if in == nil { + return nil + } + out := new(LocalPrefPrefixes) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Neighbor) DeepCopyInto(out *Neighbor) { + *out = *in + if in.Port != nil { + in, out := &in.Port, &out.Port + *out = new(uint16) + **out = **in + } + out.PasswordSecret = in.PasswordSecret + if in.HoldTime != nil { + in, out := &in.HoldTime, &out.HoldTime + *out = new(v1.Duration) + **out = **in + } + if in.KeepaliveTime != nil { + in, out := &in.KeepaliveTime, &out.KeepaliveTime + *out = new(v1.Duration) + **out = **in + } + if in.ConnectTime != nil { + in, out := &in.ConnectTime, &out.ConnectTime + *out = new(v1.Duration) + **out = **in + } + in.ToAdvertise.DeepCopyInto(&out.ToAdvertise) + in.ToReceive.DeepCopyInto(&out.ToReceive) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Neighbor. +func (in *Neighbor) DeepCopy() *Neighbor { + if in == nil { + return nil + } + out := new(Neighbor) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PrefixSelector) DeepCopyInto(out *PrefixSelector) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PrefixSelector. +func (in *PrefixSelector) DeepCopy() *PrefixSelector { + if in == nil { + return nil + } + out := new(PrefixSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RawConfig) DeepCopyInto(out *RawConfig) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RawConfig. +func (in *RawConfig) DeepCopy() *RawConfig { + if in == nil { + return nil + } + out := new(RawConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Receive) DeepCopyInto(out *Receive) { + *out = *in + in.Allowed.DeepCopyInto(&out.Allowed) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Receive. +func (in *Receive) DeepCopy() *Receive { + if in == nil { + return nil + } + out := new(Receive) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Router) DeepCopyInto(out *Router) { + *out = *in + if in.Neighbors != nil { + in, out := &in.Neighbors, &out.Neighbors + *out = make([]Neighbor, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Prefixes != nil { + in, out := &in.Prefixes, &out.Prefixes + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Imports != nil { + in, out := &in.Imports, &out.Imports + *out = make([]Import, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Router. +func (in *Router) DeepCopy() *Router { + if in == nil { + return nil + } + out := new(Router) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *SecretReference) DeepCopyInto(out *SecretReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretReference. +func (in *SecretReference) DeepCopy() *SecretReference { + if in == nil { + return nil + } + out := new(SecretReference) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/LICENSE b/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/LICENSE new file mode 100644 index 000000000000..d64569567334 --- /dev/null +++ b/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/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/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/doc.go b/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/doc.go new file mode 100644 index 000000000000..e7024fd4f893 --- /dev/null +++ b/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/doc.go @@ -0,0 +1,5 @@ +// Package v1 contains API Schema definitions for the RouteAdvertisements v1 API +// group +// +k8s:deepcopy-gen=package +// +groupName=k8s.ovn.org +package v1 diff --git a/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/register.go b/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/register.go new file mode 100644 index 000000000000..c2e4822462c2 --- /dev/null +++ b/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/register.go @@ -0,0 +1,34 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +var ( + GroupName = "k8s.ovn.org" + SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"} + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +// Adds the list of known types to api.Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &RouteAdvertisements{}, + &RouteAdvertisementsList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/types.go b/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/types.go new file mode 100644 index 000000000000..2fc2ccf59a68 --- /dev/null +++ b/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/types.go @@ -0,0 +1,91 @@ +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// +genclient +// +genclient:nonNamespaced +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +kubebuilder:resource:path=routeadvertisements,scope=Cluster,shortName=ra,singular=routeadvertisements +// +kubebuilder::singular=routeadvertisements +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=".status.status" +// RouteAdvertisements is the Schema for the routeadvertisements API +type RouteAdvertisements struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec RouteAdvertisementsSpec `json:"spec,omitempty"` + Status RouteAdvertisementsStatus `json:"status,omitempty"` +} + +// RouteAdvertisementsSpec defines the desired state of RouteAdvertisements +// +kubebuilder:validation:XValidation:rule="!has(self.nodeSelector) || !('PodNetwork' in self.advertisements)",message="If 'PodNetwork' is selected for advertisement, a 'nodeSelector' can't be specified as it needs to be advertised on all nodes" +type RouteAdvertisementsSpec struct { + // targetVRF determines which VRF the routes should be advertised in. + // +kubebuilder:validation:Optional + TargetVRF string `json:"targetVRF,omitempty"` + + // networkSelector determines which network routes should be advertised. To + // select the default network, match on label 'k8s.ovn.org/default-network'. + NetworkSelector metav1.LabelSelector `json:"networkSelector,omitempty"` + + // nodeSelector limits the advertisements to selected nodes. + // When omitted, all nodes are selected. + NodeSelector metav1.LabelSelector `json:"nodeSelector,omitempty"` + + // frrConfigurationSelector determines which FRRConfigurations will the + // OVN-Kubernetes driven FRRConfigurations be based on. + // When omitted, all FRRConfigurations will be considered. + FRRConfigurationSelector metav1.LabelSelector `json:"frrConfigurationSelector,omitempty"` + + // advertisements determines what is advertised. + // +kubebuilder:validation:Required + // +kubebuilder:validation:MinItems=1 + // +kubebuilder:validation:MaxItems=2 + // +kubebuilder:validation:XValidation:rule="self.all(x, self.exists_one(y, x == y))" + Advertisements []AdvertisementType `json:"advertisements,omitempty"` +} + +// AdvertisementType determines the type of advertisement. +// +kubebuilder:validation:Enum=PodNetwork;EgressIP +type AdvertisementType string + +const ( + // PodNetwork determines that the pod network is advertised. + PodNetwork AdvertisementType = "PodNetwork" + + // EgressIP determines that egress IPs are being advertised. + EgressIP AdvertisementType = "EgressIP" +) + +// RouteAdvertisementsStatus defines the observed state of RouteAdvertisements. +// It should always be reconstructable from the state of the cluster and/or +// outside world. +type RouteAdvertisementsStatus struct { + // status is a concise indication of whether the RouteAdvertisements + // resource is applied with success. + // +kubebuilder:validation:Optional + Status string `json:"status,omitempty"` + + // conditions is an array of condition objects indicating details about + // status of RouteAdvertisements object. + // +kubebuilder:validation:Optional + // +patchMergeKey=type + // +patchStrategy=merge + // +listType=map + // +listMapKey=type + Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` +} + +// RouteAdvertisementsList contains a list of RouteAdvertisements +// +kubebuilder:object:root=true +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +type RouteAdvertisementsList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []RouteAdvertisements `json:"items"` +} diff --git a/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/zz_generated.deepcopy.go b/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/zz_generated.deepcopy.go new file mode 100644 index 000000000000..484890a7cf91 --- /dev/null +++ b/vendor/github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1/zz_generated.deepcopy.go @@ -0,0 +1,134 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* + + +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 deepcopy-gen. DO NOT EDIT. + +package v1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteAdvertisements) DeepCopyInto(out *RouteAdvertisements) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteAdvertisements. +func (in *RouteAdvertisements) DeepCopy() *RouteAdvertisements { + if in == nil { + return nil + } + out := new(RouteAdvertisements) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RouteAdvertisements) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteAdvertisementsList) DeepCopyInto(out *RouteAdvertisementsList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]RouteAdvertisements, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteAdvertisementsList. +func (in *RouteAdvertisementsList) DeepCopy() *RouteAdvertisementsList { + if in == nil { + return nil + } + out := new(RouteAdvertisementsList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *RouteAdvertisementsList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteAdvertisementsSpec) DeepCopyInto(out *RouteAdvertisementsSpec) { + *out = *in + in.NetworkSelector.DeepCopyInto(&out.NetworkSelector) + in.NodeSelector.DeepCopyInto(&out.NodeSelector) + in.FRRConfigurationSelector.DeepCopyInto(&out.FRRConfigurationSelector) + if in.Advertisements != nil { + in, out := &in.Advertisements, &out.Advertisements + *out = make([]AdvertisementType, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteAdvertisementsSpec. +func (in *RouteAdvertisementsSpec) DeepCopy() *RouteAdvertisementsSpec { + if in == nil { + return nil + } + out := new(RouteAdvertisementsSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RouteAdvertisementsStatus) DeepCopyInto(out *RouteAdvertisementsStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteAdvertisementsStatus. +func (in *RouteAdvertisementsStatus) DeepCopy() *RouteAdvertisementsStatus { + if in == nil { + return nil + } + out := new(RouteAdvertisementsStatus) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/spf13/afero/afero.go b/vendor/github.com/spf13/afero/afero.go index 199480cd05ef..39f65852099d 100644 --- a/vendor/github.com/spf13/afero/afero.go +++ b/vendor/github.com/spf13/afero/afero.go @@ -97,7 +97,7 @@ type Fs interface { // Chown changes the uid and gid of the named file. Chown(name string, uid, gid int) error - //Chtimes changes the access and modification times of the named file + // Chtimes changes the access and modification times of the named file Chtimes(name string, atime time.Time, mtime time.Time) error } diff --git a/vendor/github.com/spf13/afero/basepath.go b/vendor/github.com/spf13/afero/basepath.go index 70a1d91680bb..2e72793a3e1b 100644 --- a/vendor/github.com/spf13/afero/basepath.go +++ b/vendor/github.com/spf13/afero/basepath.go @@ -40,7 +40,6 @@ func (f *BasePathFile) Name() string { func (f *BasePathFile) ReadDir(n int) ([]fs.DirEntry, error) { if rdf, ok := f.File.(fs.ReadDirFile); ok { return rdf.ReadDir(n) - } return readDirFile{f.File}.ReadDir(n) } diff --git a/vendor/github.com/spf13/afero/copyOnWriteFs.go b/vendor/github.com/spf13/afero/copyOnWriteFs.go index 6ff8f3099a1b..184d6dd702a0 100644 --- a/vendor/github.com/spf13/afero/copyOnWriteFs.go +++ b/vendor/github.com/spf13/afero/copyOnWriteFs.go @@ -223,7 +223,7 @@ func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, return nil, err } if isaDir { - if err = u.layer.MkdirAll(dir, 0777); err != nil { + if err = u.layer.MkdirAll(dir, 0o777); err != nil { return nil, err } return u.layer.OpenFile(name, flag, perm) @@ -247,8 +247,9 @@ func (u *CopyOnWriteFs) OpenFile(name string, flag int, perm os.FileMode) (File, // This function handles the 9 different possibilities caused // by the union which are the intersection of the following... -// layer: doesn't exist, exists as a file, and exists as a directory -// base: doesn't exist, exists as a file, and exists as a directory +// +// layer: doesn't exist, exists as a file, and exists as a directory +// base: doesn't exist, exists as a file, and exists as a directory func (u *CopyOnWriteFs) Open(name string) (File, error) { // Since the overlay overrides the base we check that first b, err := u.isBaseFile(name) @@ -322,5 +323,5 @@ func (u *CopyOnWriteFs) MkdirAll(name string, perm os.FileMode) error { } func (u *CopyOnWriteFs) Create(name string) (File, error) { - return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666) + return u.OpenFile(name, os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0o666) } diff --git a/vendor/github.com/spf13/afero/ioutil.go b/vendor/github.com/spf13/afero/ioutil.go index 386c9cdc2277..fa6abe1eeec0 100644 --- a/vendor/github.com/spf13/afero/ioutil.go +++ b/vendor/github.com/spf13/afero/ioutil.go @@ -141,8 +141,10 @@ func WriteFile(fs Fs, filename string, data []byte, perm os.FileMode) error { // We generate random temporary file names so that there's a good // chance the file doesn't exist yet - keeps the number of tries in // TempFile to a minimum. -var randNum uint32 -var randmu sync.Mutex +var ( + randNum uint32 + randmu sync.Mutex +) func reseed() uint32 { return uint32(time.Now().UnixNano() + int64(os.Getpid())) @@ -190,7 +192,7 @@ func TempFile(fs Fs, dir, pattern string) (f File, err error) { nconflict := 0 for i := 0; i < 10000; i++ { name := filepath.Join(dir, prefix+nextRandom()+suffix) - f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0600) + f, err = fs.OpenFile(name, os.O_RDWR|os.O_CREATE|os.O_EXCL, 0o600) if os.IsExist(err) { if nconflict++; nconflict > 10 { randmu.Lock() @@ -214,6 +216,7 @@ func TempFile(fs Fs, dir, pattern string) (f File, err error) { func (a Afero) TempDir(dir, prefix string) (name string, err error) { return TempDir(a.Fs, dir, prefix) } + func TempDir(fs Fs, dir, prefix string) (name string, err error) { if dir == "" { dir = os.TempDir() @@ -222,7 +225,7 @@ func TempDir(fs Fs, dir, prefix string) (name string, err error) { nconflict := 0 for i := 0; i < 10000; i++ { try := filepath.Join(dir, prefix+nextRandom()) - err = fs.Mkdir(try, 0700) + err = fs.Mkdir(try, 0o700) if os.IsExist(err) { if nconflict++; nconflict > 10 { randmu.Lock() diff --git a/vendor/github.com/spf13/afero/mem/file.go b/vendor/github.com/spf13/afero/mem/file.go index 3cf4693b5a27..62fe4498e197 100644 --- a/vendor/github.com/spf13/afero/mem/file.go +++ b/vendor/github.com/spf13/afero/mem/file.go @@ -245,7 +245,7 @@ func (f *File) Truncate(size int64) error { defer f.fileData.Unlock() if size > int64(len(f.fileData.data)) { diff := size - int64(len(f.fileData.data)) - f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{00}, int(diff))...) + f.fileData.data = append(f.fileData.data, bytes.Repeat([]byte{0o0}, int(diff))...) } else { f.fileData.data = f.fileData.data[0:size] } @@ -285,7 +285,7 @@ func (f *File) Write(b []byte) (n int, err error) { tail = f.fileData.data[n+int(cur):] } if diff > 0 { - f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{00}, int(diff)), b...)...) + f.fileData.data = append(f.fileData.data, append(bytes.Repeat([]byte{0o0}, int(diff)), b...)...) f.fileData.data = append(f.fileData.data, tail...) } else { f.fileData.data = append(f.fileData.data[:cur], b...) @@ -321,16 +321,19 @@ func (s *FileInfo) Name() string { s.Unlock() return name } + func (s *FileInfo) Mode() os.FileMode { s.Lock() defer s.Unlock() return s.mode } + func (s *FileInfo) ModTime() time.Time { s.Lock() defer s.Unlock() return s.modtime } + func (s *FileInfo) IsDir() bool { s.Lock() defer s.Unlock() diff --git a/vendor/github.com/spf13/afero/memmap.go b/vendor/github.com/spf13/afero/memmap.go index ea0798d8704a..e6b7d70b9430 100644 --- a/vendor/github.com/spf13/afero/memmap.go +++ b/vendor/github.com/spf13/afero/memmap.go @@ -15,6 +15,7 @@ package afero import ( "fmt" + "io" "log" "os" "path/filepath" @@ -43,7 +44,7 @@ func (m *MemMapFs) getData() map[string]*mem.FileData { // Root should always exist, right? // TODO: what about windows? root := mem.CreateDir(FilePathSeparator) - mem.SetMode(root, os.ModeDir|0755) + mem.SetMode(root, os.ModeDir|0o755) m.data[FilePathSeparator] = root }) return m.data @@ -96,12 +97,12 @@ func (m *MemMapFs) registerWithParent(f *mem.FileData, perm os.FileMode) { pdir := filepath.Dir(filepath.Clean(f.Name())) err := m.lockfreeMkdir(pdir, perm) if err != nil { - //log.Println("Mkdir error:", err) + // log.Println("Mkdir error:", err) return } parent, err = m.lockfreeOpen(pdir) if err != nil { - //log.Println("Open after Mkdir error:", err) + // log.Println("Open after Mkdir error:", err) return } } @@ -142,6 +143,11 @@ func (m *MemMapFs) Mkdir(name string, perm os.FileMode) error { } m.mu.Lock() + // Dobule check that it doesn't exist. + if _, ok := m.getData()[name]; ok { + m.mu.Unlock() + return &os.PathError{Op: "mkdir", Path: name, Err: ErrFileExists} + } item := mem.CreateDir(name) mem.SetMode(item, os.ModeDir|perm) m.getData()[name] = item @@ -232,7 +238,7 @@ func (m *MemMapFs) OpenFile(name string, flag int, perm os.FileMode) (File, erro file = mem.NewReadOnlyFileHandle(file.(*mem.File).Data()) } if flag&os.O_APPEND > 0 { - _, err = file.Seek(0, os.SEEK_END) + _, err = file.Seek(0, io.SeekEnd) if err != nil { file.Close() return nil, err @@ -314,6 +320,18 @@ func (m *MemMapFs) Rename(oldname, newname string) error { } else { return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound} } + + for p, fileData := range m.getData() { + if strings.HasPrefix(p, oldname+FilePathSeparator) { + m.mu.RUnlock() + m.mu.Lock() + delete(m.getData(), p) + p := strings.Replace(p, oldname, newname, 1) + m.getData()[p] = fileData + m.mu.Unlock() + m.mu.RLock() + } + } return nil } diff --git a/vendor/github.com/spf13/afero/regexpfs.go b/vendor/github.com/spf13/afero/regexpfs.go index ac359c62a05b..218f3b235bdd 100644 --- a/vendor/github.com/spf13/afero/regexpfs.go +++ b/vendor/github.com/spf13/afero/regexpfs.go @@ -10,7 +10,6 @@ import ( // The RegexpFs filters files (not directories) by regular expression. Only // files matching the given regexp will be allowed, all others get a ENOENT error ( // "No such file or directory"). -// type RegexpFs struct { re *regexp.Regexp source Fs diff --git a/vendor/github.com/spf13/afero/symlink.go b/vendor/github.com/spf13/afero/symlink.go index d1c6ea53d95b..aa6ae125b657 100644 --- a/vendor/github.com/spf13/afero/symlink.go +++ b/vendor/github.com/spf13/afero/symlink.go @@ -21,9 +21,9 @@ import ( // filesystems saying so. // It indicates support for 3 symlink related interfaces that implement the // behaviors of the os methods: -// - Lstat -// - Symlink, and -// - Readlink +// - Lstat +// - Symlink, and +// - Readlink type Symlinker interface { Lstater Linker diff --git a/vendor/github.com/spf13/afero/unionFile.go b/vendor/github.com/spf13/afero/unionFile.go index 333d367f48b9..62dd6c93c839 100644 --- a/vendor/github.com/spf13/afero/unionFile.go +++ b/vendor/github.com/spf13/afero/unionFile.go @@ -47,7 +47,7 @@ func (f *UnionFile) Read(s []byte) (int, error) { if (err == nil || err == io.EOF) && f.Base != nil { // advance the file position also in the base file, the next // call may be a write at this position (or a seek with SEEK_CUR) - if _, seekErr := f.Base.Seek(int64(n), os.SEEK_CUR); seekErr != nil { + if _, seekErr := f.Base.Seek(int64(n), io.SeekCurrent); seekErr != nil { // only overwrite err in case the seek fails: we need to // report an eventual io.EOF to the caller err = seekErr @@ -130,7 +130,7 @@ func (f *UnionFile) Name() string { type DirsMerger func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, error) { - var files = make(map[string]os.FileInfo) + files := make(map[string]os.FileInfo) for _, fi := range lofi { files[fi.Name()] = fi @@ -151,7 +151,6 @@ var defaultUnionMergeDirsFn = func(lofi, bofi []os.FileInfo) ([]os.FileInfo, err } return rfi, nil - } // Readdir will weave the two directories together and @@ -275,7 +274,7 @@ func copyFile(base Fs, layer Fs, name string, bfh File) error { return err } if !exists { - err = layer.MkdirAll(filepath.Dir(name), 0777) // FIXME? + err = layer.MkdirAll(filepath.Dir(name), 0o777) // FIXME? if err != nil { return err } diff --git a/vendor/github.com/spf13/afero/util.go b/vendor/github.com/spf13/afero/util.go index cb7de23f2694..9e4cba2746a0 100644 --- a/vendor/github.com/spf13/afero/util.go +++ b/vendor/github.com/spf13/afero/util.go @@ -43,7 +43,7 @@ func WriteReader(fs Fs, path string, r io.Reader) (err error) { ospath := filepath.FromSlash(dir) if ospath != "" { - err = fs.MkdirAll(ospath, 0777) // rwx, rw, r + err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r if err != nil { if err != os.ErrExist { return err @@ -71,7 +71,7 @@ func SafeWriteReader(fs Fs, path string, r io.Reader) (err error) { ospath := filepath.FromSlash(dir) if ospath != "" { - err = fs.MkdirAll(ospath, 0777) // rwx, rw, r + err = fs.MkdirAll(ospath, 0o777) // rwx, rw, r if err != nil { return } @@ -124,7 +124,7 @@ func GetTempDir(fs Fs, subPath string) string { return addSlash(dir) } - err := fs.MkdirAll(dir, 0777) + err := fs.MkdirAll(dir, 0o777) if err != nil { panic(err) } @@ -197,7 +197,6 @@ func FileContainsAnyBytes(fs Fs, filename string, subslices [][]byte) (bool, err // readerContains reports whether any of the subslices is within r. func readerContainsAny(r io.Reader, subslices ...[]byte) bool { - if r == nil || len(subslices) == 0 { return false } diff --git a/vendor/modules.txt b/vendor/modules.txt index 46a6ef7c207a..4838b66d8fde 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -619,8 +619,8 @@ github.com/josharian/intern # github.com/json-iterator/go v1.1.12 ## explicit; go 1.12 github.com/json-iterator/go -# github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.3.0 -## explicit; go 1.17 +# github.com/k8snetworkplumbingwg/network-attachment-definition-client v1.6.0 +## explicit; go 1.21 github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/apis/k8s.cni.cncf.io/v1 github.com/k8snetworkplumbingwg/network-attachment-definition-client/pkg/client/clientset/versioned @@ -688,6 +688,9 @@ github.com/magiconair/properties github.com/mailru/easyjson/buffer github.com/mailru/easyjson/jlexer github.com/mailru/easyjson/jwriter +# github.com/metallb/frr-k8s v0.0.15 +## explicit; go 1.22.0 +github.com/metallb/frr-k8s/api/v1beta1 # github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible ## explicit github.com/mistifyio/go-zfs @@ -1110,6 +1113,9 @@ github.com/openshift/library-go/pkg/template/templateprocessingclient github.com/openshift/library-go/test/library github.com/openshift/library-go/test/library/apiserver github.com/openshift/library-go/test/library/metrics +# github.com/ovn-org/ovn-kubernetes/go-controller v0.0.0-20250118001652-a8b9c3c31417 +## explicit; go 1.22.0 +github.com/ovn-org/ovn-kubernetes/go-controller/pkg/crd/routeadvertisements/v1 # github.com/pborman/uuid v1.2.0 ## explicit github.com/pborman/uuid @@ -1188,7 +1194,7 @@ github.com/sirupsen/logrus # github.com/soheilhy/cmux v0.1.5 ## explicit; go 1.11 github.com/soheilhy/cmux -# github.com/spf13/afero v1.9.2 +# github.com/spf13/afero v1.9.5 ## explicit; go 1.16 github.com/spf13/afero github.com/spf13/afero/internal/common @@ -3737,9 +3743,10 @@ sigs.k8s.io/cloud-provider-azure/pkg/azclient/virtualnetworklinkclient # sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader v0.0.16 ## explicit; go 1.22.0 sigs.k8s.io/cloud-provider-azure/pkg/azclient/configloader -# sigs.k8s.io/controller-runtime v0.18.4 +# sigs.k8s.io/controller-runtime v0.19.0 ## explicit; go 1.22.0 sigs.k8s.io/controller-runtime/pkg/conversion +sigs.k8s.io/controller-runtime/pkg/scheme # sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 ## explicit; go 1.21 sigs.k8s.io/json diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go b/vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go new file mode 100644 index 000000000000..55ebe2177354 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/scheme/scheme.go @@ -0,0 +1,93 @@ +/* +Copyright 2018 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. +*/ + +// Package scheme contains utilities for gradually building Schemes, +// which contain information associating Go types with Kubernetes +// groups, versions, and kinds. +// +// Each API group should define a utility function +// called AddToScheme for adding its types to a Scheme: +// +// // in package myapigroupv1... +// var ( +// SchemeGroupVersion = schema.GroupVersion{Group: "my.api.group", Version: "v1"} +// SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} +// AddToScheme = SchemeBuilder.AddToScheme +// ) +// +// func init() { +// SchemeBuilder.Register(&MyType{}, &MyTypeList) +// } +// var ( +// scheme *runtime.Scheme = runtime.NewScheme() +// ) +// +// This also true of the built-in Kubernetes types. Then, in the entrypoint for +// your manager, assemble the scheme containing exactly the types you need, +// panicing if scheme registration failed. For instance, if our controller needs +// types from the core/v1 API group (e.g. Pod), plus types from my.api.group/v1: +// +// func init() { +// utilruntime.Must(myapigroupv1.AddToScheme(scheme)) +// utilruntime.Must(kubernetesscheme.AddToScheme(scheme)) +// } +// +// func main() { +// mgr := controllers.NewManager(context.Background(), controllers.GetConfigOrDie(), manager.Options{ +// Scheme: scheme, +// }) +// // ... +// } +package scheme + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// Builder builds a new Scheme for mapping go types to Kubernetes GroupVersionKinds. +type Builder struct { + GroupVersion schema.GroupVersion + runtime.SchemeBuilder +} + +// Register adds one or more objects to the SchemeBuilder so they can be added to a Scheme. Register mutates bld. +func (bld *Builder) Register(object ...runtime.Object) *Builder { + bld.SchemeBuilder.Register(func(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(bld.GroupVersion, object...) + metav1.AddToGroupVersion(scheme, bld.GroupVersion) + return nil + }) + return bld +} + +// RegisterAll registers all types from the Builder argument. RegisterAll mutates bld. +func (bld *Builder) RegisterAll(b *Builder) *Builder { + bld.SchemeBuilder = append(bld.SchemeBuilder, b.SchemeBuilder...) + return bld +} + +// AddToScheme adds all registered types to s. +func (bld *Builder) AddToScheme(s *runtime.Scheme) error { + return bld.SchemeBuilder.AddToScheme(s) +} + +// Build returns a new Scheme containing the registered types. +func (bld *Builder) Build() (*runtime.Scheme, error) { + s := runtime.NewScheme() + return s, bld.AddToScheme(s) +} diff --git a/zz_generated.manifests/test-reporting.yaml b/zz_generated.manifests/test-reporting.yaml index 6be568949c4a..68ed0ef76a5c 100644 --- a/zz_generated.manifests/test-reporting.yaml +++ b/zz_generated.manifests/test-reporting.yaml @@ -475,6 +475,24 @@ spec: networks and persistent ips configured created using [OCPFeatureGate:NetworkSegmentation] UserDefinedNetwork [Suite:openshift/network/virtualization] should keep ip when the VMI attached to a secondary UDN is migrated between nodes' + - featureGate: RouteAdvertisements + tests: + - testName: '[sig-network][OCPFeatureGate:RouteAdvertisements][Feature:RouteAdvertisements][apigroup:operator.openshift.io] + when using openshift ovn-kubernetes [PodNetwork] Advertising a cluster user + defined network [Serial][apigroup:user.openshift.io][apigroup:security.openshift.io] + External host should be able to query route advertised pods by the pod IP' + - testName: '[sig-network][OCPFeatureGate:RouteAdvertisements][Feature:RouteAdvertisements][apigroup:operator.openshift.io] + when using openshift ovn-kubernetes [PodNetwork] Advertising a cluster user + defined network [Serial][apigroup:user.openshift.io][apigroup:security.openshift.io] + pods should communicate with external host without being SNATed' + - testName: '[sig-network][OCPFeatureGate:RouteAdvertisements][Feature:RouteAdvertisements][apigroup:operator.openshift.io] + when using openshift ovn-kubernetes [PodNetwork] Advertising the default network + [apigroup:user.openshift.io][apigroup:security.openshift.io] External host + should be able to query route advertised pods by the pod IP' + - testName: '[sig-network][OCPFeatureGate:RouteAdvertisements][Feature:RouteAdvertisements][apigroup:operator.openshift.io] + when using openshift ovn-kubernetes [PodNetwork] Advertising the default network + [apigroup:user.openshift.io][apigroup:security.openshift.io] pods should communicate + with external host without being SNATed' - featureGate: RouteExternalCertificate tests: - testName: '[sig-network][OCPFeatureGate:RouteExternalCertificate][Feature:Router][apigroup:route.openshift.io]