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  }