blob: a251f1df30d49cccbe74710c90980f38a943f63c [file] [log] [blame]
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package model
import (
"testing"
"time"
)
import (
"github.com/golang/protobuf/proto" // nolint: staticcheck
"github.com/golang/protobuf/ptypes/wrappers"
"istio.io/api/annotation"
meshconfig "istio.io/api/mesh/v1alpha1"
"istio.io/api/networking/v1beta1"
istioTypes "istio.io/api/type/v1beta1"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/config"
"github.com/apache/dubbo-go-pixiu/pkg/config/mesh"
"github.com/apache/dubbo-go-pixiu/pkg/config/schema/gvk"
"github.com/apache/dubbo-go-pixiu/pkg/test/util/assert"
)
var now = time.Now()
const istioRootNamespace = "dubbo-system"
func TestConvertToMeshConfigProxyConfig(t *testing.T) {
cases := []struct {
name string
pc *v1beta1.ProxyConfig
expected *meshconfig.ProxyConfig
}{
{
name: "concurrency",
pc: &v1beta1.ProxyConfig{
Concurrency: &wrappers.Int32Value{Value: 3},
},
expected: &meshconfig.ProxyConfig{
Concurrency: &wrappers.Int32Value{Value: 3},
},
},
{
name: "environment variables",
pc: &v1beta1.ProxyConfig{
EnvironmentVariables: map[string]string{
"a": "b",
"c": "d",
},
},
expected: &meshconfig.ProxyConfig{
ProxyMetadata: map[string]string{
"a": "b",
"c": "d",
},
},
},
}
for _, tc := range cases {
converted := toMeshConfigProxyConfig(tc.pc)
assert.Equal(t, converted, tc.expected)
}
}
func TestMergeWithPrecedence(t *testing.T) {
cases := []struct {
name string
first *meshconfig.ProxyConfig
second *meshconfig.ProxyConfig
expected *meshconfig.ProxyConfig
}{
{
name: "concurrency",
first: &meshconfig.ProxyConfig{
Concurrency: v(1),
},
second: &meshconfig.ProxyConfig{
Concurrency: v(2),
},
expected: &meshconfig.ProxyConfig{
Concurrency: v(1),
},
},
{
name: "concurrency value 0",
first: &meshconfig.ProxyConfig{
Concurrency: v(0),
},
second: &meshconfig.ProxyConfig{
Concurrency: v(2),
},
expected: &meshconfig.ProxyConfig{
Concurrency: v(0),
},
},
{
name: "source concurrency nil",
first: &meshconfig.ProxyConfig{
Concurrency: nil,
},
second: &meshconfig.ProxyConfig{
Concurrency: v(2),
},
expected: &meshconfig.ProxyConfig{
Concurrency: v(2),
},
},
{
name: "dest concurrency nil",
first: &meshconfig.ProxyConfig{
Concurrency: v(2),
},
second: &meshconfig.ProxyConfig{
Concurrency: nil,
},
expected: &meshconfig.ProxyConfig{
Concurrency: v(2),
},
},
{
name: "both concurrency nil",
first: &meshconfig.ProxyConfig{
Concurrency: nil,
},
second: &meshconfig.ProxyConfig{
Concurrency: nil,
},
expected: &meshconfig.ProxyConfig{
Concurrency: nil,
},
},
{
name: "envvars",
first: &meshconfig.ProxyConfig{
ProxyMetadata: map[string]string{
"a": "x",
"b": "y",
},
},
second: &meshconfig.ProxyConfig{
ProxyMetadata: map[string]string{
"a": "z",
"b": "y",
"c": "d",
},
},
expected: &meshconfig.ProxyConfig{
ProxyMetadata: map[string]string{
"a": "x",
"b": "y",
"c": "d",
},
},
},
{
name: "empty envars merge with populated",
first: &meshconfig.ProxyConfig{
ProxyMetadata: map[string]string{},
},
second: &meshconfig.ProxyConfig{
ProxyMetadata: map[string]string{
"a": "z",
"b": "y",
"c": "d",
},
},
expected: &meshconfig.ProxyConfig{
ProxyMetadata: map[string]string{
"a": "z",
"b": "y",
"c": "d",
},
},
},
{
name: "nil proxyconfig",
first: nil,
second: &meshconfig.ProxyConfig{
ProxyMetadata: map[string]string{
"a": "z",
"b": "y",
"c": "d",
},
},
expected: &meshconfig.ProxyConfig{
ProxyMetadata: map[string]string{
"a": "z",
"b": "y",
"c": "d",
},
},
},
}
for _, tc := range cases {
merged := mergeWithPrecedence(tc.first, tc.second)
assert.Equal(t, merged, tc.expected)
}
}
func TestEffectiveProxyConfig(t *testing.T) {
cases := []struct {
name string
configs []config.Config
defaultConfig *meshconfig.ProxyConfig
proxy *NodeMetadata
expected *meshconfig.ProxyConfig
}{
{
name: "CR applies to matching namespace",
configs: []config.Config{
newProxyConfig("ns", "test-ns",
&v1beta1.ProxyConfig{
Concurrency: v(3),
Image: &v1beta1.ProxyImage{
ImageType: "debug",
},
}),
},
proxy: newMeta("test-ns", nil, nil),
expected: &meshconfig.ProxyConfig{
Concurrency: v(3),
Image: &v1beta1.ProxyImage{
ImageType: "debug",
},
},
},
{
name: "CR takes precedence over meshConfig.defaultConfig",
configs: []config.Config{
newProxyConfig("ns", istioRootNamespace,
&v1beta1.ProxyConfig{
Concurrency: v(3),
}),
},
defaultConfig: &meshconfig.ProxyConfig{Concurrency: v(2)},
proxy: newMeta("bar", nil, nil),
expected: &meshconfig.ProxyConfig{Concurrency: v(3)},
},
{
name: "workload matching CR takes precedence over namespace matching CR",
configs: []config.Config{
newProxyConfig("workload", "test-ns",
&v1beta1.ProxyConfig{
Selector: selector(map[string]string{
"test": "selector",
}),
Concurrency: v(3),
}),
newProxyConfig("ns", "test-ns",
&v1beta1.ProxyConfig{
Concurrency: v(2),
}),
},
proxy: newMeta("test-ns", map[string]string{"test": "selector"}, nil),
expected: &meshconfig.ProxyConfig{Concurrency: v(3)},
},
{
name: "matching workload CR takes precedence over annotation",
configs: []config.Config{
newProxyConfig("workload", "test-ns",
&v1beta1.ProxyConfig{
Selector: selector(map[string]string{
"test": "selector",
}),
Concurrency: v(3),
Image: &v1beta1.ProxyImage{
ImageType: "debug",
},
}),
},
proxy: newMeta(
"test-ns",
map[string]string{
"test": "selector",
}, map[string]string{
annotation.ProxyConfig.Name: "{ \"concurrency\": 5 }",
}),
expected: &meshconfig.ProxyConfig{
Concurrency: v(3),
Image: &v1beta1.ProxyImage{
ImageType: "debug",
},
},
},
{
name: "CR in other namespaces get ignored",
configs: []config.Config{
newProxyConfig("ns", "wrong-ns",
&v1beta1.ProxyConfig{
Concurrency: v(1),
}),
newProxyConfig("workload", "wrong-ns",
&v1beta1.ProxyConfig{
Selector: selector(map[string]string{
"test": "selector",
}),
Concurrency: v(2),
}),
newProxyConfig("global", istioRootNamespace,
&v1beta1.ProxyConfig{
Concurrency: v(3),
}),
},
proxy: newMeta("test-ns", map[string]string{"test": "selector"}, nil),
expected: &meshconfig.ProxyConfig{Concurrency: v(3)},
},
{
name: "multiple matching workload CRs, oldest applies",
configs: []config.Config{
setCreationTimestamp(newProxyConfig("workload-a", "test-ns",
&v1beta1.ProxyConfig{
Selector: selector(map[string]string{
"test": "selector",
}),
EnvironmentVariables: map[string]string{
"A": "1",
},
}), now),
setCreationTimestamp(newProxyConfig("workload-b", "test-ns",
&v1beta1.ProxyConfig{
Selector: selector(map[string]string{
"test": "selector",
}),
EnvironmentVariables: map[string]string{
"B": "2",
},
}), now.Add(time.Hour)),
setCreationTimestamp(newProxyConfig("workload-c", "test-ns",
&v1beta1.ProxyConfig{
Selector: selector(map[string]string{
"test": "selector",
}),
EnvironmentVariables: map[string]string{
"C": "3",
},
}), now.Add(time.Hour)),
},
proxy: newMeta(
"test-ns",
map[string]string{
"test": "selector",
}, map[string]string{}),
expected: &meshconfig.ProxyConfig{ProxyMetadata: map[string]string{
"A": "1",
}},
},
{
name: "multiple matching namespace CRs, oldest applies",
configs: []config.Config{
setCreationTimestamp(newProxyConfig("workload-a", "test-ns",
&v1beta1.ProxyConfig{
EnvironmentVariables: map[string]string{
"A": "1",
},
}), now),
setCreationTimestamp(newProxyConfig("workload-b", "test-ns",
&v1beta1.ProxyConfig{
EnvironmentVariables: map[string]string{
"B": "2",
},
}), now.Add(time.Hour)),
setCreationTimestamp(newProxyConfig("workload-c", "test-ns",
&v1beta1.ProxyConfig{
EnvironmentVariables: map[string]string{
"C": "3",
},
}), now.Add(time.Hour)),
},
proxy: newMeta(
"test-ns",
map[string]string{}, map[string]string{}),
expected: &meshconfig.ProxyConfig{ProxyMetadata: map[string]string{
"A": "1",
}},
},
{
name: "no configured CR or default config",
proxy: newMeta("ns", nil, nil),
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
store := newProxyConfigStore(t, tc.configs)
m := &meshconfig.MeshConfig{
RootNamespace: istioRootNamespace,
DefaultConfig: tc.defaultConfig,
}
pcs, err := GetProxyConfigs(store, m)
if err != nil {
t.Fatalf("failed to list proxyconfigs: %v", err)
}
merged := pcs.EffectiveProxyConfig(
tc.proxy,
&meshconfig.MeshConfig{
RootNamespace: istioRootNamespace,
DefaultConfig: tc.defaultConfig,
})
pc := mesh.DefaultProxyConfig()
proto.Merge(pc, tc.expected)
assert.Equal(t, merged, pc)
})
}
}
func newProxyConfig(name, ns string, spec config.Spec) config.Config {
return config.Config{
Meta: config.Meta{
GroupVersionKind: gvk.ProxyConfig,
Name: name,
Namespace: ns,
},
Spec: spec,
}
}
func newProxyConfigStore(t *testing.T, configs []config.Config) ConfigStore {
t.Helper()
store := NewFakeStore()
for _, cfg := range configs {
store.Create(cfg)
}
return MakeIstioStore(store)
}
func setCreationTimestamp(c config.Config, t time.Time) config.Config {
c.Meta.CreationTimestamp = t
return c
}
func newMeta(ns string, labels, annotations map[string]string) *NodeMetadata {
return &NodeMetadata{
Namespace: ns,
Labels: labels,
Annotations: annotations,
}
}
func v(x int32) *wrappers.Int32Value {
return &wrappers.Int32Value{Value: x}
}
func selector(l map[string]string) *istioTypes.WorkloadSelector {
return &istioTypes.WorkloadSelector{MatchLabels: l}
}