Skip to content

Commit

Permalink
Merge pull request #1202 from geofffranks/tcp-route-testing-fixes
Browse files Browse the repository at this point in the history
Improve TCP Route Testing
  • Loading branch information
jochenehret authored Sep 13, 2024
2 parents 04f9e50 + 5e2c2d1 commit dfc2112
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 10 deletions.
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))
})
})
})
})

0 comments on commit dfc2112

Please sign in to comment.