1// Copyright 2019 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
15package metrics
16
17import (
18	"fmt"
19	"testing"
20
21	"istio.io/istio/pkg/test/framework"
22	"istio.io/istio/pkg/test/framework/components/bookinfo"
23	"istio.io/istio/pkg/test/framework/components/galley"
24	"istio.io/istio/pkg/test/framework/components/ingress"
25	"istio.io/istio/pkg/test/framework/components/istio"
26	"istio.io/istio/pkg/test/framework/components/mixer"
27	"istio.io/istio/pkg/test/framework/components/namespace"
28	"istio.io/istio/pkg/test/framework/components/prometheus"
29	"istio.io/istio/pkg/test/framework/label"
30	"istio.io/istio/pkg/test/framework/resource"
31	"istio.io/istio/pkg/test/framework/resource/environment"
32	"istio.io/istio/pkg/test/util/tmpl"
33	util "istio.io/istio/tests/integration/mixer"
34)
35
36var (
37	ist        istio.Instance
38	bookinfoNs namespace.Instance
39	g          galley.Instance
40	ing        ingress.Instance
41	prom       prometheus.Instance
42)
43
44// This file contains Mixer tests that are ported from Mixer E2E tests
45
46// Port of TestMetric
47func TestIngessToPrometheus_ServiceMetric(t *testing.T) {
48	framework.
49		NewTest(t).
50		// TODO(https://github.com/istio/istio/issues/14819)
51		Label(label.Flaky).
52		RequiresEnvironment(environment.Kube).
53		Run(func(ctx framework.TestContext) {
54			label := "source_workload"
55			labelValue := "istio-ingressgateway"
56			testMetric(t, ctx, label, labelValue)
57		})
58}
59
60// Port of TestMetric
61func TestIngessToPrometheus_IngressMetric(t *testing.T) {
62	framework.
63		NewTest(t).
64		RequiresEnvironment(environment.Kube).
65		Run(func(ctx framework.TestContext) {
66			ctx.NewSubTest("SetupAndPrometheus").
67				Run(func(ctx framework.TestContext) {
68					label := "destination_service"
69					labelValue := "productpage.{{.TestNamespace}}.svc.cluster.local"
70					testMetric(t, ctx, label, labelValue)
71				})
72
73			ctx.NewSubTest("IstioctlPrometheusConnection").
74				Run(func(ctx framework.TestContext) {
75					workload := "productpage-v1"
76					testIstioctl(t, ctx, workload)
77				})
78		})
79}
80
81func testMetric(t *testing.T, ctx framework.TestContext, label string, labelValue string) { // nolint:interfacer
82	t.Helper()
83	g.ApplyConfigOrFail(
84		t,
85		bookinfoNs,
86		bookinfo.GetDestinationRuleConfigFileOrFail(t, ctx).LoadWithNamespaceOrFail(t, bookinfoNs.Name()),
87		bookinfo.NetworkingVirtualServiceAllV1.LoadWithNamespaceOrFail(t, bookinfoNs.Name()),
88	)
89	defer g.DeleteConfigOrFail(t,
90		bookinfoNs,
91		bookinfo.GetDestinationRuleConfigFileOrFail(t, ctx).LoadWithNamespaceOrFail(t, bookinfoNs.Name()),
92		bookinfo.NetworkingVirtualServiceAllV1.LoadWithNamespaceOrFail(t, bookinfoNs.Name()))
93
94	util.AllowRuleSync(t)
95
96	// Warm up
97	addr := ing.HTTPAddress()
98	url := fmt.Sprintf("http://%s/productpage", addr.String())
99	res := util.SendTraffic(ing, t, "Sending traffic", url, "", 10)
100	if res.RetCodes[200] < 1 {
101		t.Fatalf("unable to retrieve 200 from product page: %v", res.RetCodes)
102	}
103
104	label = tmpl.EvaluateOrFail(t, label, map[string]string{"TestNamespace": bookinfoNs.Name()})
105	labelValue = tmpl.EvaluateOrFail(t, labelValue, map[string]string{"TestNamespace": bookinfoNs.Name()})
106
107	// Wait for some data to arrive.
108	initial, err := prom.WaitForQuiesce(`istio_requests_total{%s=%q,response_code="200"}`, label, labelValue)
109	if err != nil {
110		t.Fatal(err)
111	}
112	t.Logf("Baseline established: initial = %v", initial)
113
114	res = util.SendTraffic(ing, t, "Sending traffic", url, "", 10)
115	if res.RetCodes[200] < 1 {
116		t.Fatalf("unable to retrieve 200 from product page: %v", res.RetCodes)
117	}
118
119	final, err := prom.WaitForQuiesce(`istio_requests_total{%s=%q,response_code="200"}`, label, labelValue)
120	if err != nil {
121		t.Fatal(err)
122	}
123	t.Logf("Quiesced to: final = %v", final)
124
125	metricName := "istio_requests_total"
126	i, err := prom.Sum(initial, nil)
127	if err != nil {
128		t.Logf("prometheus values for %s:\n%s", metricName, util.PromDump(prom, metricName))
129		t.Fatal(err)
130	}
131
132	f, err := prom.Sum(final, nil)
133	if err != nil {
134		t.Logf("prometheus values for %s:\n%s", metricName, util.PromDump(prom, metricName))
135		t.Fatal(err)
136	}
137
138	// We should see 10 requests but giving an error of 1, to make test less flaky.
139	if (f - i) < float64(9) {
140		t.Errorf("Bad metric value: got %f, want at least 9", f-i)
141	}
142}
143
144// Port of TestTcpMetric
145func TestTcpMetric(t *testing.T) {
146	framework.
147		NewTest(t).
148		// TODO(https://github.com/istio/istio/issues/18105)
149		Label(label.Flaky).
150		RequiresEnvironment(environment.Kube).
151		Run(func(ctx framework.TestContext) {
152			_ = bookinfo.DeployOrFail(t, ctx, bookinfo.Config{Namespace: bookinfoNs, Cfg: bookinfo.BookinfoRatingsv2})
153			_ = bookinfo.DeployOrFail(t, ctx, bookinfo.Config{Namespace: bookinfoNs, Cfg: bookinfo.BookinfoDB})
154
155			g.ApplyConfigOrFail(
156				t,
157				bookinfoNs,
158				bookinfo.GetDestinationRuleConfigFileOrFail(t, ctx).LoadWithNamespaceOrFail(t, bookinfoNs.Name()),
159				bookinfo.NetworkingTCPDbRule.LoadWithNamespaceOrFail(t, bookinfoNs.Name()),
160			)
161			defer g.DeleteConfigOrFail(
162				t,
163				bookinfoNs,
164				bookinfo.GetDestinationRuleConfigFileOrFail(t, ctx).LoadWithNamespaceOrFail(t, bookinfoNs.Name()),
165				bookinfo.NetworkingTCPDbRule.LoadWithNamespaceOrFail(t, bookinfoNs.Name()),
166			)
167
168			util.AllowRuleSync(t)
169
170			addr := ing.HTTPAddress()
171			url := fmt.Sprintf("http://%s/productpage", addr.String())
172			res := util.SendTraffic(ing, t, "Sending traffic", url, "", 10)
173			if res.RetCodes[200] < 1 {
174				t.Fatalf("unable to retrieve 200 from product page: %v", res.RetCodes)
175			}
176
177			query := fmt.Sprintf("sum(istio_tcp_sent_bytes_total{destination_app=\"%s\"})", "mongodb")
178			util.ValidateMetric(t, prom, query, "istio_tcp_sent_bytes_total", 1)
179
180			query = fmt.Sprintf("sum(istio_tcp_received_bytes_total{destination_app=\"%s\"})", "mongodb")
181			util.ValidateMetric(t, prom, query, "istio_tcp_received_bytes_total", 1)
182
183			query = fmt.Sprintf("sum(istio_tcp_connections_opened_total{destination_app=\"%s\"})", "mongodb")
184			util.ValidateMetric(t, prom, query, "istio_tcp_connections_opened_total", 1)
185
186			query = fmt.Sprintf("sum(istio_tcp_connections_closed_total{destination_app=\"%s\"})", "mongodb")
187			util.ValidateMetric(t, prom, query, "istio_tcp_connections_closed_total", 1)
188		})
189}
190
191func TestMain(m *testing.M) {
192	framework.
193		NewSuite("mixer_telemetry_metrics", m).
194		RequireEnvironment(environment.Kube).
195		RequireSingleCluster().
196		Label(label.CustomSetup).
197		SetupOnEnv(environment.Kube, istio.Setup(&ist, func(cfg *istio.Config) {
198			cfg.ControlPlaneValues = `
199values:
200  prometheus:
201    enabled: true
202  global:
203    disablePolicyChecks: false
204  telemetry:
205    v1:
206      enabled: true
207    v2:
208      enabled: false
209components:
210  policy:
211    enabled: true
212  telemetry:
213    enabled: true`
214		})).
215		Setup(testsetup).
216		Run()
217}
218
219func testsetup(ctx resource.Context) (err error) {
220	bookinfoNs, err = namespace.New(ctx, namespace.Config{
221		Prefix: "istio-bookinfo",
222		Inject: true,
223	})
224	if err != nil {
225		return
226	}
227	if _, err := bookinfo.Deploy(ctx, bookinfo.Config{Namespace: bookinfoNs, Cfg: bookinfo.BookInfo}); err != nil {
228		return err
229	}
230	g, err = galley.New(ctx, galley.Config{})
231	if err != nil {
232		return err
233	}
234	if _, err = mixer.New(ctx, mixer.Config{Galley: g}); err != nil {
235		return err
236	}
237	ing, err = ingress.New(ctx, ingress.Config{Istio: ist})
238	if err != nil {
239		return err
240	}
241	prom, err = prometheus.New(ctx, prometheus.Config{})
242	if err != nil {
243		return err
244	}
245	yamlText, err := bookinfo.NetworkingBookinfoGateway.LoadGatewayFileWithNamespace(bookinfoNs.Name())
246	if err != nil {
247		return err
248	}
249	err = g.ApplyConfig(bookinfoNs, yamlText)
250	if err != nil {
251		return err
252	}
253
254	return nil
255}
256