istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/networking/core/route/route_cache_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 route
    16  
    17  import (
    18  	"reflect"
    19  	"testing"
    20  	"time"
    21  
    22  	discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3"
    23  
    24  	networking "istio.io/api/networking/v1alpha3"
    25  	"istio.io/istio/pilot/pkg/model"
    26  	"istio.io/istio/pkg/config"
    27  	"istio.io/istio/pkg/config/constants"
    28  	"istio.io/istio/pkg/config/schema/kind"
    29  	"istio.io/istio/pkg/util/sets"
    30  )
    31  
    32  func TestClearRDSCacheOnDelegateUpdate(t *testing.T) {
    33  	xdsCache := model.NewXdsCache()
    34  	// root virtual service
    35  	root := config.Config{
    36  		Meta: config.Meta{Name: "root", Namespace: "default"},
    37  		Spec: &networking.VirtualService{
    38  			Http: []*networking.HTTPRoute{
    39  				{
    40  					Name: "route",
    41  					Delegate: &networking.Delegate{
    42  						Namespace: "default",
    43  						Name:      "delegate",
    44  					},
    45  				},
    46  			},
    47  		},
    48  	}
    49  	// delegate virtual service
    50  	delegate := model.ConfigKey{Kind: kind.VirtualService, Name: "delegate", Namespace: "default"}
    51  	// rds cache entry
    52  	entry := Cache{
    53  		VirtualServices:         []config.Config{root},
    54  		DelegateVirtualServices: []model.ConfigHash{delegate.HashCode()},
    55  		ListenerPort:            8080,
    56  	}
    57  	resource := &discovery.Resource{Name: "bar"}
    58  
    59  	// add resource to cache
    60  	xdsCache.Add(&entry, &model.PushRequest{Start: time.Now()}, resource)
    61  	if got := xdsCache.Get(&entry); got == nil || !reflect.DeepEqual(got, resource) {
    62  		t.Fatalf("rds cache was not updated")
    63  	}
    64  
    65  	// clear cache when delegate virtual service is updated
    66  	// this func is called by `dropCacheForRequest` in `initPushContext`
    67  	xdsCache.Clear(sets.New(delegate))
    68  	if got := xdsCache.Get(&entry); got != nil {
    69  		t.Fatalf("rds cache was not cleared")
    70  	}
    71  
    72  	// add resource to cache
    73  	xdsCache.Add(&entry, &model.PushRequest{Start: time.Now()}, resource)
    74  	irrelevantDelegate := model.ConfigKey{Kind: kind.VirtualService, Name: "foo", Namespace: "default"}
    75  
    76  	// don't clear cache when irrelevant delegate virtual service is updated
    77  	xdsCache.Clear(sets.New(irrelevantDelegate))
    78  	if got := xdsCache.Get(&entry); got == nil || !reflect.DeepEqual(got, resource) {
    79  		t.Fatalf("rds cache was cleared by irrelevant delegate virtual service update")
    80  	}
    81  }
    82  
    83  func TestDependentConfigs(t *testing.T) {
    84  	tests := []struct {
    85  		name string
    86  		r    Cache
    87  		want []model.ConfigHash
    88  	}{
    89  		{
    90  			name: "no internal vs",
    91  			r: Cache{
    92  				VirtualServices: []config.Config{
    93  					{
    94  						Meta: config.Meta{
    95  							Name:      "foo",
    96  							Namespace: "default",
    97  						},
    98  					},
    99  				},
   100  			},
   101  			want: []model.ConfigHash{
   102  				model.ConfigKey{
   103  					Kind:      kind.VirtualService,
   104  					Name:      "foo",
   105  					Namespace: "default",
   106  				}.HashCode(),
   107  			},
   108  		},
   109  		{
   110  			name: "single parent",
   111  			r: Cache{
   112  				VirtualServices: []config.Config{
   113  					{
   114  						Meta: config.Meta{
   115  							Name:      "foo-0-istio-autogenerated-k8s-gateway",
   116  							Namespace: "default",
   117  							Annotations: map[string]string{
   118  								constants.InternalRouteSemantics: constants.RouteSemanticsGateway,
   119  								constants.InternalParentNames:    "HTTPRoute/foo.default",
   120  							},
   121  						},
   122  					},
   123  				},
   124  			},
   125  			want: []model.ConfigHash{
   126  				model.ConfigKey{
   127  					Kind:      kind.HTTPRoute,
   128  					Name:      "foo",
   129  					Namespace: "default",
   130  				}.HashCode(),
   131  			},
   132  		},
   133  		{
   134  			name: "multiple parents",
   135  			r: Cache{
   136  				VirtualServices: []config.Config{
   137  					{
   138  						Meta: config.Meta{
   139  							Name:      "foo-1-istio-autogenerated-k8s-gateway",
   140  							Namespace: "default",
   141  							Annotations: map[string]string{
   142  								constants.InternalRouteSemantics: constants.RouteSemanticsGateway,
   143  								constants.InternalParentNames:    "HTTPRoute/foo.default,HTTPRoute/bar.default",
   144  							},
   145  						},
   146  					},
   147  				},
   148  			},
   149  			want: []model.ConfigHash{
   150  				model.ConfigKey{
   151  					Kind:      kind.HTTPRoute,
   152  					Name:      "foo",
   153  					Namespace: "default",
   154  				}.HashCode(),
   155  				model.ConfigKey{
   156  					Kind:      kind.HTTPRoute,
   157  					Name:      "bar",
   158  					Namespace: "default",
   159  				}.HashCode(),
   160  			},
   161  		},
   162  		{
   163  			name: "mixed",
   164  			r: Cache{
   165  				VirtualServices: []config.Config{
   166  					{
   167  						Meta: config.Meta{
   168  							Name:      "foo",
   169  							Namespace: "default",
   170  						},
   171  					},
   172  					{
   173  						Meta: config.Meta{
   174  							Name:      "bar-0-istio-autogenerated-k8s-gateway",
   175  							Namespace: "default",
   176  							Annotations: map[string]string{
   177  								constants.InternalRouteSemantics: constants.RouteSemanticsGateway,
   178  								constants.InternalParentNames:    "HTTPRoute/bar.default,HTTPRoute/baz.default",
   179  							},
   180  						},
   181  					},
   182  				},
   183  			},
   184  			want: []model.ConfigHash{
   185  				model.ConfigKey{
   186  					Kind:      kind.VirtualService,
   187  					Name:      "foo",
   188  					Namespace: "default",
   189  				}.HashCode(),
   190  				model.ConfigKey{
   191  					Kind:      kind.HTTPRoute,
   192  					Name:      "bar",
   193  					Namespace: "default",
   194  				}.HashCode(),
   195  				model.ConfigKey{
   196  					Kind:      kind.HTTPRoute,
   197  					Name:      "baz",
   198  					Namespace: "default",
   199  				}.HashCode(),
   200  			},
   201  		},
   202  	}
   203  	for _, tt := range tests {
   204  		t.Run(tt.name, func(t *testing.T) {
   205  			if got := tt.r.DependentConfigs(); !reflect.DeepEqual(got, tt.want) {
   206  				t.Errorf("DependentConfigs got %v, want %v", got, tt.want)
   207  			}
   208  		})
   209  	}
   210  }
   211  
   212  func TestExtractNamespaceForKubernetesService(t *testing.T) {
   213  	tests := []struct {
   214  		hostname string
   215  		want     string
   216  	}{
   217  		{
   218  			"foo.ns.svc.cluster.local",
   219  			"ns",
   220  		},
   221  		{
   222  			"foo.svc.cluster.local",
   223  			"",
   224  		},
   225  		{
   226  			"svc.ns.svc.svc.svc",
   227  			"ns",
   228  		},
   229  		{
   230  			".svc.",
   231  			"",
   232  		},
   233  		{
   234  			"..svc.",
   235  			"",
   236  		},
   237  		{
   238  			"x.svc.",
   239  			"",
   240  		},
   241  	}
   242  	for _, tt := range tests {
   243  		t.Run(tt.hostname, func(t *testing.T) {
   244  			got, err := extractNamespaceForKubernetesService(tt.hostname)
   245  			if (err != nil) != (tt.want == "") {
   246  				t.Fatalf("unexpected error = %v", err)
   247  			}
   248  			if got != tt.want {
   249  				t.Fatalf("got = %v, want %v", got, tt.want)
   250  			}
   251  		})
   252  	}
   253  }