Skip to content
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

[#178937872] Adds TCP routing tests to apps on isolation segments #474

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions cats_suite_helpers/cats_suite_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package cats_suite_helpers

import (
"fmt"
"net"
"regexp"
"strings"
"time"

Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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
}
1 change: 1 addition & 0 deletions helpers/skip_messages/skip_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -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'`
Expand Down
140 changes: 139 additions & 1 deletion isolation_segments/isolation_segments.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package isolation_segments

import (
"fmt"
"path/filepath"

. "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers"
. "github.com/onsi/ginkgo"
Expand All @@ -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"
Expand All @@ -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() {
Expand All @@ -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()
Expand Down Expand Up @@ -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))
})
})
})
})
})
75 changes: 6 additions & 69 deletions tcp_routing/tcp_routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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))
})

Expand All @@ -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))
})
Expand Down Expand Up @@ -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)))
Expand All @@ -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
}