istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/serviceregistry/kube/controller/ambient/ambientindex_workloadentry_test.go (about) 1 // Copyright Istio Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package ambient 16 17 import ( 18 "net/netip" 19 "testing" 20 21 corev1 "k8s.io/api/core/v1" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 24 "istio.io/istio/pilot/pkg/features" 25 "istio.io/istio/pilot/pkg/model" 26 "istio.io/istio/pkg/config/constants" 27 "istio.io/istio/pkg/config/schema/gvk" 28 "istio.io/istio/pkg/test" 29 "istio.io/istio/pkg/test/util/assert" 30 "istio.io/istio/pkg/workloadapi" 31 ) 32 33 func TestAmbientIndex_WorkloadEntries(t *testing.T) { 34 s := newAmbientTestServer(t, testC, testNW) 35 36 s.addWorkloadEntries(t, "127.0.0.1", "name1", "sa1", map[string]string{"app": "a"}) 37 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "name1") 38 s.assertEvent(t, s.wleXdsName("name1")) 39 40 s.addWorkloadEntries(t, "127.0.0.2", "name2", "sa2", map[string]string{"app": "a", "other": "label"}) 41 s.addWorkloadEntries(t, "127.0.0.3", "name3", "sa3", map[string]string{"app": "other"}) 42 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name3") 43 s.assertWorkloads(t, s.addrXdsName("127.0.0.1"), workloadapi.WorkloadStatus_HEALTHY, "name1") 44 s.assertWorkloads(t, s.addrXdsName("127.0.0.2"), workloadapi.WorkloadStatus_HEALTHY, "name2") 45 assert.Equal(t, s.lookup(s.addrXdsName("127.0.0.3")), []model.AddressInfo{{ 46 Address: &workloadapi.Address{ 47 Type: &workloadapi.Address_Workload{ 48 Workload: &workloadapi.Workload{ 49 Uid: s.wleXdsName("name3"), 50 Name: "name3", 51 Namespace: testNS, 52 Network: testNW, 53 Addresses: [][]byte{parseIP("127.0.0.3")}, 54 ServiceAccount: "sa3", 55 Node: "", 56 CanonicalName: "other", 57 CanonicalRevision: "latest", 58 WorkloadType: workloadapi.WorkloadType_POD, 59 WorkloadName: "name3", 60 ClusterId: testC, 61 }, 62 }, 63 }, 64 }}) 65 s.assertEvent(t, s.wleXdsName("name2")) 66 s.assertEvent(t, s.wleXdsName("name3")) 67 68 // Non-existent IP should have no response 69 s.assertWorkloads(t, s.addrXdsName("10.0.0.1"), workloadapi.WorkloadStatus_HEALTHY) 70 s.clearEvents() 71 72 s.addService(t, "svc1", map[string]string{}, // labels 73 map[string]string{}, // annotations 74 []int32{80}, 75 map[string]string{"app": "a"}, // selector 76 "10.0.0.1", 77 ) 78 // Service shouldn't change workload list 79 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name3") 80 s.assertWorkloads(t, s.addrXdsName("127.0.0.1"), workloadapi.WorkloadStatus_HEALTHY, "name1") 81 // Now we should be able to look up a VIP as well 82 s.assertWorkloads(t, s.addrXdsName("10.0.0.1"), workloadapi.WorkloadStatus_HEALTHY, "name1", "name2") 83 // We should get an event for the two WEs and the selecting service impacted 84 s.assertEvent(t, s.wleXdsName("name1"), s.wleXdsName("name2"), s.svcXdsName("svc1")) 85 86 // Add a new pod to the service, we should see it 87 s.addWorkloadEntries(t, "127.0.0.4", "name4", "sa4", map[string]string{"app": "a"}) 88 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name3", "name4") 89 s.assertWorkloads(t, s.addrXdsName("10.0.0.1"), workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name4") 90 s.assertEvent(t, s.wleXdsName("name4")) 91 92 // Delete it, should remove from the Service as well 93 s.deleteWorkloadEntry(t, "name4") 94 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name3") 95 s.assertWorkloads(t, s.addrXdsName("10.0.0.1"), workloadapi.WorkloadStatus_HEALTHY, "name1", "name2") 96 s.assertWorkloads(t, s.addrXdsName("127.0.0.4"), workloadapi.WorkloadStatus_HEALTHY) // Should not be accessible anymore 97 s.assertEvent(t, s.wleXdsName("name4")) 98 99 s.clearEvents() 100 // Update Service to have a more restrictive label selector 101 s.addService(t, "svc1", map[string]string{}, // labels 102 map[string]string{}, // annotations 103 []int32{80}, 104 map[string]string{"app": "a", "other": "label"}, // selector 105 "10.0.0.1", 106 ) 107 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name3") 108 s.assertWorkloads(t, s.addrXdsName("10.0.0.1"), workloadapi.WorkloadStatus_HEALTHY, "name2") 109 s.assertEvent(t, s.wleXdsName("name1")) 110 111 // Update an existing WE into the service 112 s.addWorkloadEntries(t, "127.0.0.3", "name3", "sa3", map[string]string{"app": "a", "other": "label"}) 113 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name3") 114 s.assertWorkloads(t, s.addrXdsName("10.0.0.1"), workloadapi.WorkloadStatus_HEALTHY, "name2", "name3") 115 s.assertEvent(t, s.wleXdsName("name3")) 116 117 // And remove it again from the service VIP mapping by changing its label to not match the service svc1.ns1 selector 118 s.addWorkloadEntries(t, "127.0.0.3", "name3", "sa3", map[string]string{"app": "a"}) 119 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name3") 120 s.assertWorkloads(t, s.addrXdsName("10.0.0.1"), workloadapi.WorkloadStatus_HEALTHY, "name2") 121 s.assertEvent(t, s.wleXdsName("name3")) 122 123 // Delete the service entirely 124 s.sc.Delete("svc1", "ns1") 125 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name3") 126 s.assertWorkloads(t, s.addrXdsName("10.0.0.1"), workloadapi.WorkloadStatus_HEALTHY) 127 s.assertEvent(t, s.wleXdsName("name2"), s.svcXdsName("svc1")) 128 129 // Add a waypoint proxy pod for namespace 130 s.addPods(t, "127.0.0.200", "waypoint-ns-pod", "namespace-wide", 131 map[string]string{ 132 constants.ManagedGatewayLabel: constants.ManagedGatewayMeshControllerLabel, 133 constants.GatewayNameLabel: "waypoint-ns", 134 }, nil, true, corev1.PodRunning) 135 s.assertAddresses(t, "", "name1", "name2", "name3", "waypoint-ns-pod") 136 s.assertEvent(t, s.podXdsName("waypoint-ns-pod")) 137 s.addWaypoint(t, "10.0.0.2", "waypoint-ns", constants.AllTraffic, true) 138 s.ns.CreateOrUpdate(&corev1.Namespace{ 139 ObjectMeta: metav1.ObjectMeta{ 140 Name: testNS, 141 Labels: map[string]string{ 142 constants.AmbientUseWaypointLabel: "waypoint-ns", 143 }, 144 }, 145 }) 146 // All these workloads updated, so push them 147 s.assertEvent(t, 148 s.wleXdsName("name1"), 149 s.wleXdsName("name2"), 150 s.wleXdsName("name3"), 151 ) 152 // create the waypoint service 153 s.addService(t, "waypoint-ns", 154 map[string]string{constants.ManagedGatewayLabel: constants.ManagedGatewayMeshControllerLabel}, // labels 155 map[string]string{}, // annotations 156 []int32{80}, 157 map[string]string{constants.GatewayNameLabel: "waypoint-ns"}, // selector 158 "10.0.0.2", 159 ) 160 s.assertEvent(t, s.podXdsName("waypoint-ns-pod"), 161 s.svcXdsName("waypoint-ns"), 162 ) 163 s.assertAddresses(t, "", "name1", "name2", "name3", "waypoint-ns", "waypoint-ns-pod") 164 // We should now see the waypoint service IP 165 assert.Equal(t, 166 s.lookup(s.addrXdsName("127.0.0.3"))[0].Address.GetWorkload().Waypoint.GetAddress().Address, 167 netip.MustParseAddr("10.0.0.2").AsSlice()) 168 169 // Add another one, expect the same result 170 s.addPods(t, "127.0.0.201", "waypoint2-ns-pod", "namespace-wide", 171 map[string]string{ 172 constants.ManagedGatewayLabel: constants.ManagedGatewayMeshControllerLabel, 173 constants.GatewayNameLabel: "waypoint-ns", 174 }, nil, true, corev1.PodRunning) 175 s.assertAddresses(t, "", "name1", "name2", "name3", "waypoint-ns", "waypoint-ns-pod", "waypoint2-ns-pod") 176 // all these workloads already have a waypoint, only expect the new waypoint pod 177 s.assertEvent(t, s.podXdsName("waypoint2-ns-pod")) 178 179 // Waypoints do not have waypoints 180 assert.Equal(t, 181 s.lookup(s.addrXdsName("127.0.0.200"))[0].Address.GetWorkload().GetWaypoint(), 182 nil) 183 184 s.addService(t, "svc1", 185 map[string]string{}, // labels 186 map[string]string{}, // annotations 187 []int32{80}, 188 map[string]string{"app": "a"}, // selector 189 "10.0.0.1", 190 ) 191 s.assertWorkloads(t, s.addrXdsName("10.0.0.1"), workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name3") 192 // Send update for the workloads as well... 193 s.assertEvent(t, s.wleXdsName("name1"), 194 s.wleXdsName("name2"), 195 s.wleXdsName("name3"), 196 s.svcXdsName("svc1")) 197 198 // Delete a waypoint pod 199 s.deletePod(t, "waypoint2-ns-pod") 200 s.assertEvent(t, s.podXdsName("waypoint2-ns-pod")) // only expect event on the single waypoint pod 201 202 // Adding a new WorkloadEntry should also see the waypoint 203 s.addWorkloadEntries(t, "127.0.0.6", "name6", "sa6", map[string]string{"app": "a"}) 204 s.assertEvent(t, s.wleXdsName("name6")) 205 assert.Equal(t, 206 s.lookup(s.addrXdsName("127.0.0.6"))[0].Address.GetWorkload().Waypoint.GetAddress().Address, 207 netip.MustParseAddr("10.0.0.2").AsSlice()) 208 209 s.deleteWorkloadEntry(t, "name6") 210 s.assertEvent(t, s.wleXdsName("name6")) 211 212 s.deleteWaypoint(t, "waypoint-ns") 213 s.ns.Update(&corev1.Namespace{ 214 ObjectMeta: metav1.ObjectMeta{ 215 Name: testNS, 216 Labels: map[string]string{ 217 constants.AmbientUseWaypointLabel: "none", 218 }, 219 }, 220 }) 221 // all affected addresses with the waypoint should be updated 222 s.assertEvent(t, 223 s.svcXdsName("svc1"), 224 s.svcXdsName("waypoint-ns"), 225 s.wleXdsName("name1"), 226 s.wleXdsName("name2"), 227 s.wleXdsName("name3")) 228 229 s.deleteService(t, "waypoint-ns") 230 s.assertEvent(t, s.podXdsName("waypoint-ns-pod"), 231 s.svcXdsName("waypoint-ns")) 232 233 s.deleteWorkloadEntry(t, "name3") 234 s.assertEvent(t, s.wleXdsName("name3")) 235 s.deleteWorkloadEntry(t, "name2") 236 s.assertEvent(t, s.wleXdsName("name2")) 237 238 s.addPolicy(t, "global", "istio-system", nil, gvk.AuthorizationPolicy, nil) 239 s.addPolicy(t, "namespace", "default", nil, gvk.AuthorizationPolicy, nil) 240 assert.Equal(t, 241 s.lookup(s.addrXdsName("127.0.0.1"))[0].GetWorkload().GetAuthorizationPolicies(), 242 nil) 243 s.clearEvents() 244 245 s.addPolicy(t, "selector", "ns1", map[string]string{"app": "a"}, gvk.AuthorizationPolicy, nil) 246 s.assertEvent(t, s.wleXdsName("name1")) 247 assert.Equal(t, 248 s.lookup(s.addrXdsName("127.0.0.1"))[0].GetWorkload().GetAuthorizationPolicies(), 249 []string{"ns1/selector"}) 250 251 // WorkloadEntry not in policy 252 s.addWorkloadEntries(t, "127.0.0.2", "name2", "sa2", map[string]string{"app": "not-a"}) 253 s.assertEvent(t, s.wleXdsName("name2")) 254 assert.Equal(t, 255 s.lookup(s.addrXdsName("127.0.0.2"))[0].GetWorkload().GetAuthorizationPolicies(), 256 nil) 257 258 // Add it to the policy by updating its selector 259 s.addWorkloadEntries(t, "127.0.0.2", "name2", "sa2", map[string]string{"app": "a"}) 260 s.assertEvent(t, s.wleXdsName("name2")) 261 assert.Equal(t, 262 s.lookup(s.addrXdsName("127.0.0.2"))[0].GetWorkload().GetAuthorizationPolicies(), 263 []string{"ns1/selector"}) 264 265 s.addPolicy(t, "global-selector", "istio-system", map[string]string{"app": "a"}, gvk.AuthorizationPolicy, nil) 266 s.assertEvent(t, s.wleXdsName("name1"), s.wleXdsName("name2")) 267 assert.Equal(t, 268 s.lookup(s.addrXdsName("127.0.0.1"))[0].GetWorkload().GetAuthorizationPolicies(), 269 []string{"istio-system/global-selector", "ns1/selector"}) 270 271 // Update selector to not select 272 s.addPolicy(t, "global-selector", "istio-system", map[string]string{"app": "not-a"}, gvk.AuthorizationPolicy, nil) 273 s.assertEvent(t, s.wleXdsName("name1"), s.wleXdsName("name2")) 274 assert.Equal(t, 275 s.lookup(s.addrXdsName("127.0.0.1"))[0].GetWorkload().GetAuthorizationPolicies(), 276 []string{"ns1/selector"}) 277 278 s.authz.Delete("selector", "ns1") 279 s.assertEvent(t, s.wleXdsName("name1"), s.wleXdsName("name2")) 280 assert.Equal(t, 281 s.lookup(s.addrXdsName("127.0.0.1"))[0].GetWorkload().GetAuthorizationPolicies(), 282 nil) 283 } 284 285 func TestAmbientIndex_EmptyAddrWorkloadEntries(t *testing.T) { 286 s := newAmbientTestServer(t, testC, testNW) 287 s.addWorkloadEntries(t, "", "emptyaddr1", "sa1", map[string]string{"app": "a"}) 288 s.assertEvent(t, s.wleXdsName("emptyaddr1")) 289 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "emptyaddr1") 290 291 s.addWorkloadEntries(t, "", "emptyaddr2", "sa1", map[string]string{"app": "a"}) 292 s.assertEvent(t, s.wleXdsName("emptyaddr2")) 293 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "emptyaddr1", "emptyaddr2") 294 295 // ensure we stored and can fetch both; neither was blown away 296 assert.Equal(t, 297 s.lookup(s.wleXdsName("emptyaddr1"))[0].GetWorkload().GetName(), 298 "emptyaddr1") // can lookup this workload by name 299 assert.Equal(t, 300 s.lookup(s.wleXdsName("emptyaddr2"))[0].GetWorkload().GetName(), 301 "emptyaddr2") // can lookup this workload by name 302 303 assert.Equal(t, 304 len(s.lookup(s.addrXdsName(""))), 305 0) // cannot lookup these workloads by address 306 } 307 308 func TestAmbientIndex_UpdateExistingWorkloadEntry(t *testing.T) { 309 s := newAmbientTestServer(t, testC, testNW) 310 s.addWorkloadEntries(t, "", "emptyaddr1", "sa1", map[string]string{"app": "a"}) 311 s.assertEvent(t, s.wleXdsName("emptyaddr1")) 312 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "emptyaddr1") 313 314 // update service account for existing WE and expect a new xds event 315 s.addWorkloadEntries(t, "", "emptyaddr1", "sa2", map[string]string{"app": "a"}) 316 s.assertEvent(t, s.wleXdsName("emptyaddr1")) 317 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "emptyaddr1") 318 } 319 320 func TestAmbientIndex_InlinedWorkloadEntries(t *testing.T) { 321 s := newAmbientTestServer(t, testC, testNW) 322 323 s.addServiceEntry(t, "se.istio.io", []string{"240.240.23.45"}, "se1", testNS, map[string]string{"app": "a"}, []string{"127.0.0.1", "127.0.0.2"}) 324 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "se1") 325 s.assertEvent(t, s.seIPXdsName("se1", "127.0.0.1"), s.seIPXdsName("se1", "127.0.0.2"), "ns1/se.istio.io") 326 327 s.addPolicy(t, "selector", "ns1", map[string]string{"app": "a"}, gvk.AuthorizationPolicy, nil) 328 s.assertEvent(t, s.seIPXdsName("se1", "127.0.0.1"), s.seIPXdsName("se1", "127.0.0.2")) 329 assert.Equal(t, 330 s.lookup(s.seIPXdsName("se1", "127.0.0.1"))[0].GetWorkload().GetAuthorizationPolicies(), 331 []string{"ns1/selector"}) 332 assert.Equal(t, 333 s.lookup(s.seIPXdsName("se1", "127.0.0.2"))[0].GetWorkload().GetAuthorizationPolicies(), 334 []string{"ns1/selector"}) 335 336 s.deletePolicy("selector", "ns1", gvk.AuthorizationPolicy) 337 s.assertEvent(t, s.seIPXdsName("se1", "127.0.0.1"), s.seIPXdsName("se1", "127.0.0.2")) 338 assert.Equal(t, 339 s.lookup(s.seIPXdsName("se1", "127.0.0.1"))[0].GetWorkload().GetAuthorizationPolicies(), 340 nil) 341 assert.Equal(t, 342 s.lookup(s.seIPXdsName("se1", "127.0.0.2"))[0].GetWorkload().GetAuthorizationPolicies(), 343 nil) 344 345 s.deleteServiceEntry(t, "se1", testNS) 346 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY) // Asserting no WE residual 347 } 348 349 func TestAmbientIndex_WorkloadEntries_DisableK8SServiceSelectWorkloadEntries(t *testing.T) { 350 test.SetForTest(t, &features.EnableK8SServiceSelectWorkloadEntries, false) 351 s := newAmbientTestServer(t, testC, testNW) 352 353 s.addWorkloadEntries(t, "127.0.0.1", "name1", "sa1", map[string]string{"app": "a"}) 354 s.assertEvent(t, s.wleXdsName("name1")) 355 s.addWorkloadEntries(t, "127.0.0.2", "name2", "sa2", map[string]string{"app": "a", "other": "label"}) 356 s.assertEvent(t, s.wleXdsName("name2")) 357 s.addWorkloadEntries(t, "127.0.0.3", "name3", "sa3", map[string]string{"app": "other"}) 358 s.assertEvent(t, s.wleXdsName("name3")) 359 s.addPods(t, "127.0.0.201", "pod1", "pod1", map[string]string{"app": "a"}, nil, true, corev1.PodRunning) 360 s.assertEvent(t, s.podXdsName("pod1")) 361 362 s.addService(t, "svc1", map[string]string{}, // labels 363 map[string]string{}, // annotations 364 []int32{80}, 365 map[string]string{"app": "a"}, // selector 366 "10.0.0.1", 367 ) 368 s.assertEvent(t, s.podXdsName("pod1"), "ns1/svc1.ns1.svc.company.com") 369 370 s.clearEvents() 371 s.assertWorkloads(t, "", workloadapi.WorkloadStatus_HEALTHY, "name1", "name2", "name3", "pod1") 372 373 // Setting the PILOT_ENABLE_K8S_SELECT_WORKLOAD_ENTRIES to false shouldn't include workload entries when 374 // looking up by the k8s service address 375 s.assertWorkloads(t, s.addrXdsName("10.0.0.1"), workloadapi.WorkloadStatus_HEALTHY, "pod1") 376 }