istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/xds/server_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 xds 16 17 import ( 18 "testing" 19 20 discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" 21 22 "istio.io/istio/pkg/model" 23 ) 24 25 type TestProxy struct { 26 WatchedResources map[string]*WatchedResource 27 } 28 29 func (p *TestProxy) DeleteWatchedResource(url string) { 30 delete(p.WatchedResources, url) 31 } 32 33 func (p *TestProxy) GetWatchedResource(url string) *WatchedResource { 34 return p.WatchedResources[url] 35 } 36 37 func (p *TestProxy) NewWatchedResource(url string, names []string) { 38 p.WatchedResources[url] = &WatchedResource{ResourceNames: names} 39 } 40 41 func (p *TestProxy) UpdateWatchedResource(url string, f func(*WatchedResource) *WatchedResource) { 42 p.WatchedResources[url] = f(p.WatchedResources[url]) 43 } 44 45 func (p *TestProxy) GetID() string { 46 return "" 47 } 48 49 func TestShouldRespond(t *testing.T) { 50 tests := []struct { 51 name string 52 proxy *TestProxy 53 request *discovery.DiscoveryRequest 54 response bool 55 }{ 56 { 57 name: "initial request", 58 proxy: &TestProxy{ 59 WatchedResources: map[string]*WatchedResource{}, 60 }, 61 request: &discovery.DiscoveryRequest{ 62 TypeUrl: model.ClusterType, 63 }, 64 response: true, 65 }, 66 { 67 name: "ack", 68 proxy: &TestProxy{ 69 WatchedResources: map[string]*WatchedResource{ 70 model.ClusterType: { 71 NonceSent: "nonce", 72 }, 73 }, 74 }, 75 request: &discovery.DiscoveryRequest{ 76 TypeUrl: model.ClusterType, 77 VersionInfo: "v1", 78 ResponseNonce: "nonce", 79 }, 80 response: false, 81 }, 82 { 83 name: "ack forced", 84 proxy: &TestProxy{ 85 WatchedResources: map[string]*WatchedResource{ 86 model.EndpointType: { 87 NonceSent: "nonce", 88 AlwaysRespond: true, 89 ResourceNames: []string{"my-resource"}, 90 }, 91 }, 92 }, 93 request: &discovery.DiscoveryRequest{ 94 TypeUrl: model.EndpointType, 95 VersionInfo: "v1", 96 ResponseNonce: "nonce", 97 ResourceNames: []string{"my-resource"}, 98 }, 99 response: true, 100 }, 101 { 102 name: "nack", 103 proxy: &TestProxy{ 104 WatchedResources: map[string]*WatchedResource{ 105 model.ClusterType: { 106 NonceSent: "nonce", 107 }, 108 }, 109 }, 110 request: &discovery.DiscoveryRequest{ 111 TypeUrl: model.ClusterType, 112 VersionInfo: "v1", 113 ResponseNonce: "stale nonce", 114 }, 115 response: false, 116 }, 117 { 118 name: "reconnect", 119 proxy: &TestProxy{ 120 WatchedResources: map[string]*WatchedResource{}, 121 }, 122 request: &discovery.DiscoveryRequest{ 123 TypeUrl: model.ClusterType, 124 VersionInfo: "v1", 125 ResponseNonce: "reconnect nonce", 126 }, 127 response: true, 128 }, 129 { 130 name: "resources change", 131 proxy: &TestProxy{ 132 WatchedResources: map[string]*WatchedResource{ 133 model.EndpointType: { 134 NonceSent: "nonce", 135 ResourceNames: []string{"cluster1"}, 136 }, 137 }, 138 }, 139 request: &discovery.DiscoveryRequest{ 140 TypeUrl: model.EndpointType, 141 VersionInfo: "v1", 142 ResponseNonce: "nonce", 143 ResourceNames: []string{"cluster1", "cluster2"}, 144 }, 145 response: true, 146 }, 147 { 148 name: "ack with same resources", 149 proxy: &TestProxy{ 150 WatchedResources: map[string]*WatchedResource{ 151 model.EndpointType: { 152 NonceSent: "nonce", 153 ResourceNames: []string{"cluster2", "cluster1"}, 154 }, 155 }, 156 }, 157 request: &discovery.DiscoveryRequest{ 158 TypeUrl: model.EndpointType, 159 VersionInfo: "v1", 160 ResponseNonce: "nonce", 161 ResourceNames: []string{"cluster1", "cluster2"}, 162 }, 163 response: false, 164 }, 165 { 166 name: "unsubscribe EDS", 167 proxy: &TestProxy{ 168 WatchedResources: map[string]*WatchedResource{ 169 model.EndpointType: { 170 NonceSent: "nonce", 171 ResourceNames: []string{"cluster2", "cluster1"}, 172 }, 173 }, 174 }, 175 request: &discovery.DiscoveryRequest{ 176 TypeUrl: model.EndpointType, 177 VersionInfo: "v1", 178 ResponseNonce: "nonce", 179 ResourceNames: []string{}, 180 }, 181 response: false, 182 }, 183 } 184 185 for _, tt := range tests { 186 t.Run(tt.name, func(t *testing.T) { 187 if response, _ := ShouldRespond(tt.proxy, "test", tt.request); response != tt.response { 188 t.Fatalf("Unexpected value for response, expected %v, got %v", tt.response, response) 189 } 190 if tt.name != "reconnect" && tt.response { 191 if tt.proxy.WatchedResources[tt.request.TypeUrl].NonceAcked != tt.request.ResponseNonce { 192 t.Fatalf("Version & Nonce not updated properly") 193 } 194 } 195 }) 196 } 197 }