From fe33c52495879893dcf7b9e0d701e12d3d6c5191 Mon Sep 17 00:00:00 2001 From: Geoff Franks Date: Tue, 10 Aug 2021 17:09:06 +0000 Subject: [PATCH] [#178937872] Adds TCP routing tests to apps on isolation segments --- cats_suite_helpers/cats_suite_helpers.go | 73 ++++++++++++ helpers/skip_messages/skip_messages.go | 1 + isolation_segments/isolation_segments.go | 140 ++++++++++++++++++++++- tcp_routing/tcp_routing.go | 75 +----------- 4 files changed, 219 insertions(+), 70 deletions(-) diff --git a/cats_suite_helpers/cats_suite_helpers.go b/cats_suite_helpers/cats_suite_helpers.go index 1bcccca75..355da1e01 100644 --- a/cats_suite_helpers/cats_suite_helpers.go +++ b/cats_suite_helpers/cats_suite_helpers.go @@ -2,6 +2,8 @@ package cats_suite_helpers import ( "fmt" + "net" + "regexp" "strings" "time" @@ -59,6 +61,17 @@ func AppsDescribe(description string, callback func()) bool { }) } +func IsolatedTCPRoutingDescribe(description string, callback func()) bool { + return Describe("[tcp routing]", func() { + BeforeEach(func() { + if Config.GetIncludeRoutingIsolationSegments() || !Config.GetIncludeTCPRouting() || !Config.GetIncludeIsolationSegments() { + Skip(skip_messages.SkipIsolatedTCPRoutingMessage) + } + }) + Describe(description, callback) + }) +} + func IsolationSegmentsDescribe(description string, callback func()) bool { return Describe("[isolation_segments]", func() { BeforeEach(func() { @@ -344,3 +357,63 @@ func VolumeServicesDescribe(description string, callback func()) bool { Describe(description, callback) }) } + +func GetNServerResponses(n int, domainName, externalPort1 string) ([]string, error) { + var responses []string + + for i := 0; i < n; i++ { + resp, err := SendAndReceive(domainName, externalPort1) + if err != nil { + return nil, err + } + + responses = append(responses, resp) + } + + return responses, nil +} + +func MapTCPRoute(appName, domainName string) string { + createRouteSession := cf.Cf("map-route", appName, domainName).Wait() + Expect(createRouteSession).To(Exit(0)) + + r := regexp.MustCompile(fmt.Sprintf(`.+%s:(\d+).+`, domainName)) + return r.FindStringSubmatch(string(createRouteSession.Out.Contents()))[1] +} + +func SendAndReceive(addr string, externalPort string) (string, error) { + address := fmt.Sprintf("%s:%s", addr, externalPort) + + conn, err := net.Dial("tcp", address) + if err != nil { + return "", err + } + defer conn.Close() + + message := []byte(fmt.Sprintf("Time is %d", time.Now().Nanosecond())) + + _, err = conn.Write(message) + if err != nil { + if ne, ok := err.(*net.OpError); ok { + if ne.Temporary() { + return SendAndReceive(addr, externalPort) + } + } + + return "", err + } + + buff := make([]byte, 1024) + _, err = conn.Read(buff) + if err != nil { + if ne, ok := err.(*net.OpError); ok { + if ne.Temporary() { + return SendAndReceive(addr, externalPort) + } + } + + return "", err + } + + return string(buff), nil +} diff --git a/helpers/skip_messages/skip_messages.go b/helpers/skip_messages/skip_messages.go index 109125529..8cf9dbf55 100644 --- a/helpers/skip_messages/skip_messages.go +++ b/helpers/skip_messages/skip_messages.go @@ -38,6 +38,7 @@ const SkipWindowsMessage = `Skipping this test because config.IncludeWindows is NOTE: Ensure that your deployment includes at least one Windows cell before enabling this test.` const SkipWindowsContextPathsMessage = `Skipping this test because config.UseWindowsContextPath is set to 'false'. NOTE: Ensure that your deployment includes at least one Windows cell before enabling this test.` +const SkipIsolatedTCPRoutingMessage = `Skipping this test because either or both of config.IncludeTCPRouting and config.IncludeIsolationSegments is set to 'false', or config.IncludeRoutingIsolationSegments is set to 'true'.` const SkipIsolationSegmentsMessage = `Skipping this test because config.IncludeIsolationSegments is set to 'false'` const SkipRoutingIsolationSegmentsMessage = `Skipping this test because Config.IncludeRoutingIsolationSegments is set to 'false'.` const SkipZipkinMessage = `Skipping this test because config.IncludeZipkin is set to 'false'` diff --git a/isolation_segments/isolation_segments.go b/isolation_segments/isolation_segments.go index 4524c35d5..35676be75 100644 --- a/isolation_segments/isolation_segments.go +++ b/isolation_segments/isolation_segments.go @@ -2,6 +2,7 @@ package isolation_segments import ( "fmt" + "path/filepath" . "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers" . "github.com/onsi/ginkgo" @@ -12,6 +13,7 @@ import ( "github.com/cloudfoundry-incubator/cf-test-helpers/cf" "github.com/cloudfoundry-incubator/cf-test-helpers/helpers" "github.com/cloudfoundry-incubator/cf-test-helpers/workflowhelpers" + "github.com/cloudfoundry/cf-acceptance-tests/helpers/app_helpers" "github.com/cloudfoundry/cf-acceptance-tests/helpers/assets" "github.com/cloudfoundry/cf-acceptance-tests/helpers/random_name" "github.com/cloudfoundry/cf-acceptance-tests/helpers/v3_helpers" @@ -20,6 +22,7 @@ import ( const ( SHARED_ISOLATION_SEGMENT_GUID = "933b4c58-120b-499a-b85d-4b6fc9e2903b" binaryHi = "Hello from a binary" + IsolationSegRouterGroupName = "default-tcp" ) var _ = IsolationSegmentsDescribe("IsolationSegments", func() { @@ -44,7 +47,7 @@ var _ = IsolationSegmentsDescribe("IsolationSegments", func() { } workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() { - createQuota := cf.Cf("create-quota", quotaName, "-m", "10G", "-r", "1000", "-s", "5").Wait(TestSetup.ShortTimeout()) + createQuota := cf.Cf("create-quota", quotaName, "-m", "10G", "-r", "1000", "-s", "5", "--reserved-route-ports", "20").Wait(TestSetup.ShortTimeout()) Expect(createQuota).To(Exit(0)) createOrg := cf.Cf("create-org", orgName).Wait() @@ -229,4 +232,139 @@ var _ = IsolationSegmentsDescribe("IsolationSegments", func() { }) }) }) + + IsolatedTCPRoutingDescribe("When TCP routing is enabled", func() { + + var domainName string + + BeforeEach(func() { + domainName = fmt.Sprintf("tcp.%s", isoSegDomain) + workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() { + v3_helpers.EntitleOrgToIsolationSegment(orgGuid, isoSegGuid) + session := cf.Cf("curl", fmt.Sprintf("/v3/spaces?names=%s", spaceName)) + bytes := session.Wait().Out.Contents() + spaceGuid = v3_helpers.GetGuidFromResponse(bytes) + v3_helpers.AssignIsolationSegmentToSpace(spaceGuid, isoSegGuid) + + routerGroupOutput := string(cf.Cf("router-groups").Wait().Out.Contents()) + Expect(routerGroupOutput).To( + MatchRegexp(fmt.Sprintf("%s\\s+tcp", IsolationSegRouterGroupName)), + fmt.Sprintf("Router group %s of type tcp doesn't exist", IsolationSegRouterGroupName), + ) + + Expect(cf.Cf("create-shared-domain", + domainName, + "--router-group", IsolationSegRouterGroupName, + ).Wait()).To(Exit()) + }) + }) + + Context("external ports", func() { + var ( + appName string + tcpDropletReceiver = assets.NewAssets().TCPListener + serverId1 = "server1" + externalPort1 string + ) + + BeforeEach(func() { + appName = random_name.CATSRandomName("APP") + cmd := fmt.Sprintf("tcp-listener --serverId=%s", serverId1) + + target := cf.Cf("target", "-o", orgName, "-s", spaceName).Wait() + Expect(target).To(Exit(0), "failed targeting") + + Expect(cf.Cf("push", + "--no-route", + "--no-start", + appName, + "-p", tcpDropletReceiver, + "-b", Config.GetGoBuildpackName(), + "-m", DEFAULT_MEMORY_LIMIT, + "-f", filepath.Join(tcpDropletReceiver, "manifest.yml"), + "-c", cmd, + ).Wait()).To(Exit(0)) + externalPort1 = MapTCPRoute(appName, domainName) + appStart := cf.Cf("start", appName).Wait(Config.CfPushTimeoutDuration()) + Expect(appStart).To(Exit(0)) + Expect(string(appStart.Out.Contents())).To(ContainSubstring(isoSegName)) + }) + + AfterEach(func() { + app_helpers.AppReport(appName) + Eventually(cf.Cf("delete", appName, "-f", "-r")).Should(Exit(0)) + }) + + It("maps a single external port to an application's container port", func() { + resp, err := SendAndReceive(domainName, externalPort1) + Expect(err).ToNot(HaveOccurred()) + Expect(resp).To(ContainSubstring(serverId1)) + }) + + Context("with two different apps", func() { + var ( + secondAppName string + serverId2 = "server2" + ) + + BeforeEach(func() { + secondAppName = random_name.CATSRandomName("APP") + cmd := fmt.Sprintf("tcp-listener --serverId=%s", serverId2) + + target := cf.Cf("target", "-o", orgName, "-s", spaceName).Wait() + Expect(target).To(Exit(0), "failed targeting") + + Expect(cf.Cf("push", + "--no-route", + "--no-start", + secondAppName, + "-p", tcpDropletReceiver, + "-b", Config.GetGoBuildpackName(), + "-m", DEFAULT_MEMORY_LIMIT, + "-f", filepath.Join(tcpDropletReceiver, "manifest.yml"), + "-c", cmd, + ).Wait()).To(Exit(0)) + + Expect(cf.Cf("map-route", + secondAppName, domainName, "--port", externalPort1, + ).Wait()).To(Exit(0)) + appStart := cf.Cf("start", secondAppName).Wait(Config.CfPushTimeoutDuration()) + Expect(appStart).To(Exit(0)) + Expect(string(appStart.Out.Contents())).To(ContainSubstring(isoSegName)) + }) + + AfterEach(func() { + app_helpers.AppReport(secondAppName) + Eventually(cf.Cf("delete-route", domainName, "--port", externalPort1, "-f")).Should(Exit(0)) + Eventually(cf.Cf("delete", appName, "-f", "-r")).Should(Exit(0)) + Eventually(cf.Cf("delete", secondAppName, "-f", "-r")).Should(Exit(0)) + }) + + It("maps single external port to both applications", func() { + serverResponses, err := GetNServerResponses(10, domainName, externalPort1) + Expect(err).ToNot(HaveOccurred()) + Expect(serverResponses).To(ContainElement(ContainSubstring(serverId1))) + Expect(serverResponses).To(ContainElement(ContainSubstring(serverId2))) + }) + }) + + Context("with a second external port", func() { + var externalPort2 string + + BeforeEach(func() { + externalPort2 = MapTCPRoute(appName, domainName) + }) + + It("maps both ports to the same application", func() { + resp1, err := SendAndReceive(domainName, externalPort1) + Expect(err).ToNot(HaveOccurred()) + Expect(resp1).To(ContainSubstring(serverId1)) + + resp2, err := SendAndReceive(domainName, externalPort2) + Expect(err).ToNot(HaveOccurred()) + Expect(resp2).To(ContainSubstring(serverId1)) + }) + }) + }) + }) }) diff --git a/tcp_routing/tcp_routing.go b/tcp_routing/tcp_routing.go index a0c977e6b..80c080af6 100644 --- a/tcp_routing/tcp_routing.go +++ b/tcp_routing/tcp_routing.go @@ -2,10 +2,7 @@ package tcp_routing import ( "fmt" - "net" "path/filepath" - "regexp" - "time" "github.com/cloudfoundry-incubator/cf-test-helpers/cf" "github.com/cloudfoundry-incubator/cf-test-helpers/workflowhelpers" @@ -61,7 +58,7 @@ var _ = TCPRoutingDescribe("TCP Routing", func() { "-f", filepath.Join(tcpDropletReceiver, "manifest.yml"), "-c", cmd, ).Wait()).To(Exit(0)) - externalPort1 = mapTCPRoute(appName, domainName) + externalPort1 = MapTCPRoute(appName, domainName) Expect(cf.Cf("start", appName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0)) }) @@ -71,7 +68,7 @@ var _ = TCPRoutingDescribe("TCP Routing", func() { }) It("maps a single external port to an application's container port", func() { - resp, err := sendAndReceive(domainName, externalPort1) + resp, err := SendAndReceive(domainName, externalPort1) Expect(err).ToNot(HaveOccurred()) Expect(resp).To(ContainSubstring(serverId1)) }) @@ -111,7 +108,7 @@ var _ = TCPRoutingDescribe("TCP Routing", func() { }) It("maps single external port to both applications", func() { - serverResponses, err := getNServerResponses(10, domainName, externalPort1) + serverResponses, err := GetNServerResponses(10, domainName, externalPort1) Expect(err).ToNot(HaveOccurred()) Expect(serverResponses).To(ContainElement(ContainSubstring(serverId1))) Expect(serverResponses).To(ContainElement(ContainSubstring(serverId2))) @@ -122,78 +119,18 @@ var _ = TCPRoutingDescribe("TCP Routing", func() { var externalPort2 string BeforeEach(func() { - externalPort2 = mapTCPRoute(appName, domainName) + externalPort2 = MapTCPRoute(appName, domainName) }) It("maps both ports to the same application", func() { - resp1, err := sendAndReceive(domainName, externalPort1) + resp1, err := SendAndReceive(domainName, externalPort1) Expect(err).ToNot(HaveOccurred()) Expect(resp1).To(ContainSubstring(serverId1)) - resp2, err := sendAndReceive(domainName, externalPort2) + resp2, err := SendAndReceive(domainName, externalPort2) Expect(err).ToNot(HaveOccurred()) Expect(resp2).To(ContainSubstring(serverId1)) }) }) }) }) - -func getNServerResponses(n int, domainName, externalPort1 string) ([]string, error) { - var responses []string - - for i := 0; i < n; i++ { - resp, err := sendAndReceive(domainName, externalPort1) - if err != nil { - return nil, err - } - - responses = append(responses, resp) - } - - return responses, nil -} - -func mapTCPRoute(appName, domainName string) string { - createRouteSession := cf.Cf("map-route", appName, domainName).Wait() - Expect(createRouteSession).To(Exit(0)) - - r := regexp.MustCompile(fmt.Sprintf(`.+%s:(\d+).+`, domainName)) - return r.FindStringSubmatch(string(createRouteSession.Out.Contents()))[1] -} - -func sendAndReceive(addr string, externalPort string) (string, error) { - address := fmt.Sprintf("%s:%s", addr, externalPort) - - conn, err := net.Dial("tcp", address) - if err != nil { - return "", err - } - defer conn.Close() - - message := []byte(fmt.Sprintf("Time is %d", time.Now().Nanosecond())) - - _, err = conn.Write(message) - if err != nil { - if ne, ok := err.(*net.OpError); ok { - if ne.Temporary() { - return sendAndReceive(addr, externalPort) - } - } - - return "", err - } - - buff := make([]byte, 1024) - _, err = conn.Read(buff) - if err != nil { - if ne, ok := err.(*net.OpError); ok { - if ne.Temporary() { - return sendAndReceive(addr, externalPort) - } - } - - return "", err - } - - return string(buff), nil -}