github.com/cilium/cilium@v1.16.2/test/k8s/lrp.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package k8sTest 5 6 import ( 7 "context" 8 "fmt" 9 "strings" 10 "sync" 11 "time" 12 13 . "github.com/onsi/gomega" 14 15 . "github.com/cilium/cilium/test/ginkgo-ext" 16 "github.com/cilium/cilium/test/helpers" 17 ) 18 19 // The 5.4 CI job is intended to catch BPF complexity regressions and as such 20 // doesn't need to execute this test suite. 21 var _ = SkipDescribeIf(func() bool { return helpers.RunsOn54Kernel() && helpers.DoesNotRunOnAKS() }, "K8sDatapathLRPTests", func() { 22 var ( 23 kubectl *helpers.Kubectl 24 ciliumFilename string 25 ) 26 27 BeforeAll(func() { 28 kubectl = helpers.CreateKubectl(helpers.K8s1VMName(), logger) 29 ciliumFilename = helpers.TimestampFilename("cilium.yaml") 30 }) 31 32 JustAfterEach(func() { 33 kubectl.ValidateNoErrorsInLogs(CurrentGinkgoTestDescription().Duration) 34 }) 35 36 AfterAll(func() { 37 UninstallCiliumFromManifest(kubectl, ciliumFilename) 38 kubectl.CloseSSHClient() 39 }) 40 41 AfterFailed(func() { 42 kubectl.CiliumReport("cilium-dbg lrp list", "cilium-dbg service list") 43 }) 44 45 SkipContextIf(func() bool { return helpers.RunsOnAKS() }, "Checks local redirect policy", func() { 46 const ( 47 lrpServiceName = "lrp-demo-service" 48 be1Name = "k8s1-backend" 49 be2Name = "k8s2-backend" 50 feFilter = "role=frontend" 51 beFilter = "role=backend" 52 beFilter2 = "role=lrpAddrBackend" 53 lrpAddrIP = "169.254.169.254" 54 ) 55 56 var ( 57 deploymentYAML string 58 lrpSvcYAML string 59 svcIP string 60 curl4TCP string 61 curl4UDP string 62 curl4in6TCP string 63 curl4in6UDP string 64 curlTCPAddr string 65 curlUDPAddr string 66 be3Name string 67 be4Name string 68 ) 69 70 BeforeAll(func() { 71 DeployCiliumOptionsAndDNS(kubectl, ciliumFilename, map[string]string{ 72 "localRedirectPolicy": "true", 73 }) 74 deploymentYAML = helpers.ManifestGet(kubectl.BasePath(), "lrp-test.yaml") 75 lrpSvcYAML = helpers.ManifestGet(kubectl.BasePath(), "lrp-svc.yaml") 76 res := kubectl.ApplyDefault(deploymentYAML) 77 res.ExpectSuccess("Unable to apply %s", deploymentYAML) 78 for _, pod := range []string{feFilter, beFilter, beFilter2} { 79 err := kubectl.WaitforPods(helpers.DefaultNamespace, fmt.Sprintf("-l %s", pod), helpers.HelperTimeout) 80 Expect(err).Should(BeNil()) 81 } 82 clusterIP, _, err := kubectl.GetServiceHostPort(helpers.DefaultNamespace, lrpServiceName) 83 svcIP = clusterIP 84 Expect(err).To(BeNil(), "Cannot get svc IP") 85 86 http4SVCURL := getHTTPLink(svcIP, 80) 87 tftp4SVCURL := getTFTPLink(svcIP, 69) 88 http4in6SVCURL := getHTTPLink("::ffff:"+svcIP, 80) 89 tftp4in6SVCURL := getTFTPLink("::ffff:"+svcIP, 69) 90 91 curl4TCP = helpers.CurlFailNoStats(http4SVCURL) 92 curl4UDP = helpers.CurlFailNoStats(tftp4SVCURL) 93 curl4in6TCP = helpers.CurlFailNoStats(http4in6SVCURL) 94 curl4in6UDP = helpers.CurlFailNoStats(tftp4in6SVCURL) 95 curlTCPAddr = helpers.CurlFailNoStats(getHTTPLink(lrpAddrIP, 80)) 96 curlUDPAddr = helpers.CurlFailNoStats(getTFTPLink(lrpAddrIP, 69)) 97 98 // Hostnames for host networked pods 99 be3Name, _ = kubectl.GetNodeInfo(helpers.K8s1) 100 be4Name, _ = kubectl.GetNodeInfo(helpers.K8s2) 101 }) 102 103 AfterAll(func() { 104 _ = kubectl.Delete(deploymentYAML) 105 ExpectAllPodsTerminated(kubectl) 106 }) 107 108 It("LRP connectivity", func() { 109 type lrpTestCase struct { 110 selector string 111 cmd string 112 want string 113 notWant string 114 } 115 116 // Basic sanity check 117 ciliumPods, err := kubectl.GetCiliumPods() 118 Expect(err).To(BeNil(), "Cannot get cilium pods") 119 for _, pod := range ciliumPods { 120 service := kubectl.CiliumExecMustSucceed(context.TODO(), pod, fmt.Sprintf("cilium-dbg service list | grep \" %s:\"", svcIP), "Cannot retrieve services on cilium pod") 121 service.ExpectContains("LocalRedirect", "LocalRedirect is not present in the cilium service list for [%s]", svcIP) 122 service2 := kubectl.CiliumExecMustSucceed(context.TODO(), pod, fmt.Sprintf("cilium-dbg service list | grep \" %s:\"", lrpAddrIP), "Cannot retrieve services on cilium pod") 123 service2.ExpectContains("LocalRedirect", "LocalRedirect is not present in the cilium service list for [%s]", lrpAddrIP) 124 } 125 126 By("Checking traffic goes to local backend") 127 testCases := []lrpTestCase{ 128 { 129 selector: "id=app1", 130 cmd: curl4TCP, 131 // Expects to see local backend name in returned Hostname field 132 want: be1Name, 133 // Expects never to see remote backend name in returned Hostname field 134 notWant: be2Name, 135 }, 136 { 137 selector: "id=app2", 138 cmd: curl4TCP, 139 want: be2Name, 140 notWant: be1Name, 141 }, 142 { 143 selector: "id=app1", 144 cmd: curl4UDP, 145 want: be1Name, 146 notWant: be2Name, 147 }, 148 { 149 selector: "id=app2", 150 cmd: curl4UDP, 151 want: be2Name, 152 notWant: be1Name, 153 }, 154 { 155 selector: "id=app1", 156 cmd: curl4in6TCP, 157 want: be1Name, 158 notWant: be2Name, 159 }, 160 { 161 selector: "id=app2", 162 cmd: curl4in6TCP, 163 want: be2Name, 164 notWant: be1Name, 165 }, 166 { 167 selector: "id=app1", 168 cmd: curl4in6UDP, 169 want: be1Name, 170 notWant: be2Name, 171 }, 172 { 173 selector: "id=app2", 174 cmd: curl4in6UDP, 175 want: be2Name, 176 notWant: be1Name, 177 }, 178 // Address matcher test cases. 179 { 180 selector: "id=app1", 181 cmd: curlTCPAddr, 182 want: be3Name, 183 notWant: be4Name, 184 }, 185 { 186 selector: "id=app2", 187 cmd: curlUDPAddr, 188 want: be4Name, 189 notWant: be3Name, 190 }, 191 } 192 193 var wg sync.WaitGroup 194 for _, testCase := range testCases { 195 wg.Add(1) 196 go func(tc lrpTestCase) { 197 defer GinkgoRecover() 198 defer wg.Done() 199 Consistently(func() bool { 200 pods, err := kubectl.GetPodNames(helpers.DefaultNamespace, tc.selector) 201 Expect(err).Should(BeNil(), "cannot retrieve pod names by filter %q", tc.selector) 202 Expect(len(pods)).Should(BeNumerically(">", 0), "no pod exists by filter %q", tc.selector) 203 ret := true 204 for _, pod := range pods { 205 res := kubectl.ExecPodCmd(helpers.DefaultNamespace, pod, tc.cmd) 206 Expect(err).To(BeNil(), "%s failed in %s pod", tc.cmd, pod) 207 ret = ret && strings.Contains(res.Stdout(), tc.want) && !strings.Contains(res.Stdout(), tc.notWant) 208 } 209 return ret 210 }, 30*time.Second, 1*time.Second).Should(BeTrue(), "assertion fails for test case: %v", tc) 211 }(testCase) 212 } 213 wg.Wait() 214 }) 215 216 It("LRP restores service when removed", func() { 217 type lrpTestCase struct { 218 selector string 219 cmd string 220 } 221 222 _ = kubectl.Delete(lrpSvcYAML) 223 // Basic sanity check 224 ciliumPods, err := kubectl.GetCiliumPods() 225 Expect(err).To(BeNil(), "Cannot get cilium pods") 226 for _, pod := range ciliumPods { 227 service := kubectl.CiliumExecMustSucceed(context.TODO(), pod, fmt.Sprintf("cilium-dbg service list | grep \" %s:\"", svcIP), "Cannot retrieve services on cilium pod") 228 service.ExpectContains("ClusterIP", "Original service is not present in the cilium service list") 229 } 230 231 By("Checking traffic goes to both backends") 232 testCases := []lrpTestCase{ 233 { 234 selector: "id=app1", 235 cmd: curl4TCP, 236 }, 237 { 238 selector: "id=app2", 239 cmd: curl4TCP, 240 }, 241 { 242 selector: "id=app1", 243 cmd: curl4UDP, 244 }, 245 { 246 selector: "id=app2", 247 cmd: curl4UDP, 248 }, 249 } 250 251 var wg sync.WaitGroup 252 for _, tc := range testCases { 253 pods, err := kubectl.GetPodNames(helpers.DefaultNamespace, tc.selector) 254 Expect(err).Should(BeNil(), "cannot retrieve pod names by filter %q", tc.selector) 255 Expect(len(pods)).Should(BeNumerically(">", 0), "no pod exists by filter %q", tc.selector) 256 for _, pod := range pods { 257 wg.Add(1) 258 go func(tc lrpTestCase, pod string) { 259 defer GinkgoRecover() 260 defer wg.Done() 261 want := []string{be1Name, be2Name} 262 be1Found := false 263 be2Found := false 264 Eventually(func() bool { 265 res := kubectl.ExecPodCmd(helpers.DefaultNamespace, pod, tc.cmd) 266 ExpectWithOffset(1, res).Should(helpers.CMDSuccess(), 267 "%s failed in %s pod", tc.cmd, pod) 268 be1Found = be1Found || strings.Contains(res.Stdout(), want[0]) 269 be2Found = be2Found || strings.Contains(res.Stdout(), want[1]) 270 return be1Found && be2Found 271 }, 30*time.Second, 1*time.Second).Should(BeTrue(), "assertion fails for test case: %v", tc) 272 }(tc, pod) 273 } 274 } 275 wg.Wait() 276 }) 277 }) 278 279 })