istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/xds/nds_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  package xds_test
    15  
    16  import (
    17  	"reflect"
    18  	"testing"
    19  	"time"
    20  
    21  	core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
    22  	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    23  	"github.com/google/go-cmp/cmp"
    24  	"google.golang.org/protobuf/testing/protocmp"
    25  
    26  	"istio.io/istio/pilot/pkg/model"
    27  	"istio.io/istio/pilot/pkg/util/protoconv"
    28  	v3 "istio.io/istio/pilot/pkg/xds/v3"
    29  	"istio.io/istio/pilot/test/xds"
    30  	"istio.io/istio/pkg/config/constants"
    31  	dnsProto "istio.io/istio/pkg/dns/proto"
    32  )
    33  
    34  func TestNDS(t *testing.T) {
    35  	cases := []struct {
    36  		name     string
    37  		meta     model.NodeMetadata
    38  		expected *dnsProto.NameTable
    39  	}{
    40  		{
    41  			name: "auto allocate",
    42  			meta: model.NodeMetadata{
    43  				DNSCapture:      true,
    44  				DNSAutoAllocate: true,
    45  			},
    46  			expected: &dnsProto.NameTable{
    47  				Table: map[string]*dnsProto.NameTable_NameInfo{
    48  					"random-1.host.example": {
    49  						Ips:      []string{"240.240.116.21"},
    50  						Registry: "External",
    51  					},
    52  					"random-2.host.example": {
    53  						Ips:      []string{"9.9.9.9"},
    54  						Registry: "External",
    55  					},
    56  					"random-3.host.example": {
    57  						Ips:      []string{"240.240.81.100"},
    58  						Registry: "External",
    59  					},
    60  				},
    61  			},
    62  		},
    63  		{
    64  			name: "just capture",
    65  			meta: model.NodeMetadata{
    66  				DNSCapture: true,
    67  			},
    68  			expected: &dnsProto.NameTable{
    69  				Table: map[string]*dnsProto.NameTable_NameInfo{
    70  					"random-2.host.example": {
    71  						Ips:      []string{"9.9.9.9"},
    72  						Registry: "External",
    73  					},
    74  				},
    75  			},
    76  		},
    77  	}
    78  	for _, tt := range cases {
    79  		t.Run(tt.name, func(t *testing.T) {
    80  			s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{
    81  				ConfigString: mustReadFile(t, "./testdata/nds-se.yaml"),
    82  			})
    83  
    84  			ads := s.ConnectADS().WithType(v3.NameTableType)
    85  			res := ads.RequestResponseAck(t, &discovery.DiscoveryRequest{
    86  				Node: &core.Node{
    87  					Id:       ads.ID,
    88  					Metadata: tt.meta.ToStruct(),
    89  				},
    90  			})
    91  
    92  			nt := &dnsProto.NameTable{}
    93  			err := res.Resources[0].UnmarshalTo(nt)
    94  			if err != nil {
    95  				t.Fatal("Failed to unmarshal name table", err)
    96  				return
    97  			}
    98  			if len(nt.Table) == 0 {
    99  				t.Fatalf("expected more than 0 entries in name table")
   100  			}
   101  			if diff := cmp.Diff(nt, tt.expected, protocmp.Transform()); diff != "" {
   102  				t.Fatalf("name table does not match expected value:\n %v", diff)
   103  			}
   104  		})
   105  	}
   106  }
   107  
   108  func TestGenerate(t *testing.T) {
   109  	nt := &dnsProto.NameTable{
   110  		Table: make(map[string]*dnsProto.NameTable_NameInfo),
   111  	}
   112  	emptyNameTable := model.Resources{&discovery.Resource{Resource: protoconv.MessageToAny(nt)}}
   113  
   114  	cases := []struct {
   115  		name      string
   116  		proxy     *model.Proxy
   117  		resources []string
   118  		request   *model.PushRequest
   119  		nameTable []*discovery.Resource
   120  	}{
   121  		{
   122  			name:      "partial push with headless endpoint update",
   123  			proxy:     &model.Proxy{Type: model.SidecarProxy},
   124  			request:   &model.PushRequest{Reason: model.NewReasonStats(model.HeadlessEndpointUpdate)},
   125  			nameTable: emptyNameTable,
   126  		},
   127  		{
   128  			name:      "full push",
   129  			proxy:     &model.Proxy{Type: model.SidecarProxy},
   130  			request:   &model.PushRequest{Full: true},
   131  			nameTable: emptyNameTable,
   132  		},
   133  		{
   134  			name:      "partial push with no headless endpoint update",
   135  			proxy:     &model.Proxy{Type: model.SidecarProxy},
   136  			request:   &model.PushRequest{},
   137  			nameTable: nil,
   138  		},
   139  	}
   140  	for _, tt := range cases {
   141  		t.Run(tt.name, func(t *testing.T) {
   142  			if tt.proxy.Metadata == nil {
   143  				tt.proxy.Metadata = &model.NodeMetadata{}
   144  			}
   145  			tt.proxy.Metadata.ClusterID = constants.DefaultClusterName
   146  			s := xds.NewFakeDiscoveryServer(t, xds.FakeOptions{})
   147  
   148  			gen := s.Discovery.Generators[v3.NameTableType]
   149  			tt.request.Start = time.Now()
   150  			nametable, _, _ := gen.Generate(s.SetupProxy(tt.proxy), &model.WatchedResource{ResourceNames: tt.resources}, tt.request)
   151  			if len(tt.nameTable) == 0 {
   152  				if len(nametable) != 0 {
   153  					t.Errorf("unexpected nametable. want: %v, got: %v", tt.nameTable, nametable)
   154  				}
   155  			} else {
   156  				if !reflect.DeepEqual(tt.nameTable[0].Resource, nametable[0].Resource) {
   157  					t.Errorf("unexpected nametable. want: %v, got: %v", tt.nameTable, nametable)
   158  				}
   159  			}
   160  		})
   161  	}
   162  }