blob: cf8351ae6821986115ba9cedef665961485a60e8 [file] [log] [blame]
//go:build integ
// +build integ
// 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 proxyconfig
import (
"fmt"
"strings"
"testing"
"time"
)
import (
"github.com/apache/dubbo-go-pixiu/pkg/test/framework"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/echo/deployment"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/istio"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/components/namespace"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/label"
"github.com/apache/dubbo-go-pixiu/pkg/test/framework/resource"
"github.com/apache/dubbo-go-pixiu/pkg/test/util/retry"
"github.com/apache/dubbo-go-pixiu/pkg/test/util/tmpl"
)
var i istio.Instance
func TestMain(m *testing.M) {
framework.
NewSuite(m).
Skip("used for feature development, no need to run in CI").
Label(label.CustomSetup).
Setup(istio.Setup(&i, func(ctx resource.Context, cfg *istio.Config) {
cfg.ControlPlaneValues = `
values:
meshConfig:
defaultConfig:
proxyMetadata:
A: "1"
B: "2"
`
})).
Run()
}
type proxyConfigInstance struct {
namespace string
config string
}
func TestProxyConfig(t *testing.T) {
framework.NewTest(t).
Features("usability.observability.proxy-config").
RequireIstioVersion("1.13").
Run(func(ctx framework.TestContext) {
ns := namespace.NewOrFail(ctx, ctx, namespace.Config{
Prefix: "pc-test",
Inject: true,
})
cases := []struct {
name string
// namespace, labels, and annotations for the echo instance
pcAnnotation string
// service, echo service to use for this subtest
service string
// proxyconfig resources to apply
configs []proxyConfigInstance
// expected environment variables post-injection
expected map[string]string
}{
{
"default config maintained",
"",
"",
[]proxyConfigInstance{},
map[string]string{
"A": "1",
"B": "2",
},
},
{
"global takes precedence over default config",
"",
"",
[]proxyConfigInstance{
newProxyConfig("global", "dubbo-system", nil, map[string]string{
"A": "3",
}),
},
map[string]string{
"A": "3",
"B": "2",
},
},
{
"pod annotation takes precedence over namespace",
"{ \"proxyMetadata\": {\"A\": \"5\"} }",
"",
[]proxyConfigInstance{
newProxyConfig("namespace-scoped", ns.Name(), nil, map[string]string{
"A": "4",
}),
},
map[string]string{
"A": "5",
},
},
{
"workload selector takes precedence over namespace",
"",
"matcher",
[]proxyConfigInstance{
newProxyConfig("namespace-scoped", ns.Name(), nil, map[string]string{
"A": "6",
}),
newProxyConfig("workload-selector", ns.Name(), map[string]string{
"app": "matcher",
}, map[string]string{
"A": "5",
}),
},
map[string]string{
"A": "5",
},
},
}
for i, tc := range cases {
ctx.NewSubTest(tc.name).Run(func(t framework.TestContext) {
applyProxyConfigs(t, tc.configs)
defer deleteProxyConfigs(t, tc.configs)
svc := fmt.Sprintf("echo-%d", i)
if tc.service != "" {
svc = tc.service
}
echoConfig := echo.Config{
Namespace: ns,
Service: svc,
}
if tc.pcAnnotation != "" {
echoConfig.Subsets = []echo.SubsetConfig{
{
Annotations: map[echo.Annotation]*echo.AnnotationValue{
echo.SidecarConfig: {
Value: tc.pcAnnotation,
},
},
},
}
}
instances := deployment.New(ctx, t.Clusters().Configs()...).WithConfig(echoConfig).BuildOrFail(t)
checkInjectedValues(t, instances, tc.expected)
})
}
})
}
func checkInjectedValues(t framework.TestContext, instances echo.Instances, values map[string]string) {
t.Helper()
for _, i := range instances {
i := i
attempts := 0
retry.UntilSuccessOrFail(t, func() error {
// to avoid sleeping for ProxyConfig propagation, we
// can just re-trigger injection on every retry.
if attempts > 0 {
err := i.Restart()
if err != nil {
return fmt.Errorf("failed to restart echo instance: %v", err)
}
}
attempts++
for _, w := range i.WorkloadsOrFail(t) {
w := w
for k, v := range values {
// can we rely on printenv being in the container once distroless is default?
out, _, err := i.Config().Cluster.PodExec(w.PodName(), i.Config().Namespace.Name(),
"istio-proxy", fmt.Sprintf("printenv %s", k))
out = strings.TrimSuffix(out, "\n")
if err != nil {
return fmt.Errorf("could not exec into pod: %v", err)
}
if out != v {
return fmt.Errorf("expected envvar %s with value %q, got %q", k, v, out)
}
}
}
return nil
}, retry.Timeout(time.Second*45))
}
}
func applyProxyConfigs(ctx framework.TestContext, configs []proxyConfigInstance) {
for _, config := range configs {
ctx.ConfigIstio().YAML(config.namespace, config.config).ApplyOrFail(ctx)
}
// TODO(Monkeyanator) give a few seconds for PC to propagate
// shouldn't be required but multicluster seems to have some issues with echo instance restart.
time.Sleep(time.Second * 5)
}
func deleteProxyConfigs(ctx framework.TestContext, configs []proxyConfigInstance) {
for _, config := range configs {
ctx.ConfigIstio().YAML(config.namespace, config.config).DeleteOrFail(ctx)
}
}
func newProxyConfig(name, ns string, selector, values map[string]string) proxyConfigInstance {
tpl := `
apiVersion: networking.istio.io/v1beta1
kind: ProxyConfig
metadata:
name: {{ .Name }}
spec:
{{- if .Selector }}
selector:
matchLabels:
{{- range $k, $v := .Selector }}
{{ $k }}: {{ $v }}
{{- end }}
{{- end }}
environmentVariables:
{{- range $k, $v := .Values }}
{{ $k }}: "{{ $v }}"
{{- end }}
`
return proxyConfigInstance{
namespace: ns,
config: tmpl.MustEvaluate(tpl, struct {
Name string
Selector map[string]string
Values map[string]string
}{
Name: name,
Selector: selector,
Values: values,
}),
}
}