Skip to content

Commit

Permalink
Merge pull request #474 from geofffranks/tcp-routing-isolation-tests
Browse files Browse the repository at this point in the history
[#178937872] Adds TCP routing tests to apps on isolation segments

Signed off by: awittrock@vmware.com
Signed off by: ancosta@vmware.com
  • Loading branch information
acosta11 committed Aug 19, 2021
2 parents 41f1a3d + fe33c52 commit b835be2
Show file tree
Hide file tree
Showing 4 changed files with 219 additions and 70 deletions.
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
}

0 comments on commit b835be2

Please sign in to comment.