Skip to content

Service profiles integration tests#2638

Merged
dadjeibaah merged 11 commits intomasterfrom
dad/sp-integration-tests
Apr 9, 2019
Merged

Service profiles integration tests#2638
dadjeibaah merged 11 commits intomasterfrom
dad/sp-integration-tests

Conversation

@dadjeibaah
Copy link
Contributor

@dadjeibaah dadjeibaah commented Apr 4, 2019

This PR adds integration tests for service profiles that test profile generation through the tap and Open API flags. This work serves as a baseline for additional work to come listed in issue #2518 .

Related to #2518

@dadjeibaah dadjeibaah self-assigned this Apr 4, 2019
@l5d-bot
Copy link
Collaborator

l5d-bot commented Apr 4, 2019

Copy link
Member

@alpeb alpeb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good 👍
I added a few comments below.
Also I didn't see the new files emojivoto-injected.yml and Voting.proto being used, mabye they were added by mistake?

func TestServiceProfilesFromTap(t *testing.T) {
testCases := []testCase{
{
namespace: "emojivoto",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind leveraging TestHelper.GetTestNamespace to generate these namespaces with a prefix? It's not uncommon to have emojivoto/booksapp lying around and this would conflict.
This would imply removing the namespace workload in emojivoto.yml and booksapp.yml, and all the namespace fields in emojivoto.yml.

Also by using a prefix you could leave those namespaces around like the other tests do, for bin/clean-up to take care of, without having to rely on tearDown().


for _, tc := range testCases {
t.Run(fmt.Sprintf("service profiles from tap:%s", tc.namespace), func(t *testing.T) {
cmd := []string{"inject", fmt.Sprintf("testdata/%s", tc.injectYAML)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since #2595 got merged, install_test.go is leaving the control plane with auto-injection enabled, so you don't need to explicitly inject things. Although this doesn't create a problem per se, as the auto-injector will just ignore payloads that are already injected...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming that this assumes that the namespaces for these apps have linkerd.io/inject: enabled? I could see value in testing both ways, though either approach would be fine for these tests.

return true
}

func getRoutes(deployName, namespace string, helper *testutil.TestHelper) ([]string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TestHelper is already global, so you don't need to pass it here.

t.Fatalf("routes command failed: %s\n", err)
}

if len(routes) <= 1 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you could use here assertExpectedRoutes as you do below.

"strings"
"testing"

"github.com/cloudflare/cfssl/log"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably VSCode playing tricks 🙂

Copy link
Member

@siggy siggy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking good, awesome to include tests for swagger, proto, and tap.

these tests are adding nearly 5 minutes on my laptop. i suspect a lot of that is taken up by tearDown(), which will go away per @alpeb's comment.

i think a lot of the rest of the time is spent on deployment, particularly booksapp. consider deploying something smaller with less startup time, or just re-use/modify smoke-test.yaml, which should already be deployed and injected by the time your tests run. also have a look at tap_application.yaml.

$ go test -v ./test/serviceprofiles -integration-tests -linkerd `pwd`/bin/linkerd
=== RUN   TestServiceProfilesFromTap
=== RUN   TestServiceProfilesFromTap/service_profiles_from_tap:emojivoto
=== RUN   TestServiceProfilesFromTap/service_profiles_from_tap:booksapp
--- PASS: TestServiceProfilesFromTap (166.14s)
    --- PASS: TestServiceProfilesFromTap/service_profiles_from_tap:emojivoto (39.31s)
    --- PASS: TestServiceProfilesFromTap/service_profiles_from_tap:booksapp (126.83s)
=== RUN   TestServiceProfilesFromSwagger
--- PASS: TestServiceProfilesFromSwagger (1.00s)
=== RUN   TestServiceProfilesFromProto
--- PASS: TestServiceProfilesFromProto (2.12s)
PASS
ok  	github.com/linkerd/linkerd2/test/serviceprofiles	289.940s

cmd := []string{"routes", "--namespace", namespace, deployName}
out, stderr, err := helper.LinkerdRun(cmd...)
if err != nil {
log.Infof("error getting routes: %s\n", stderr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we typically don't log in these tests, better to just return the error, or call t.Errorf or t.Fatalf.


for _, tc := range testCases {
t.Run(fmt.Sprintf("service profiles from tap:%s", tc.namespace), func(t *testing.T) {
cmd := []string{"inject", fmt.Sprintf("testdata/%s", tc.injectYAML)}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming that this assumes that the namespaces for these apps have linkerd.io/inject: enabled? I could see value in testing both ways, though either approach would be fine for these tests.

t.Fatalf("routes command failed: %s\n", err.Error())
}
for _, route := range routes {
if len(route) <= 1 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think len(route) is testing the length of each route name? should this instead be...

if len(routes) <= 1 {
	t.Fatalf("Expected routes for service to be greater than or equal to 1 but got %d\n", len(routes))
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we feel about testing routes the way routes_test.go is doing it (i.e. comparing CLI output with golden files)? Not sure which one is better, but at least with routes_test.go, I can open up the golden file to see what the expected output is.

@dadjeibaah
Copy link
Contributor Author

@alpeb @siggy thanks for the review. I also was getting concerned about the amount of time the tests were adding to the test suite. However, the original issue reference emojivoto and booksapp. I can switch you using either smoke_test or tap_application instead, which I agree, would be better.

Copy link
Contributor

@ihcsim ihcsim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tests!

I think TestServiceProfilesFromTap(), TestServiceProfilesFromSwagger() and TestServiceProfilesFromProto() are essentially very similar, except for the flags passed to the profile command.

TIOLI: Group them as data sources under one TestServiceProfiles() function. I think something like this might work:

diff --git a/test/serviceprofiles/serviceprofiles_test.go b/test/serviceprofiles/serviceprofiles_test.go
index 2b7707fd..314672cd 100644
--- a/test/serviceprofiles/serviceprofiles_test.go
+++ b/test/serviceprofiles/serviceprofiles_test.go
@@ -75,6 +74,65 @@ func TestServiceProfilesFromTap(t *testing.T) {
    t.Fatalf("Expected route details for service to be at most 1 but got %d\n", len(routes))
  }

+    dataSources := []struct {
+      name string
+      args []string
+    }{
+      {
+        name: "tap",
+        args: []string{
+          tc.deployName,
+          "--tap-route-limit",
+          "5",
+          "--tap-duration",
+          "10s",
+        },
+      },
+      {
+        name: "open-api",
+        args: []string{"testdata/authors.swagger"},
+      },
+      {
+        name: "proto",
+        args: []string{"testdata/Emoji.proto"},
+      },
+    }
+
+    for _, dataSource := range dataSources {
+      routes, err := getRoutes(tc.deployName, tc.namespace, TestHelper)
+      if err != nil {
+        t.Fatalf("routes command failed: %s\n", err)
+      }
+
+      if len(routes) > 1 {
+        t.Fatalf("Expected route details for service to be at-most 1 but got %d\n", len(routes))
+      }
+
+      sourceFlag := fmt.Sprintf("--%s", dataSource.name)
+      cmd := []string{"profile", "--namespace", tc.namespace, tc.spName, sourceFlag}
+      cmd = append(cmd, dataSource.args...)
+      out, _, err := TestHelper.LinkerdRun(cmd...)
+      if err != nil {
+        t.Fatalf("profile command failed: %s\n", err.Error())
+      }
+
+      out, err = TestHelper.KubectlApply(out, tc.namespace)
+      if err != nil {
+        t.Fatalf("kubectl apply command failed:\n%s", err)
+      }
+
+      // check that authors now has more than one route
+      routes, err = getRoutes(tc.deployName, tc.namespace, TestHelper)
+      if err != nil {
+        t.Fatalf("routes command failed: %s\n", err)
+      }
+
+      if len(routes) <= 1 {
+        t.Fatalf("Expected route details for service to be greater than 1 but got %d\n", len(routes))
+      }
+    }

t.Fatalf("routes command failed: %s\n", err.Error())
}
for _, route := range routes {
if len(route) <= 1 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How do we feel about testing routes the way routes_test.go is doing it (i.e. comparing CLI output with golden files)? Not sure which one is better, but at least with routes_test.go, I can open up the golden file to see what the expected output is.

Dennis Adjei-Baah added 6 commits April 8, 2019 10:26
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
Signed-off-by: Dennis Adjei-Baah <[email protected]>
@dadjeibaah dadjeibaah force-pushed the dad/sp-integration-tests branch from c4b4f3b to ccbe530 Compare April 8, 2019 17:27
@l5d-bot
Copy link
Collaborator

l5d-bot commented Apr 8, 2019

Signed-off-by: Dennis Adjei-Baah <[email protected]>
@l5d-bot
Copy link
Collaborator

l5d-bot commented Apr 8, 2019

Signed-off-by: Dennis Adjei-Baah <[email protected]>
@l5d-bot
Copy link
Collaborator

l5d-bot commented Apr 8, 2019

Copy link
Contributor

@ihcsim ihcsim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes. Some comments below.

}

if !assertExpectedRoutes(tc.expectedRoutes, routes) {
t.Fatalf("Expected routes to have prefixes:\n%s\nbut got:\n%s",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should make this t.Errorf(), so that even if one test case fails, the loop can continue with subsequent test cases.

Signed-off-by: Dennis Adjei-Baah <[email protected]>
}

for _, tc := range testCases {
t.Run(tc.sourceName, func(t *testing.T) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to fix ci:

tc := tc // pin

@@ -0,0 +1,197 @@
# slow_cooker --http-> gateway --grpc-> t1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about reusing test/tap/testdata/tap_application.yaml?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about that too, but then I thought it would be better to have a tap_application.yaml specifically for serviceprofiles. If we want to modify t* services for specific test scenarios. e.g modifying t3 to fail 50% of the time to test out and validate retries.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense, and I see you just added --percent-failure... mind bumping the bb images to v0.0.5 to match the original version?

cmd = append(cmd, tc.args...)
out, _, err := TestHelper.LinkerdRun(cmd...)
if err != nil {
t.Fatalf("profile command failed: %s\n", err.Error())
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm getting a failure on this test (i'll dig into what's happening):

--- FAIL: TestServiceProfiles (33.18s)
    --- FAIL: TestServiceProfiles/tap (11.57s)
        serviceprofiles_test.go:106: profile command failed: exit status 1
    --- PASS: TestServiceProfiles/open-api (0.93s)

...mind adding a bit more output to help troubleshoot?

			if err != nil {
				t.Fatalf("'linkerd %s' command failed with %s: %s\n", cmd, err.Error(), out)
			}

@l5d-bot
Copy link
Collaborator

l5d-bot commented Apr 8, 2019

@siggy
Copy link
Member

siggy commented Apr 8, 2019

Signed-off-by: Dennis Adjei-Baah <[email protected]>
@l5d-bot
Copy link
Collaborator

l5d-bot commented Apr 9, 2019

Copy link
Member

@siggy siggy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm! one more fix but good to go after that 👍 🚢

cmd = append(cmd, tc.args...)
out, stderr, err := TestHelper.LinkerdRun(cmd...)
if err != nil {
t.Fatalf("'linkerd %s' command failed with %s: %s\n", cmd, err.Error(), stderr)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this failed on my local system with:

$ go test -v ./test/serviceprofiles -integration-tests -linkerd `pwd`/bin/linkerd
=== RUN   TestServiceProfiles
=== RUN   TestServiceProfiles/tap
=== RUN   TestServiceProfiles/open-api
--- FAIL: TestServiceProfiles (35.05s)
    --- FAIL: TestServiceProfiles/tap (10.43s)
        serviceprofiles_test.go:102: 'linkerd [profile --namespace l5d-integration-serviceprofile-test t1-svc --tap deploy/t1 --tap-route-limit 5 --tap-duration 10s]' command failed with exit status 1: Error: Tap duration exceeded, try increasing --tap-duration
            Usage:

i'm guessing it's a timing issue between slow-cooker's sleep 15 at startup, and the profile --tap --tap-duration 10s not seeing any requests.

i think you can mitigate this by increasing --tap-duration to 25s, but also decrease --tap-route-limit to 1, so the command should complete as soon as it sees the first request.

diff --git a/test/serviceprofiles/serviceprofiles_test.go b/test/serviceprofiles/serviceprofiles_test.go
index 34803828..c3fadc26 100644
--- a/test/serviceprofiles/serviceprofiles_test.go
+++ b/test/serviceprofiles/serviceprofiles_test.go
@@ -84,9 +84,9 @@ func TestServiceProfiles(t *testing.T) {
                                tc.args = []string{
                                        tc.deployName,
                                        "--tap-route-limit",
-                                       "5",
+                                       "1",
                                        "--tap-duration",
-                                       "10s",
+                                       "25s",
                                }
                        }

}

func getRoutes(deployName, namespace string) ([]string, error) {
cmd := []string{"routes", "--namespace", namespace, deployName}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not necessarily for this PR, but it may make parsing easier if you add --output json

Copy link
Contributor

@ihcsim ihcsim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Running test [serviceprofiles_test.go] 
=== RUN   TestServiceProfiles
=== RUN   TestServiceProfiles/tap
=== RUN   TestServiceProfiles/open-api
--- PASS: TestServiceProfiles (26.97s)
    --- PASS: TestServiceProfiles/tap (10.40s)
    --- PASS: TestServiceProfiles/open-api (0.38s)
PASS
ok      command-line-arguments  27.098s

Signed-off-by: Dennis Adjei-Baah <[email protected]>
@l5d-bot
Copy link
Collaborator

l5d-bot commented Apr 9, 2019

@dadjeibaah dadjeibaah merged commit c166b1d into master Apr 9, 2019
@dadjeibaah dadjeibaah deleted the dad/sp-integration-tests branch April 9, 2019 17:16
@admc admc mentioned this pull request Apr 9, 2019
25 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants