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

Improve TCP Route Testing #1202

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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ include_app_syslog_tcp
* `include_sso`: Flag to include the services tests that integrate with Single Sign On. `include_services` must also be set for tests to run.
* `include_tasks`: Flag to include the v3 task tests. `include_v3` must also be set for tests to run. The CC API task_creation feature flag must be enabled for these tests to pass.
* `include_tcp_routing`: Flag to include the TCP Routing tests. These tests are equivalent to the [TCP Routing tests](https://github.com/cloudfoundry/routing-acceptance-tests/blob/master/tcp_routing/tcp_routing_test.go) from the Routing Acceptance Tests.
* `tcp_domain`: Domain that will be used for apps with TCP routes
* `include_user_provided_services`: Flag to include test for user-provided services.
* `include_v3`: Flag to include tests for the v3 API.
* `include_zipkin`: Flag to include tests for Zipkin tracing. `include_routing` must also be set for tests to run. CF must be deployed with `router.tracing.enable_zipkin` set for tests to pass.
Expand All @@ -156,6 +157,8 @@ include_app_syslog_tcp
* `timeout_scale`: Used primarily to scale default timeouts for test setup and teardown actions (e.g. creating an org) as opposed to main test actions (e.g. pushing an app).
* `isolation_segment_name`: Name of the isolation segment to use for the isolation segments test.
* `isolation_segment_domain`: Domain that will route to the isolated router in the isolation segments and routing isolation segments tests. [See below](#routing-isolation-segments)
* `include_tcp_isolation_segments`: Flag to include the TCP Routing tests on Isolation Segments. These tests are equivalent to the [TCP Routing tests](https://github.com/cloudfoundry/routing-acceptance-tests/blob/master/tcp_routing/tcp_routing_test.go) from the Routing Acceptance Tests.
* `isolation_segment_tcp_domain`: Domain that will be used for isolated apps with TCP routes
* `private_docker_registry_image`: Name of the private docker image to use when testing private docker registries. [See below](#private-docker)
* `private_docker_registry_username`: Username to access the private docker repository. [See below](#private-docker)
* `private_docker_registry_password`: Password to access the private docker repository. [See below](#private-docker)
Expand Down
23 changes: 21 additions & 2 deletions cats_suite_helpers/cats_suite_helpers.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cats_suite_helpers

import (
"bytes"
"fmt"
"net"
"regexp"
Expand Down Expand Up @@ -56,7 +57,7 @@ func AppsDescribe(description string, callback func()) bool {
func IsolatedTCPRoutingDescribe(description string, callback func()) bool {
return Describe("[isolated tcp routing]", func() {
BeforeEach(func() {
if Config.GetIncludeRoutingIsolationSegments() || !Config.GetIncludeTCPIsolationSegments() {
if !Config.GetIncludeTCPIsolationSegments() {
Skip(skip_messages.SkipIsolatedTCPRoutingMessage)
}
})
Expand Down Expand Up @@ -377,6 +378,17 @@ func WindowsDescribe(description string, callback func()) bool {
})
}

func WindowsTCPRoutingDescribe(description string, callback func()) bool {
return Describe("[windows routing]", func() {
BeforeEach(func() {
if !Config.GetIncludeTCPRouting() || !Config.GetIncludeWindows() {
Skip(skip_messages.SkipTCPRoutingMessage)
}
})
Describe(description, callback)
})
}

func VolumeServicesDescribe(description string, callback func()) bool {
return Describe("[volume_services]", func() {
BeforeEach(func() {
Expand Down Expand Up @@ -451,5 +463,12 @@ func SendAndReceive(addr string, externalPort string) (string, error) {
return "", err
}

return string(buff), nil
// only grab up to the first null byte of a message since we have a predefined slice length that may not be full
i := len(buff)

if j := bytes.IndexByte(buff, 0); j > 0 {
i = j
}

return string(buff[:i]), nil
}
2 changes: 2 additions & 0 deletions helpers/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ type CatsConfig interface {
GetAdminClientSecret() string
GetApiEndpoint() string
GetAppsDomain() string
GetTCPDomain() string
GetArtifactsDirectory() string
GetBinaryBuildpackName() string
GetStaticFileBuildpackName() string
Expand All @@ -67,6 +68,7 @@ type CatsConfig interface {
GetHwcBuildpackName() string
GetIsolationSegmentName() string
GetIsolationSegmentDomain() string
GetIsolationSegmentTCPDomain() string
GetJavaBuildpackName() string
GetNamePrefix() string
GetNginxBuildpackName() string
Expand Down
18 changes: 16 additions & 2 deletions helpers/config/config_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const (
type config struct {
ApiEndpoint *string `json:"api"`
AppsDomain *string `json:"apps_domain"`
TCPDomain *string `json:"tcp_domain"`
UseHttp *bool `json:"use_http"`

AdminPassword *string `json:"admin_password"`
Expand All @@ -34,8 +35,9 @@ type config struct {

ConfigurableTestPassword *string `json:"test_password"`

IsolationSegmentName *string `json:"isolation_segment_name"`
IsolationSegmentDomain *string `json:"isolation_segment_domain"`
IsolationSegmentName *string `json:"isolation_segment_name"`
IsolationSegmentDomain *string `json:"isolation_segment_domain"`
IsolationSegmentTCPDomain *string `json:"isolation_segment_tcp_domain"`

SkipSSLValidation *bool `json:"skip_ssl_validation"`

Expand Down Expand Up @@ -811,6 +813,14 @@ func (c *config) GetAppsDomain() string {
return *c.AppsDomain
}

func (c *config) GetTCPDomain() string {
if c.TCPDomain == nil || *c.TCPDomain == "" {
return fmt.Sprintf("tcp.%s", *c.AppsDomain)
}

return *c.TCPDomain
}

func (c *config) GetSkipSSLValidation() bool {
return *c.SkipSSLValidation
}
Expand All @@ -827,6 +837,10 @@ func (c *config) GetIsolationSegmentDomain() string {
return *c.IsolationSegmentDomain
}

func (c *config) GetIsolationSegmentTCPDomain() string {
return *c.IsolationSegmentTCPDomain
}

func (c *config) GetNamePrefix() string {
return *c.NamePrefix
}
Expand Down
13 changes: 8 additions & 5 deletions isolation_segments/isolation_segments.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ var _ = IsolationSegmentsDescribe("IsolationSegments", func() {
var domainName string

BeforeEach(func() {
domainName = fmt.Sprintf("tcp.%s", isoSegDomain)
domainName = Config.GetIsolationSegmentTCPDomain()
workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
v3_helpers.EntitleOrgToIsolationSegment(orgGuid, isoSegGuid)
session := cf.Cf("curl", fmt.Sprintf("/v3/spaces?names=%s", spaceName))
Expand Down Expand Up @@ -346,10 +346,13 @@ var _ = IsolationSegmentsDescribe("IsolationSegments", func() {
})

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)))
getServerResponses := func() []string {
serverResponses, err := GetNServerResponses(10, domainName, externalPort1)
Expect(err).ToNot(HaveOccurred())
return serverResponses
}
Eventually(getServerResponses, 30).Should(ContainElement(ContainSubstring(serverId1)))
Eventually(getServerResponses, 30).Should(ContainElement(ContainSubstring(serverId2)))
})
})

Expand Down
2 changes: 1 addition & 1 deletion tcp_routing/tcp_routing.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var _ = TCPRoutingDescribe("TCP Routing", func() {
var domainName string

BeforeEach(func() {
domainName = fmt.Sprintf("tcp.%s", Config.GetAppsDomain())
domainName = Config.GetTCPDomain()
workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
routerGroupOutput := string(cf.Cf("router-groups").Wait().Out.Contents())
Expect(routerGroupOutput).To(
Expand Down
150 changes: 150 additions & 0 deletions windows/tcp_routing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package windows

import (
"fmt"
"os"
"path/filepath"

. "github.com/cloudfoundry/cf-acceptance-tests/cats_suite_helpers"
"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-test-helpers/v2/cf"
"github.com/cloudfoundry/cf-test-helpers/v2/workflowhelpers"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/gexec"
. "github.com/onsi/gomega/gexec"
)

const DefaultRouterGroupName = "default-tcp"

var _ = WindowsTCPRoutingDescribe("Windows TCP Routing", func() {
var domainName string
var compiledApp string

BeforeEach(func() {
domainName = Config.GetTCPDomain()
workflowhelpers.AsUser(TestSetup.AdminUserContext(), Config.DefaultTimeoutDuration(), func() {
routerGroupOutput := string(cf.Cf("router-groups").Wait().Out.Contents())
Expect(routerGroupOutput).To(
MatchRegexp(fmt.Sprintf("%s\\s+tcp", DefaultRouterGroupName)),
fmt.Sprintf("Router group %s of type tcp doesn't exist", DefaultRouterGroupName),
)

Expect(cf.Cf("create-shared-domain",
domainName,
"--router-group", DefaultRouterGroupName,
).Wait()).To(Exit())
})

originalDir, err := os.Getwd()
Expect(err).NotTo(HaveOccurred())
err = os.Chdir(assets.NewAssets().TCPListener)
Expect(err).NotTo(HaveOccurred())
compiledApp, err = gexec.BuildWithEnvironment(".", []string{"GOOS=windows"})
Expect(err).NotTo(HaveOccurred())
err = os.Chdir(originalDir)
Expect(err).NotTo(HaveOccurred())
})

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)

Expect(cf.Cf("push",
"--no-route",
"--no-start",
appName,
"-p", compiledApp,
"-b", Config.GetBinaryBuildpackName(),
"-s", "windows",
"-m", DEFAULT_MEMORY_LIMIT,
"-f", filepath.Join(tcpDropletReceiver, "manifest.yml"),
"-c", cmd,
).Wait()).To(Exit(0))
externalPort1 = MapTCPRoute(appName, domainName)
Expect(cf.Cf("start", appName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
})

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)

Expect(cf.Cf("push",
"--no-route",
"--no-start",
secondAppName,
"-p", compiledApp,
"-s", "windows",
"-b", Config.GetBinaryBuildpackName(),
"-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))
Expect(cf.Cf("start", secondAppName).Wait(Config.CfPushTimeoutDuration())).To(Exit(0))
})

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))
})
})
})
})