-
Notifications
You must be signed in to change notification settings - Fork 600
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
E2E test for Channel Tracing #1858
Changes from 34 commits
ff72461
ca5f88a
9ca4206
5d4cd00
b442008
a9a55d1
5f536c5
7c9b13c
68693df
e165ca5
3b33fb9
ca04836
3a3fc29
e833388
3a84ed5
793a2f1
dadd66d
28ad88d
8eedb51
8e96712
ca9f3c3
9c765c7
5340ad2
1a8a05c
0fbd809
a13ed44
30c5b0d
b7acbfb
1a36cd2
5ef3845
fd4e160
04f6246
86fe35a
22d45d3
037fe7b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// +build e2e | ||
|
||
/* | ||
Copyright 2019 The Knative 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 conformance | ||
|
||
import ( | ||
"fmt" | ||
"regexp" | ||
"testing" | ||
"time" | ||
|
||
"k8s.io/apimachinery/pkg/util/uuid" | ||
"knative.dev/eventing/test/base/resources" | ||
"knative.dev/eventing/test/common" | ||
"knative.dev/eventing/test/conformance/helpers" | ||
"knative.dev/pkg/test/zipkin" | ||
) | ||
|
||
func TestChannelTracing(t *testing.T) { | ||
testCases := map[string]struct { | ||
incomingTraceId bool | ||
istio bool | ||
}{ | ||
//"no incoming trace id": {}, | ||
"includes incoming trace id": { | ||
incomingTraceId: true, | ||
}, /* | ||
"caller has istio": { | ||
istio: true, | ||
},*/ | ||
} | ||
|
||
for n, tc := range testCases { | ||
loggerPodName := "logger" | ||
t.Run(n, func(t *testing.T) { | ||
channelTestRunner.RunTests(t, common.FeatureBasic, func(st *testing.T, channel string) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function needs to be defined as a helper function, if you want it to be ported to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. I didn't know, thanks for pointing it out. |
||
// Don't accidentally use t, use st instead. To ensure this, shadow 't' to a useless | ||
// type. | ||
t := struct{}{} | ||
_ = fmt.Sprintf("%s", t) | ||
|
||
client := common.Setup(st, true) | ||
defer common.TearDown(client) | ||
|
||
// Do NOT call zipkin.CleanupZipkinTracingSetup. That will be called exactly once in | ||
Harwayne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// TestMain. | ||
helpers.SetupZipkinTracing(st, client) | ||
|
||
mustContain := setupChannelTracing(st, channel, client, loggerPodName, tc.incomingTraceId) | ||
assertLogContents(st, client, loggerPodName, mustContain) | ||
|
||
traceID := getTraceID(st, client, loggerPodName) | ||
|
||
// We expect the following spans: | ||
// 1. Sending pod sends event to Channel (only if the sending pod generates a span). | ||
// 2. Channel receives event from sending pod. | ||
// 3. Channel sends event to logging pod. | ||
// 4. Logging pod receives event from Channel. | ||
// So we expect 4 spans if the sending pod is generating a span, 3 if not. | ||
expectedNumSpans := 3 | ||
if tc.incomingTraceId { | ||
expectedNumSpans = 4 | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you explain why the number of spans is different when the incoming message includes trace Id ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a comment to the code, does it make sense? |
||
trace, err := zipkin.JSONTrace(traceID, expectedNumSpans, 60*time.Second) | ||
Harwayne marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
st.Fatalf("Unable to get trace %q: %v. Trace so far %+v", traceID, err, trace) | ||
} | ||
st.Logf("I got the trace, %q!\n%+v", traceID, trace) | ||
|
||
// TODO: Assert information in the trace. | ||
}) | ||
}) | ||
} | ||
} | ||
|
||
// setupChannelTracing is the general setup for TestChannelTracing. It creates the following: | ||
// SendEvents (Pod) -> Channel -> Subscription -> K8s Service -> LogEvents (Pod) | ||
// It returns a string that is expected to be sent by the SendEvents Pod and should be present in | ||
// the LogEvents Pod logs. | ||
func setupChannelTracing(t *testing.T, channel string, client *common.Client, loggerPodName string, incomingTraceId bool) string { | ||
// Create the Channel. | ||
channelName := "ch" | ||
channelTypeMeta := common.GetChannelTypeMeta(channel) | ||
client.CreateChannelOrFail(channelName, channelTypeMeta) | ||
|
||
// Create the 'sink', a LogEvents Pod and a K8s Service that points to it. | ||
pod := resources.EventDetailsPod(loggerPodName) | ||
client.CreatePodOrFail(pod, common.WithService(loggerPodName)) | ||
|
||
// Create the Subscription linking the Channel to the LogEvents K8s Service. | ||
client.CreateSubscriptionOrFail( | ||
"sub", | ||
channelName, | ||
channelTypeMeta, | ||
resources.WithSubscriberForSubscription(loggerPodName), | ||
) | ||
|
||
// Wait for all test resources to be ready, so that we can start sending events. | ||
if err := client.WaitForAllTestResourcesReady(); err != nil { | ||
t.Fatalf("Failed to get all test resources ready: %v", err) | ||
} | ||
|
||
// Everything is setup to receive an event. Generate a CloudEvent. | ||
senderName := "sender" | ||
eventID := fmt.Sprintf("%s", uuid.NewUUID()) | ||
body := fmt.Sprintf("TestChannelTracing %s", eventID) | ||
event := &resources.CloudEvent{ | ||
ID: eventID, | ||
Source: senderName, | ||
Type: resources.CloudEventDefaultType, | ||
Data: fmt.Sprintf(`{"msg":%q}`, body), | ||
Encoding: resources.CloudEventEncodingBinary, | ||
} | ||
|
||
// Send the CloudEvent (either with or without tracing inside the SendEvents Pod). | ||
sendEvent := client.SendFakeEventToAddressable | ||
if incomingTraceId { | ||
sendEvent = client.SendFakeEventWithTracingToAddressable | ||
} | ||
if err := sendEvent(senderName, channelName, channelTypeMeta, event); err != nil { | ||
t.Fatalf("Failed to send fake CloudEvent to the channel %q", channelName) | ||
} | ||
return body | ||
} | ||
|
||
// assertLogContents verifies that loggerPodName's logs contains mustContain. It is used to show | ||
// that the expected event was sent to the logger Pod. | ||
func assertLogContents(t *testing.T, client *common.Client, loggerPodName string, mustContain string) { | ||
if err := client.CheckLog(loggerPodName, common.CheckerContains(mustContain)); err != nil { | ||
t.Fatalf("String %q not found in logs of logger pod %q: %v", mustContain, loggerPodName, err) | ||
} | ||
} | ||
|
||
// getTraceID gets the TraceID from loggerPodName's logs. It will also assert that body is present. | ||
func getTraceID(t *testing.T, client *common.Client, loggerPodName string) string { | ||
logs, err := client.GetLog(loggerPodName) | ||
if err != nil { | ||
t.Fatalf("Error getting logs: %v", err) | ||
} | ||
// This is the format that the eventdetails image prints headers. | ||
re := regexp.MustCompile("\nGot Header X-B3-Traceid: ([a-zA-Z0-9]{32})\n") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a bit hacky... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Totally agree, what do you recommend? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can define a library to add and parse headers to/from the logs, each header will be quoted with something like This requires bigger changes, so it can be in a different PR. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #1945 to track. |
||
matches := re.FindStringSubmatch(logs) | ||
if len(matches) != 2 { | ||
t.Fatalf("Unable to extract traceID: %q", logs) | ||
} | ||
traceID := matches[1] | ||
return traceID | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// +build e2e | ||
|
||
/* | ||
Copyright 2019 The Knative 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 helpers | ||
|
||
import ( | ||
"sync" | ||
"testing" | ||
|
||
"knative.dev/eventing/test/common" | ||
"knative.dev/pkg/test/zipkin" | ||
) | ||
|
||
// SetupZipkinTracing sets up port forwarding to Zipkin and sets the knative-eventing tracing config | ||
// to debug mode (everything is sampled). | ||
func SetupZipkinTracing(t *testing.T, client *common.Client) { | ||
// Do NOT call zipkin.CleanupZipkinTracingSetup. That will be called exactly once in | ||
// TestMain. | ||
zipkin.SetupZipkinTracing(client.Kube.Kube, t.Logf) | ||
setTracingConfigToDebugMode(t, client) | ||
} | ||
|
||
var setTracingConfigOnce = sync.Once{} | ||
|
||
// TODO Do we need a tear down method as well? | ||
func setTracingConfigToDebugMode(t *testing.T, client *common.Client) { | ||
setTracingConfigOnce.Do(func() { | ||
err := client.Kube.UpdateConfigMap("knative-eventing", "config-tracing", map[string]string{ | ||
"backend": "zipkin", | ||
"zipkin-endpoint": "http://zipkin.istio-system.svc.cluster.local:9411/api/v2/spans", | ||
"debug": "true", | ||
"sample-rate": "1.0", | ||
}) | ||
if err != nil { | ||
t.Fatalf("Unable to set the ConfigMap: %v", err) | ||
} | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, so this one is still not working? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Works consistently on my Linux desktop, is flaky on my Mac laptop. For now, I'll leave it commented out (or I could remove the line if you prefer), so that the rest of the test can get in.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed it for now, rather than just commenting it out.