istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/mesh/mesh_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 mesh_test
    16  
    17  import (
    18  	"fmt"
    19  	"testing"
    20  
    21  	"github.com/google/go-cmp/cmp"
    22  	"google.golang.org/protobuf/testing/protocmp"
    23  	"google.golang.org/protobuf/types/known/wrapperspb"
    24  
    25  	meshconfig "istio.io/api/mesh/v1alpha1"
    26  	"istio.io/istio/pkg/config/mesh"
    27  	"istio.io/istio/pkg/config/validation/agent"
    28  	"istio.io/istio/pkg/test/util/assert"
    29  	"istio.io/istio/pkg/util/protomarshal"
    30  )
    31  
    32  func TestApplyProxyConfig(t *testing.T) {
    33  	config := mesh.DefaultMeshConfig()
    34  	defaultDiscovery := config.DefaultConfig.DiscoveryAddress
    35  
    36  	t.Run("apply single", func(t *testing.T) {
    37  		mc, err := mesh.ApplyProxyConfig("discoveryAddress: foo", config)
    38  		if err != nil {
    39  			t.Fatal(err)
    40  		}
    41  		if mc.DefaultConfig.DiscoveryAddress != "foo" {
    42  			t.Fatalf("expected discoveryAddress: foo, got %q", mc.DefaultConfig.DiscoveryAddress)
    43  		}
    44  	})
    45  
    46  	t.Run("apply again", func(t *testing.T) {
    47  		mc, err := mesh.ApplyProxyConfig("drainDuration: 5s", config)
    48  		if err != nil {
    49  			t.Fatal(err)
    50  		}
    51  		// Ensure we didn't modify the passed in mesh config
    52  		if mc.DefaultConfig.DiscoveryAddress != defaultDiscovery {
    53  			t.Fatalf("expected discoveryAddress: %q, got %q", defaultDiscovery, mc.DefaultConfig.DiscoveryAddress)
    54  		}
    55  		if mc.DefaultConfig.DrainDuration.Seconds != 5 {
    56  			t.Fatalf("expected drainDuration: 5s, got %q", mc.DefaultConfig.DrainDuration.Seconds)
    57  		}
    58  	})
    59  
    60  	t.Run("apply proxy metadata", func(t *testing.T) {
    61  		config := mesh.DefaultMeshConfig()
    62  		config.DefaultConfig.ProxyMetadata = map[string]string{
    63  			"merged":  "original",
    64  			"default": "foo",
    65  		}
    66  		mc, err := mesh.ApplyProxyConfig(`proxyMetadata: {"merged":"override","override":"bar"}`, config)
    67  		assert.NoError(t, err)
    68  		// Ensure we didn't modify the passed in mesh config
    69  		assert.Equal(t, mc.DefaultConfig.ProxyMetadata, map[string]string{
    70  			"merged":   "override",
    71  			"default":  "foo",
    72  			"override": "bar",
    73  		}, "unexpected proxy metadata")
    74  	})
    75  	t.Run("apply proxy metadata to mesh config", func(t *testing.T) {
    76  		config := mesh.DefaultMeshConfig()
    77  		config.DefaultConfig.ProxyMetadata = map[string]string{
    78  			"merged":  "original",
    79  			"default": "foo",
    80  		}
    81  		mc, err := mesh.ApplyMeshConfig(`defaultConfig:
    82    proxyMetadata: {"merged":"override","override":"bar"}`, config)
    83  		if err != nil {
    84  			t.Fatal(err)
    85  		}
    86  		// Ensure we didn't modify the passed in mesh config
    87  		assert.Equal(t, mc.DefaultConfig.ProxyMetadata, map[string]string{
    88  			"merged":   "override",
    89  			"default":  "foo",
    90  			"override": "bar",
    91  		}, "unexpected proxy metadata")
    92  	})
    93  	t.Run("apply should not modify", func(t *testing.T) {
    94  		config := mesh.DefaultMeshConfig()
    95  		config.DefaultConfig.ProxyMetadata = map[string]string{
    96  			"foo": "bar",
    97  		}
    98  		orig, err := protomarshal.ToYAML(config)
    99  		if err != nil {
   100  			t.Fatal(err)
   101  		}
   102  
   103  		if _, err := mesh.ApplyProxyConfig(`proxyMetadata: {"merged":"override","override":"bar"}`, config); err != nil {
   104  			t.Fatal(err)
   105  		}
   106  		after, err := protomarshal.ToYAML(config)
   107  		if err != nil {
   108  			t.Fatal(err)
   109  		}
   110  		if orig != after {
   111  			t.Fatalf("Changed before and after. Expected %v, got %v", orig, after)
   112  		}
   113  	})
   114  }
   115  
   116  func TestProxyConfigMerge(t *testing.T) {
   117  	cases := []struct {
   118  		name    string
   119  		base    string
   120  		overlay string
   121  		result  string
   122  	}{
   123  		{
   124  			name: "disabled then enabled",
   125  			base: `
   126  proxyHeaders:
   127    requestId:
   128      disabled: true`,
   129  			overlay: `
   130  proxyHeaders:
   131    requestId:
   132      disabled: false`,
   133  			result: `
   134  proxyHeaders:
   135    requestId:
   136      disabled: false`,
   137  		},
   138  		{
   139  			name: "enabled then disabled",
   140  			base: `
   141  proxyHeaders:
   142    requestId:
   143      disabled: false`,
   144  			overlay: `
   145  proxyHeaders:
   146    requestId:
   147      disabled: true`,
   148  			result: `
   149  proxyHeaders:
   150    requestId:
   151      disabled: true`,
   152  		},
   153  		{
   154  			name: "set multiple fields",
   155  			base: `
   156  proxyHeaders:
   157    forwardedClientCert: APPEND_FORWARD
   158    server:
   159      value: server
   160    requestId:
   161      disabled: true
   162    attemptCount: {}
   163    envoyDebugHeaders:
   164      disabled: true`,
   165  			overlay: `
   166  proxyHeaders:
   167    forwardedClientCert: ALWAYS_FORWARD_ONLY
   168    server:
   169      disabled: true
   170    requestId: {}
   171    attemptCount:
   172      disabled: true
   173    envoyDebugHeaders:
   174      disabled: true`,
   175  			result: `
   176  proxyHeaders:
   177    forwardedClientCert: ALWAYS_FORWARD_ONLY
   178    server:
   179      disabled: true
   180    requestId: {}
   181    attemptCount:
   182      disabled: true
   183    envoyDebugHeaders:
   184      disabled: true`,
   185  		},
   186  	}
   187  	for _, tt := range cases {
   188  		t.Run(tt.name, func(t *testing.T) {
   189  			mc := &meshconfig.MeshConfig{DefaultConfig: &meshconfig.ProxyConfig{}}
   190  			var err error
   191  			mc, err = mesh.ApplyProxyConfig(tt.base, mc)
   192  			assert.NoError(t, err)
   193  			mc, err = mesh.ApplyProxyConfig(tt.overlay, mc)
   194  			assert.NoError(t, err)
   195  
   196  			want := &meshconfig.ProxyConfig{}
   197  			assert.NoError(t, protomarshal.ApplyYAML(tt.result, want))
   198  
   199  			assert.Equal(t, mc.GetDefaultConfig(), want)
   200  		})
   201  	}
   202  }
   203  
   204  func TestDefaultProxyConfig(t *testing.T) {
   205  	if err := agent.ValidateMeshConfigProxyConfig(mesh.DefaultProxyConfig()); err != nil {
   206  		t.Errorf("validation of default proxy config failed with %v", err)
   207  	}
   208  }
   209  
   210  func TestDefaultMeshConfig(t *testing.T) {
   211  	warn, err := agent.ValidateMeshConfig(mesh.DefaultMeshConfig())
   212  	if err != nil {
   213  		t.Errorf("validation of default mesh config failed with %v", err)
   214  	}
   215  	if warn != nil {
   216  		t.Errorf("validation of default mesh config produced warnings: %v", warn)
   217  	}
   218  }
   219  
   220  func TestApplyMeshConfigDefaults(t *testing.T) {
   221  	configPath := "/test/config/patch"
   222  	yaml := fmt.Sprintf(`
   223  defaultConfig:
   224    configPath: %s
   225  `, configPath)
   226  
   227  	want := mesh.DefaultMeshConfig()
   228  	want.DefaultConfig.ConfigPath = configPath
   229  
   230  	got, err := mesh.ApplyMeshConfigDefaults(yaml)
   231  	if err != nil {
   232  		t.Fatalf("ApplyMeshConfigDefaults() failed: %v", err)
   233  	}
   234  	assert.Equal(t, got, want)
   235  	// Verify overrides
   236  	got, err = mesh.ApplyMeshConfigDefaults(`
   237  serviceSettings: 
   238    - settings:
   239        clusterLocal: true
   240      host:
   241        - "*.myns.svc.cluster.local"
   242  ingressClass: foo
   243  enableTracing: false
   244  trustDomainAliases: ["default", "both"]
   245  defaultServiceExportTo: 
   246  - "foo"
   247  outboundTrafficPolicy:
   248    mode: REGISTRY_ONLY
   249  clusterLocalNamespaces: 
   250  - "foons"
   251  defaultProviders:
   252    tracing: [foo]
   253  extensionProviders:
   254  - name: sd
   255    stackdriver: {}
   256  defaultConfig:
   257    tracing: {}
   258    concurrency: 4`)
   259  	if err != nil {
   260  		t.Fatal(err)
   261  	}
   262  	if got.DefaultConfig.Tracing.GetZipkin() != nil {
   263  		t.Error("Failed to override tracing")
   264  	}
   265  	if len(got.DefaultProviders.GetMetrics()) != 0 {
   266  		t.Errorf("default providers deep merge failed, got %v", got.DefaultProviders.GetMetrics())
   267  	}
   268  	if !cmp.Equal(getExtensionProviders(got.ExtensionProviders), []string{"prometheus", "stackdriver", "envoy", "sd"}, protocmp.Transform()) {
   269  		t.Errorf("extension providers deep merge failed, got %v", getExtensionProviders(got.ExtensionProviders))
   270  	}
   271  	if len(got.TrustDomainAliases) != 2 {
   272  		t.Errorf("trust domain aliases deep merge failed")
   273  	}
   274  
   275  	gotY, err := protomarshal.ToYAML(got)
   276  	t.Log("Result: \n", gotY, err)
   277  }
   278  
   279  func getExtensionProviders(eps []*meshconfig.MeshConfig_ExtensionProvider) []string {
   280  	got := []string{}
   281  	for _, ep := range eps {
   282  		got = append(got, ep.Name)
   283  	}
   284  	return got
   285  }
   286  
   287  func TestDeepMerge(t *testing.T) {
   288  	cases := []struct {
   289  		name string
   290  		in   string
   291  		out  string
   292  	}{
   293  		{
   294  			name: "set other default provider",
   295  			in: `
   296  defaultProviders:
   297    tracing: [foo]`,
   298  			out: `defaultProviders:
   299    metrics:
   300    - stackdriver
   301    tracing:
   302    - foo
   303  extensionProviders:
   304  - name: stackdriver
   305    stackdriver:
   306      maxNumberOfAttributes: 3
   307  trustDomainAliases: ["both", "default"]
   308  `,
   309  		},
   310  		{
   311  			name: "override default provider",
   312  			in: `
   313  defaultProviders:
   314    metrics: [foo]`,
   315  			out: `defaultProviders:
   316    metrics:
   317    - foo
   318  extensionProviders:
   319  - name: stackdriver
   320    stackdriver:
   321      maxNumberOfAttributes: 3
   322  trustDomainAliases: ["both", "default"]
   323  `,
   324  		},
   325  		{
   326  			name: "replace builtin provider",
   327  			in: `
   328  extensionProviders:
   329  - name: stackdriver
   330    stackdriver:
   331      maxNumberOfAnnotations: 5`,
   332  			out: `defaultProviders:
   333    metrics:
   334    - stackdriver
   335  extensionProviders:
   336  - name: stackdriver
   337    stackdriver:
   338      maxNumberOfAnnotations: 5
   339  trustDomainAliases: ["both", "default"]
   340  `,
   341  		},
   342  		{
   343  			name: "add provider with existing type",
   344  			in: `
   345  extensionProviders:
   346  - name: stackdriver-annotations
   347    stackdriver:
   348      maxNumberOfAnnotations: 5`,
   349  			out: `defaultProviders:
   350    metrics:
   351    - stackdriver
   352  extensionProviders:
   353  - name: stackdriver
   354    stackdriver:
   355      maxNumberOfAttributes: 3
   356  - name: stackdriver-annotations
   357    stackdriver:
   358      maxNumberOfAnnotations: 5
   359  trustDomainAliases: ["both", "default"]
   360  `,
   361  		},
   362  		{
   363  			name: "add provider",
   364  			in: `
   365  extensionProviders:
   366  - name: prometheus
   367    prometheus: {}`,
   368  			out: `defaultProviders:
   369    metrics:
   370    - stackdriver
   371  extensionProviders:
   372  - name: stackdriver
   373    stackdriver:
   374      maxNumberOfAttributes: 3
   375  - name: prometheus
   376    prometheus: {}
   377  trustDomainAliases: ["both", "default"]
   378  `,
   379  		},
   380  		{
   381  			name: "add trust domain aliases",
   382  			in: `
   383  trustDomainAliases: ["added", "both"]`,
   384  			out: `defaultProviders:
   385    metrics:
   386    - stackdriver
   387  extensionProviders:
   388  - name: stackdriver
   389    stackdriver:
   390      maxNumberOfAttributes: 3
   391  trustDomainAliases:
   392  - added
   393  - both
   394  - default
   395  `,
   396  		},
   397  	}
   398  	for _, tt := range cases {
   399  		t.Run(tt.name, func(t *testing.T) {
   400  			mc := mesh.DefaultMeshConfig()
   401  			mc.DefaultProviders = &meshconfig.MeshConfig_DefaultProviders{
   402  				Metrics: []string{"stackdriver"},
   403  			}
   404  			mc.ExtensionProviders = []*meshconfig.MeshConfig_ExtensionProvider{{
   405  				Name: "stackdriver",
   406  				Provider: &meshconfig.MeshConfig_ExtensionProvider_Stackdriver{
   407  					Stackdriver: &meshconfig.MeshConfig_ExtensionProvider_StackdriverProvider{
   408  						MaxNumberOfAttributes: &wrapperspb.Int64Value{Value: 3},
   409  					},
   410  				},
   411  			}}
   412  			mc.TrustDomainAliases = []string{"default", "both"}
   413  			res, err := mesh.ApplyMeshConfig(tt.in, mc)
   414  			if err != nil {
   415  				t.Fatal(err)
   416  			}
   417  			// Just extract fields we are testing
   418  			minimal := &meshconfig.MeshConfig{}
   419  			minimal.DefaultProviders = res.DefaultProviders
   420  			minimal.ExtensionProviders = res.ExtensionProviders
   421  			minimal.TrustDomainAliases = res.TrustDomainAliases
   422  
   423  			want := &meshconfig.MeshConfig{}
   424  			protomarshal.ApplyYAML(tt.out, want)
   425  			if d := cmp.Diff(want, minimal, protocmp.Transform()); d != "" {
   426  				t.Fatalf("got diff %v", d)
   427  			}
   428  		})
   429  	}
   430  }
   431  
   432  func TestApplyMeshNetworksDefaults(t *testing.T) {
   433  	yml := `
   434  networks:
   435    network1:
   436      endpoints:
   437      - fromCidr: "192.168.0.1/24"
   438      gateways:
   439      - address: 1.1.1.1
   440        port: 80
   441    network2:
   442      endpoints:
   443      - fromRegistry: reg1
   444      gateways:
   445      - registryServiceName: reg1
   446        port: 443
   447  `
   448  
   449  	want := mesh.EmptyMeshNetworks()
   450  	want.Networks = map[string]*meshconfig.Network{
   451  		"network1": {
   452  			Endpoints: []*meshconfig.Network_NetworkEndpoints{
   453  				{
   454  					Ne: &meshconfig.Network_NetworkEndpoints_FromCidr{
   455  						FromCidr: "192.168.0.1/24",
   456  					},
   457  				},
   458  			},
   459  			Gateways: []*meshconfig.Network_IstioNetworkGateway{
   460  				{
   461  					Gw: &meshconfig.Network_IstioNetworkGateway_Address{
   462  						Address: "1.1.1.1",
   463  					},
   464  					Port: 80,
   465  				},
   466  			},
   467  		},
   468  		"network2": {
   469  			Endpoints: []*meshconfig.Network_NetworkEndpoints{
   470  				{
   471  					Ne: &meshconfig.Network_NetworkEndpoints_FromRegistry{
   472  						FromRegistry: "reg1",
   473  					},
   474  				},
   475  			},
   476  			Gateways: []*meshconfig.Network_IstioNetworkGateway{
   477  				{
   478  					Gw: &meshconfig.Network_IstioNetworkGateway_RegistryServiceName{
   479  						RegistryServiceName: "reg1",
   480  					},
   481  					Port: 443,
   482  				},
   483  			},
   484  		},
   485  	}
   486  
   487  	got, err := mesh.ParseMeshNetworks(yml)
   488  	if err != nil {
   489  		t.Fatalf("ApplyMeshNetworksDefaults() failed: %v", err)
   490  	}
   491  	assert.Equal(t, got, &want)
   492  }