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  }