istio.io/istio@v0.0.0-20240520182934-d79c90f27776/tests/integration/pilot/proxyconfig/proxyconfig_test.go (about) 1 //go:build integ 2 // +build integ 3 4 // Copyright Istio Authors 5 // 6 // Licensed under the Apache License, Version 2.0 (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // 10 // http://www.apache.org/licenses/LICENSE-2.0 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 18 package proxyconfig 19 20 import ( 21 "fmt" 22 "strings" 23 "testing" 24 "time" 25 26 "istio.io/api/annotation" 27 "istio.io/istio/pkg/test/framework" 28 "istio.io/istio/pkg/test/framework/components/echo" 29 "istio.io/istio/pkg/test/framework/components/echo/deployment" 30 "istio.io/istio/pkg/test/framework/components/istio" 31 "istio.io/istio/pkg/test/framework/components/namespace" 32 "istio.io/istio/pkg/test/framework/label" 33 "istio.io/istio/pkg/test/framework/resource" 34 "istio.io/istio/pkg/test/util/retry" 35 "istio.io/istio/pkg/test/util/tmpl" 36 ) 37 38 var i istio.Instance 39 40 func TestMain(m *testing.M) { 41 framework. 42 NewSuite(m). 43 Skip("used for feature development, no need to run in CI"). 44 Label(label.CustomSetup). 45 Setup(istio.Setup(&i, func(ctx resource.Context, cfg *istio.Config) { 46 cfg.ControlPlaneValues = ` 47 values: 48 meshConfig: 49 defaultConfig: 50 proxyMetadata: 51 A: "1" 52 B: "2" 53 ` 54 })). 55 Run() 56 } 57 58 type proxyConfigInstance struct { 59 namespace string 60 config string 61 } 62 63 func TestProxyConfig(t *testing.T) { 64 framework.NewTest(t). 65 RequireIstioVersion("1.13"). 66 Run(func(ctx framework.TestContext) { 67 ns := namespace.NewOrFail(ctx, ctx, namespace.Config{ 68 Prefix: "pc-test", 69 Inject: true, 70 }) 71 cases := []struct { 72 name string 73 // namespace, labels, and annotations for the echo instance 74 pcAnnotation string 75 // service, echo service to use for this subtest 76 service string 77 // proxyconfig resources to apply 78 configs []proxyConfigInstance 79 // expected environment variables post-injection 80 expected map[string]string 81 }{ 82 { 83 "default config maintained", 84 "", 85 "", 86 []proxyConfigInstance{}, 87 map[string]string{ 88 "A": "1", 89 "B": "2", 90 }, 91 }, 92 { 93 "global takes precedence over default config", 94 "", 95 "", 96 []proxyConfigInstance{ 97 newProxyConfig("global", "istio-system", nil, map[string]string{ 98 "A": "3", 99 }), 100 }, 101 map[string]string{ 102 "A": "3", 103 "B": "2", 104 }, 105 }, 106 { 107 "pod annotation takes precedence over namespace", 108 "{ \"proxyMetadata\": {\"A\": \"5\"} }", 109 "", 110 []proxyConfigInstance{ 111 newProxyConfig("namespace-scoped", ns.Name(), nil, map[string]string{ 112 "A": "4", 113 }), 114 }, 115 map[string]string{ 116 "A": "5", 117 }, 118 }, 119 { 120 "workload selector takes precedence over namespace", 121 "", 122 "matcher", 123 []proxyConfigInstance{ 124 newProxyConfig("namespace-scoped", ns.Name(), nil, map[string]string{ 125 "A": "6", 126 }), 127 newProxyConfig("workload-selector", ns.Name(), map[string]string{ 128 "app": "matcher", 129 }, map[string]string{ 130 "A": "5", 131 }), 132 }, 133 map[string]string{ 134 "A": "5", 135 }, 136 }, 137 } 138 139 for i, tc := range cases { 140 ctx.NewSubTest(tc.name).Run(func(t framework.TestContext) { 141 applyProxyConfigs(t, tc.configs) 142 defer deleteProxyConfigs(t, tc.configs) 143 144 svc := fmt.Sprintf("echo-%d", i) 145 if tc.service != "" { 146 svc = tc.service 147 } 148 echoConfig := echo.Config{ 149 Namespace: ns, 150 Service: svc, 151 } 152 if tc.pcAnnotation != "" { 153 echoConfig.Subsets = []echo.SubsetConfig{ 154 { 155 Annotations: map[string]string{ 156 annotation.ProxyConfig.Name: tc.pcAnnotation, 157 }, 158 }, 159 } 160 } 161 162 instances := deployment.New(ctx, t.Clusters().Configs()...).WithConfig(echoConfig).BuildOrFail(t) 163 checkInjectedValues(t, instances, tc.expected) 164 }) 165 } 166 }) 167 } 168 169 func checkInjectedValues(t framework.TestContext, instances echo.Instances, values map[string]string) { 170 t.Helper() 171 for _, i := range instances { 172 i := i 173 attempts := 0 174 retry.UntilSuccessOrFail(t, func() error { 175 // to avoid sleeping for ProxyConfig propagation, we 176 // can just re-trigger injection on every retry. 177 if attempts > 0 { 178 err := i.Restart() 179 if err != nil { 180 return fmt.Errorf("failed to restart echo instance: %v", err) 181 } 182 } 183 attempts++ 184 for _, w := range i.WorkloadsOrFail(t) { 185 w := w 186 for k, v := range values { 187 // can we rely on printenv being in the container once distroless is default? 188 out, _, err := i.Config().Cluster.PodExec(w.PodName(), i.Config().Namespace.Name(), 189 "istio-proxy", fmt.Sprintf("printenv %s", k)) 190 out = strings.TrimSuffix(out, "\n") 191 if err != nil { 192 return fmt.Errorf("could not exec into pod: %v", err) 193 } 194 if out != v { 195 return fmt.Errorf("expected envvar %s with value %q, got %q", k, v, out) 196 } 197 } 198 } 199 return nil 200 }, retry.Timeout(time.Second*45)) 201 } 202 } 203 204 func applyProxyConfigs(ctx framework.TestContext, configs []proxyConfigInstance) { 205 for _, config := range configs { 206 ctx.ConfigIstio().YAML(config.namespace, config.config).ApplyOrFail(ctx) 207 } 208 // TODO(Monkeyanator) give a few seconds for PC to propagate 209 // shouldn't be required but multicluster seems to have some issues with echo instance restart. 210 time.Sleep(time.Second * 5) 211 } 212 213 func deleteProxyConfigs(ctx framework.TestContext, configs []proxyConfigInstance) { 214 for _, config := range configs { 215 ctx.ConfigIstio().YAML(config.namespace, config.config).DeleteOrFail(ctx) 216 } 217 } 218 219 func newProxyConfig(name, ns string, selector, values map[string]string) proxyConfigInstance { 220 tpl := ` 221 apiVersion: networking.istio.io/v1beta1 222 kind: ProxyConfig 223 metadata: 224 name: {{ .Name }} 225 spec: 226 {{- if .Selector }} 227 selector: 228 matchLabels: 229 {{- range $k, $v := .Selector }} 230 {{ $k }}: {{ $v }} 231 {{- end }} 232 {{- end }} 233 environmentVariables: 234 {{- range $k, $v := .Values }} 235 {{ $k }}: "{{ $v }}" 236 {{- end }} 237 ` 238 return proxyConfigInstance{ 239 namespace: ns, 240 config: tmpl.MustEvaluate(tpl, struct { 241 Name string 242 Selector map[string]string 243 Values map[string]string 244 }{ 245 Name: name, 246 Selector: selector, 247 Values: values, 248 }), 249 } 250 }