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 }