diff --git a/pkg/controller/security/manager_test.go b/pkg/controller/security/manager_test.go index 989878197..1c46c897d 100644 --- a/pkg/controller/security/manager_test.go +++ b/pkg/controller/security/manager_test.go @@ -24,6 +24,7 @@ import ( "github.com/agiledragon/gomonkey/v2" "github.com/stretchr/testify/assert" "istio.io/istio/pkg/security" + "istio.io/istio/pkg/test/util/retry" camock "kmesh.net/kmesh/pkg/controller/security/mock" ) @@ -124,7 +125,7 @@ func runTestCertRotate(t *testing.T) { for { secretManager.certsCache.mu.RLock() cert2 := secretManager.certsCache.certs[identity1] - if cert2 != nil && cert2.cert.CreatedTime != oldCert.CreatedTime { + if cert2 != nil && cert2.cert != nil && cert2.cert.CreatedTime != oldCert.CreatedTime { newCert = *cert2.cert secretManager.certsCache.mu.RUnlock() break @@ -160,13 +161,32 @@ func runTestretryFetchCert(t *testing.T) { go secretManager.Run(stopCh) identity := "identity" - identity1 := "identity1" secretManager.SendCertRequest(identity, ADD) time.Sleep(100 * time.Millisecond) patches2.Reset() - secretManager.SendCertRequest(identity1, RETRY) - time.Sleep(2000 * time.Millisecond) - assert.NotNil(t, secretManager.GetCert(identity).cert) + + secretManager.SendCertRequest(identity, RETRY) + + err = retry.UntilSuccess( + func() error { + cert := secretManager.GetCert(identity) + if cert != nil { + secretManager.certsCache.mu.RLock() + hasCert := cert.cert != nil + secretManager.certsCache.mu.RUnlock() + if hasCert { + return nil + } + } + return fmt.Errorf("cert not found for identity %s", identity) + }, + retry.Delay(100*time.Millisecond), + retry.Timeout(6*time.Second), + ) + + if err != nil { + t.Errorf("Failed to fetch cert after retry: %v", err) + } close(stopCh) } diff --git a/pkg/status/status_server.go b/pkg/status/status_server.go index 9486d4ce5..bf6c52a49 100644 --- a/pkg/status/status_server.go +++ b/pkg/status/status_server.go @@ -23,6 +23,7 @@ import ( "io" "net/http" "net/http/pprof" + "sort" "strconv" "time" @@ -568,6 +569,16 @@ func (s *Server) StopServer() error { } func printWorkloadDump(w http.ResponseWriter, wd WorkloadDump) { + sort.Slice(wd.Workloads, func(i, j int) bool { + return wd.Workloads[i].Name < wd.Workloads[j].Name + }) + sort.Slice(wd.Services, func(i, j int) bool { + return wd.Services[i].Name < wd.Services[j].Name + }) + sort.Slice(wd.Policies, func(i, j int) bool { + return wd.Policies[i].Name < wd.Policies[j].Name + }) + data, err := json.MarshalIndent(wd, "", " ") if err != nil { log.Errorf("Failed to marshal WorkloadDump: %v", err) diff --git a/pkg/status/status_server_test.go b/pkg/status/status_server_test.go index dea4b7313..39141730e 100644 --- a/pkg/status/status_server_test.go +++ b/pkg/status/status_server_test.go @@ -133,11 +133,11 @@ func TestServer_setLoggerLevel(t *testing.T) { } } -func TestServer_configDumpWorkload(t *testing.T) { - w1 := &workloadapi.Workload{ +func buildWorkload(name string) *workloadapi.Workload { + return &workloadapi.Workload{ Uid: "cluster0//Pod/ns/name", Namespace: "ns", - Name: "name", + Name: name, Addresses: [][]byte{netip.AddrFrom4([4]byte{1, 2, 3, 4}).AsSlice()}, Network: "testnetwork", CanonicalName: "foo", @@ -173,10 +173,13 @@ func TestServer_configDumpWorkload(t *testing.T) { }, }, } - svc := &workloadapi.Service{ - Name: "svc", +} + +func buildService(name, hostname string) *workloadapi.Service { + return &workloadapi.Service{ + Name: name, Namespace: "ns", - Hostname: "hostname", + Hostname: hostname, Ports: []*workloadapi.Port{ { ServicePort: 80, @@ -199,6 +202,12 @@ func TestServer_configDumpWorkload(t *testing.T) { }, }, }} +} + +func TestServer_configDumpWorkload(t *testing.T) { + w := buildWorkload("name") + svc := buildService("svc", "hostname") + policy := &security.Authorization{ Name: "policy", Namespace: "ns", @@ -207,7 +216,7 @@ func TestServer_configDumpWorkload(t *testing.T) { } fakeWorkloadCache := cache.NewWorkloadCache() fakeServiceCache := cache.NewServiceCache() - fakeWorkloadCache.AddOrUpdateWorkload(w1) + fakeWorkloadCache.AddOrUpdateWorkload(w) fakeServiceCache.AddOrUpdateService(svc) fakeAuth := auth.NewRbac(fakeWorkloadCache) fakeAuth.UpdatePolicy(policy) @@ -225,20 +234,123 @@ func TestServer_configDumpWorkload(t *testing.T) { } // Create a new HTTP request and response - req := httptest.NewRequest(http.MethodGet, "/configDumpWorkload", nil) - w := httptest.NewRecorder() + req1 := httptest.NewRequest(http.MethodGet, "/configDumpWorkload", nil) + w1 := httptest.NewRecorder() // Call the configDumpWorkload function - server.configDumpWorkload(w, req) + server.configDumpWorkload(w1, req1) // Check the response status code - if w.Code != http.StatusOK { - t.Errorf("Expected status code %d, but got %d", http.StatusOK, w.Code) + if w1.Code != http.StatusOK { + t.Errorf("Expected status code %d, but got %d", http.StatusOK, w1.Code) + } + + util.RefreshGoldenFile(t, w1.Body.Bytes(), "./testdata/workload_configdump.json") + + util.CompareContent(t, w1.Body.Bytes(), "./testdata/workload_configdump.json") + + fakeWorkloadCache = cache.NewWorkloadCache() + fakeServiceCache = cache.NewServiceCache() + + workloads := []*workloadapi.Workload{} + services := []*workloadapi.Service{} + + for i := 0; i < 10; i++ { + w := buildWorkload(fmt.Sprintf("workload-%d", i)) + w.Uid = fmt.Sprintf("cluster0//Pod/ns/workload-%d", i) + workloads = append(workloads, w) + svc := buildService(fmt.Sprintf("service-%d", i), fmt.Sprintf("hostname-%d", i)) + services = append(services, svc) + + fakeWorkloadCache.AddOrUpdateWorkload(w) + fakeServiceCache.AddOrUpdateService(svc) } - util.RefreshGoldenFile(t, w.Body.Bytes(), "./testdata/workload_configdump.json") + // Create a new HTTP response + w2 := httptest.NewRecorder() + + server = &Server{ + xdsClient: &controller.XdsClient{ + WorkloadController: &workload.Controller{ + Processor: &workload.Processor{ + WorkloadCache: fakeWorkloadCache, + ServiceCache: fakeServiceCache, + }, + Rbac: fakeAuth, + }, + }, + } + + // Call the configDumpWorkload function + server.configDumpWorkload(w2, req1) + + // Check the response status code + if w2.Code != http.StatusOK { + t.Errorf("Expected status code %d, but got %d", http.StatusOK, w2.Code) + } + + util.RefreshGoldenFile(t, w2.Body.Bytes(), "./testdata/workload_configdump_original_sorted.json") + util.CompareContent(t, w2.Body.Bytes(), "./testdata/workload_configdump_original_sorted.json") + + fakeWorkloadCache = cache.NewWorkloadCache() + fakeServiceCache = cache.NewServiceCache() + // Modify workloads and services properties + for i := 0; i < 5; i++ { // Modify first 5 items + // Modify workload properties + w := buildWorkload(fmt.Sprintf("workload-%d-modified", i)) + w.ClusterId = "cluster1" // Changed cluster + w.Uid = fmt.Sprintf("cluster1//Pod/ns/workload-%d-modified", i) + w.Status = workloadapi.WorkloadStatus_UNHEALTHY + + workloads[i] = w + // Modify service properties + svc := buildService(fmt.Sprintf("service-%d-modified", i), fmt.Sprintf("hostname-%d-modified", i)) + // Modify service ports + svc.Ports = []*workloadapi.Port{ + { + ServicePort: 90, + TargetPort: 9090, + }, + { + ServicePort: 91, + TargetPort: 0, + }, + { + ServicePort: 92, + TargetPort: 0, + }, + } + services[i] = svc + } + for _, w := range workloads { + fakeWorkloadCache.AddOrUpdateWorkload(w) + } + for _, svc := range services { + fakeServiceCache.AddOrUpdateService(svc) + } + + w3 := httptest.NewRecorder() + + server = &Server{ + xdsClient: &controller.XdsClient{ + WorkloadController: &workload.Controller{ + Processor: &workload.Processor{ + WorkloadCache: fakeWorkloadCache, + ServiceCache: fakeServiceCache, + }, + Rbac: fakeAuth, + }, + }, + } + + server.configDumpWorkload(w3, req1) + + if w3.Code != http.StatusOK { + t.Errorf("Expected status code %d, but got %d", http.StatusOK, w3.Code) + } - util.CompareContent(t, w.Body.Bytes(), "./testdata/workload_configdump.json") + util.RefreshGoldenFile(t, w3.Body.Bytes(), "./testdata/workload_configdump_modified_sorted.json") + util.CompareContent(t, w3.Body.Bytes(), "./testdata/workload_configdump_modified_sorted.json") } func TestServer_dumpWorkloadBpfMap(t *testing.T) { diff --git a/pkg/status/testdata/workload_configdump_modified_sorted.json b/pkg/status/testdata/workload_configdump_modified_sorted.json new file mode 100644 index 000000000..c983043b5 --- /dev/null +++ b/pkg/status/testdata/workload_configdump_modified_sorted.json @@ -0,0 +1,495 @@ +{ + "workloads": [ + { + "uid": "cluster1//Pod/ns/workload-0-modified", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-0-modified", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster1", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "UNHEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster1//Pod/ns/workload-1-modified", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-1-modified", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster1", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "UNHEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster1//Pod/ns/workload-2-modified", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-2-modified", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster1", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "UNHEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster1//Pod/ns/workload-3-modified", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-3-modified", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster1", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "UNHEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster1//Pod/ns/workload-4-modified", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-4-modified", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster1", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "UNHEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-5", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-5", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-6", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-6", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-7", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-7", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-8", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-8", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-9", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-9", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + } + ], + "services": [ + { + "name": "service-0-modified", + "namespace": "ns", + "hostname": "hostname-0-modified", + "vips": [], + "ports": [ + { + "service_port": 90, + "target_port": 9090 + }, + { + "service_port": 91 + }, + { + "service_port": 92 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-1-modified", + "namespace": "ns", + "hostname": "hostname-1-modified", + "vips": [], + "ports": [ + { + "service_port": 90, + "target_port": 9090 + }, + { + "service_port": 91 + }, + { + "service_port": 92 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-2-modified", + "namespace": "ns", + "hostname": "hostname-2-modified", + "vips": [], + "ports": [ + { + "service_port": 90, + "target_port": 9090 + }, + { + "service_port": 91 + }, + { + "service_port": 92 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-3-modified", + "namespace": "ns", + "hostname": "hostname-3-modified", + "vips": [], + "ports": [ + { + "service_port": 90, + "target_port": 9090 + }, + { + "service_port": 91 + }, + { + "service_port": 92 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-4-modified", + "namespace": "ns", + "hostname": "hostname-4-modified", + "vips": [], + "ports": [ + { + "service_port": 90, + "target_port": 9090 + }, + { + "service_port": 91 + }, + { + "service_port": 92 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-5", + "namespace": "ns", + "hostname": "hostname-5", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-6", + "namespace": "ns", + "hostname": "hostname-6", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-7", + "namespace": "ns", + "hostname": "hostname-7", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-8", + "namespace": "ns", + "hostname": "hostname-8", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-9", + "namespace": "ns", + "hostname": "hostname-9", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + } + ], + "policies": [ + { + "name": "policy", + "namespace": "ns", + "scope": "GLOBAL", + "action": "ALLOW", + "rules": null + } + ] +} \ No newline at end of file diff --git a/pkg/status/testdata/workload_configdump_original_sorted.json b/pkg/status/testdata/workload_configdump_original_sorted.json new file mode 100644 index 000000000..40834898f --- /dev/null +++ b/pkg/status/testdata/workload_configdump_original_sorted.json @@ -0,0 +1,495 @@ +{ + "workloads": [ + { + "uid": "cluster0//Pod/ns/workload-0", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-0", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-1", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-1", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-2", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-2", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-3", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-3", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-4", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-4", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-5", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-5", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-6", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-6", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-7", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-7", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-8", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-8", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + }, + { + "uid": "cluster0//Pod/ns/workload-9", + "addresses": [ + "1.2.3.4" + ], + "waypoint": "testnetwork/192.168.1.10", + "protocol": "NONE", + "name": "workload-9", + "namespace": "ns", + "serviceAccount": "", + "workloadName": "name", + "workloadType": "POD", + "canonicalName": "foo", + "canonicalRevision": "latest", + "clusterId": "cluster0", + "locality": {}, + "node": "", + "network": "testnetwork", + "status": "HEALTHY", + "applicationTunnel": { + "protocol": "" + }, + "services": [ + "ns/hostname" + ] + } + ], + "services": [ + { + "name": "service-0", + "namespace": "ns", + "hostname": "hostname-0", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-1", + "namespace": "ns", + "hostname": "hostname-1", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-2", + "namespace": "ns", + "hostname": "hostname-2", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-3", + "namespace": "ns", + "hostname": "hostname-3", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-4", + "namespace": "ns", + "hostname": "hostname-4", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-5", + "namespace": "ns", + "hostname": "hostname-5", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-6", + "namespace": "ns", + "hostname": "hostname-6", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-7", + "namespace": "ns", + "hostname": "hostname-7", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-8", + "namespace": "ns", + "hostname": "hostname-8", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + }, + { + "name": "service-9", + "namespace": "ns", + "hostname": "hostname-9", + "vips": [], + "ports": [ + { + "service_port": 80, + "target_port": 8080 + }, + { + "service_port": 81 + }, + { + "service_port": 82 + } + ], + "loadBalancer": null, + "waypoint": { + "destination": "testnetwork/192.168.1.11" + } + } + ], + "policies": [ + { + "name": "policy", + "namespace": "ns", + "scope": "GLOBAL", + "action": "ALLOW", + "rules": null + } + ] +} \ No newline at end of file