istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/model/network_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 model_test 16 17 import ( 18 "fmt" 19 "net" 20 "reflect" 21 "sync" 22 "testing" 23 "time" 24 25 "github.com/miekg/dns" 26 27 meshconfig "istio.io/api/mesh/v1alpha1" 28 "istio.io/istio/pilot/pkg/model" 29 "istio.io/istio/pilot/pkg/serviceregistry/memory" 30 "istio.io/istio/pilot/pkg/serviceregistry/util/xdsfake" 31 "istio.io/istio/pkg/config/mesh" 32 "istio.io/istio/pkg/test" 33 "istio.io/istio/pkg/test/scopes" 34 "istio.io/istio/pkg/test/util/assert" 35 "istio.io/istio/pkg/test/util/retry" 36 "istio.io/istio/pkg/util/sets" 37 ) 38 39 func TestGatewayHostnames(t *testing.T) { 40 test.SetForTest(t, &model.MinGatewayTTL, 30*time.Millisecond) 41 ttl := uint32(0) // second 42 43 gwHost := "test.gw.istio.io" 44 workingDNSServer := newFakeDNSServer(ttl, sets.New(gwHost)) 45 failingDNSServer := newFakeDNSServer(ttl, sets.NewWithLength[string](0)) 46 failingDNSServer.setFailure(true) 47 model.NetworkGatewayTestDNSServers = []string{ 48 // try resolving with the failing server first to make sure the next upstream is retried 49 failingDNSServer.Server.PacketConn.LocalAddr().String(), 50 workingDNSServer.Server.PacketConn.LocalAddr().String(), 51 } 52 t.Cleanup(func() { 53 errW := workingDNSServer.Shutdown() 54 errF := failingDNSServer.Shutdown() 55 if errW != nil || errF != nil { 56 t.Logf("failed shutting down fake dns servers") 57 } 58 }) 59 60 meshNetworks := mesh.NewFixedNetworksWatcher(nil) 61 xdsUpdater := xdsfake.NewFakeXDS() 62 env := &model.Environment{NetworksWatcher: meshNetworks, ServiceDiscovery: memory.NewServiceDiscovery()} 63 if err := env.InitNetworksManager(xdsUpdater); err != nil { 64 t.Fatal(err) 65 } 66 67 var gateways []model.NetworkGateway 68 t.Run("initial resolution", func(t *testing.T) { 69 meshNetworks.SetNetworks(&meshconfig.MeshNetworks{Networks: map[string]*meshconfig.Network{ 70 "nw0": {Gateways: []*meshconfig.Network_IstioNetworkGateway{{ 71 Gw: &meshconfig.Network_IstioNetworkGateway_Address{ 72 Address: gwHost, 73 }, 74 Port: 15443, 75 }}}, 76 }}) 77 xdsUpdater.WaitOrFail(t, "xds full") 78 gateways = env.NetworkManager.AllGateways() 79 // A and AAAA 80 if len(gateways) != 2 { 81 t.Fatalf("expected 2 IPs") 82 } 83 if gateways[0].Network != "nw0" || gateways[1].Network != "nw0" { 84 t.Fatalf("unexpected network: %v", gateways) 85 } 86 }) 87 88 t.Run("re-resolve after TTL", func(t *testing.T) { 89 // after the update, we should see the next gateway. Since TTL is low we don't know the exact IP, but we know it should change from 90 // the original 91 assert.EventuallyEqual(t, env.NetworkManager.AllGateways, gateways) 92 xdsUpdater.WaitOrFail(t, "xds full") 93 }) 94 95 workingDNSServer.setFailure(true) 96 gateways = env.NetworkManager.AllGateways() 97 t.Run("resolution failed", func(t *testing.T) { 98 xdsUpdater.AssertEmpty(t, 10*model.MinGatewayTTL) 99 // the gateways should remain 100 currentGateways := env.NetworkManager.AllGateways() 101 if len(currentGateways) == 0 || !reflect.DeepEqual(currentGateways, gateways) { 102 t.Fatalf("unexpected network: %v", currentGateways) 103 } 104 if !env.NetworkManager.IsMultiNetworkEnabled() { 105 t.Fatalf("multi network is not enabled") 106 } 107 }) 108 109 workingDNSServer.setFailure(false) 110 t.Run("resolution recovered", func(t *testing.T) { 111 // addresses should be updated 112 retry.UntilOrFail(t, func() bool { 113 return !reflect.DeepEqual(env.NetworkManager.AllGateways(), gateways) 114 }, retry.Timeout(10*model.MinGatewayTTL), retry.Delay(time.Millisecond*10)) 115 xdsUpdater.WaitOrFail(t, "xds full") 116 }) 117 118 workingDNSServer.setHosts(make(sets.String)) 119 t.Run("no answer", func(t *testing.T) { 120 assert.EventuallyEqual(t, func() int { 121 return len(env.NetworkManager.AllGateways()) 122 }, 0, retry.Timeout(10*model.MinGatewayTTL)) 123 xdsUpdater.WaitOrFail(t, "xds full") 124 if env.NetworkManager.IsMultiNetworkEnabled() { 125 t.Fatalf("multi network should not be enabled when there are no gateways") 126 } 127 }) 128 129 workingDNSServer.setHosts(sets.New(gwHost)) 130 t.Run("new answer", func(t *testing.T) { 131 retry.UntilOrFail(t, func() bool { 132 return len(env.NetworkManager.AllGateways()) != 0 && 133 !reflect.DeepEqual(env.NetworkManager.AllGateways(), gateways) 134 }, retry.Timeout(10*model.MinGatewayTTL), retry.Delay(time.Millisecond*10)) 135 xdsUpdater.WaitOrFail(t, "xds full") 136 }) 137 138 t.Run("forget", func(t *testing.T) { 139 meshNetworks.SetNetworks(nil) 140 xdsUpdater.WaitOrFail(t, "xds full") 141 if len(env.NetworkManager.AllGateways()) > 0 { 142 t.Fatalf("expected no gateways") 143 } 144 }) 145 } 146 147 type fakeDNSServer struct { 148 *dns.Server 149 ttl uint32 150 failure bool 151 152 mu sync.Mutex 153 // map fqdn hostname -> successful query count 154 hosts map[string]int 155 } 156 157 func newFakeDNSServer(ttl uint32, hosts sets.String) *fakeDNSServer { 158 var wg sync.WaitGroup 159 wg.Add(1) 160 s := &fakeDNSServer{ 161 Server: &dns.Server{Addr: ":0", Net: "udp", NotifyStartedFunc: wg.Done}, 162 ttl: ttl, 163 hosts: make(map[string]int, len(hosts)), 164 } 165 s.Handler = s 166 167 for k := range hosts { 168 s.hosts[dns.Fqdn(k)] = 0 169 } 170 171 go func() { 172 if err := s.ListenAndServe(); err != nil { 173 scopes.Framework.Errorf("fake dns server error: %v", err) 174 } 175 }() 176 wg.Wait() 177 return s 178 } 179 180 func (s *fakeDNSServer) ServeDNS(w dns.ResponseWriter, r *dns.Msg) { 181 s.mu.Lock() 182 defer s.mu.Unlock() 183 184 msg := (&dns.Msg{}).SetReply(r) 185 if s.failure { 186 msg.Rcode = dns.RcodeServerFailure 187 } else { 188 domain := msg.Question[0].Name 189 c, ok := s.hosts[domain] 190 if ok { 191 s.hosts[domain]++ 192 switch r.Question[0].Qtype { 193 case dns.TypeA: 194 msg.Answer = append(msg.Answer, &dns.A{ 195 Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: s.ttl}, 196 A: net.ParseIP(fmt.Sprintf("10.0.0.%d", c)), 197 }) 198 case dns.TypeAAAA: 199 // set a long TTL for AAAA 200 msg.Answer = append(msg.Answer, &dns.AAAA{ 201 Hdr: dns.RR_Header{Name: domain, Rrtype: dns.TypeAAAA, Class: dns.ClassINET, Ttl: s.ttl * 10}, 202 AAAA: net.ParseIP(fmt.Sprintf("fd00::%x", c)), 203 }) 204 // simulate behavior of some public/cloud DNS like Cloudflare or DigitalOcean 205 case dns.TypeANY: 206 msg.Rcode = dns.RcodeRefused 207 default: 208 msg.Rcode = dns.RcodeNotImplemented 209 } 210 } else { 211 msg.Rcode = dns.RcodeNameError 212 } 213 } 214 if err := w.WriteMsg(msg); err != nil { 215 scopes.Framework.Errorf("failed writing fake DNS response: %v", err) 216 } 217 } 218 219 func (s *fakeDNSServer) setHosts(hosts sets.String) { 220 s.mu.Lock() 221 defer s.mu.Unlock() 222 s.hosts = make(map[string]int, len(hosts)) 223 for k := range hosts { 224 s.hosts[dns.Fqdn(k)] = 0 225 } 226 } 227 228 func (s *fakeDNSServer) setFailure(failure bool) { 229 s.mu.Lock() 230 defer s.mu.Unlock() 231 s.failure = failure 232 }