Skip to content

Commit

Permalink
testing/deployer: support tproxy in v2 for dataplane
Browse files Browse the repository at this point in the history
  • Loading branch information
rboyer committed Oct 17, 2023
1 parent 39d6c0d commit ec38b3a
Show file tree
Hide file tree
Showing 16 changed files with 773 additions and 94 deletions.
28 changes: 9 additions & 19 deletions test-integ/catalogv2/explicit_destinations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package catalogv2

import (
"fmt"
"strings"
"testing"

pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
Expand All @@ -18,7 +17,7 @@ import (
"github.com/hashicorp/consul/test-integ/topoutil"
)

// TestBasicL4ExplicitDestination sets up the following:
// TestBasicL4ExplicitDestinations sets up the following:
//
// - 1 cluster (no peering / no wanfed)
// - 3 servers in that cluster
Expand All @@ -37,8 +36,8 @@ import (
// - part1/default
// - default/nsa
// - part1/nsa
func TestBasicL4ExplicitDestination(t *testing.T) {
cfg := testBasicL4ExplicitDestinationCreator{}.NewConfig(t)
func TestBasicL4ExplicitDestinations(t *testing.T) {
cfg := testBasicL4ExplicitDestinationsCreator{}.NewConfig(t)

sp := sprawltest.Launch(t, cfg)

Expand Down Expand Up @@ -69,20 +68,11 @@ func TestBasicL4ExplicitDestination(t *testing.T) {
for _, ship := range ships {
t.Run("relationship: "+ship.String(), func(t *testing.T) {
var (
svc = ship.Caller
u = ship.Upstream
clusterPrefix string
svc = ship.Caller
u = ship.Upstream
)

if u.Peer == "" {
if u.ID.PartitionOrDefault() == "default" {
clusterPrefix = strings.Join([]string{u.PortName, u.ID.Name, u.ID.Namespace, u.Cluster, "internal"}, ".")
} else {
clusterPrefix = strings.Join([]string{u.PortName, u.ID.Name, u.ID.Namespace, u.ID.Partition, u.Cluster, "internal-v1"}, ".")
}
} else {
clusterPrefix = strings.Join([]string{u.ID.Name, u.ID.Namespace, u.Peer, "external"}, ".")
}
clusterPrefix := clusterPrefixForUpstream(u)

asserter.UpstreamEndpointStatus(t, svc, clusterPrefix+".", "HEALTHY", 1)
asserter.HTTPServiceEchoes(t, svc, u.LocalPort, "")
Expand All @@ -91,9 +81,9 @@ func TestBasicL4ExplicitDestination(t *testing.T) {
}
}

type testBasicL4ExplicitDestinationCreator struct{}
type testBasicL4ExplicitDestinationsCreator struct{}

func (c testBasicL4ExplicitDestinationCreator) NewConfig(t *testing.T) *topology.Config {
func (c testBasicL4ExplicitDestinationsCreator) NewConfig(t *testing.T) *topology.Config {
const clusterName = "dc1"

servers := topoutil.NewTopologyServerSet(clusterName+"-server", 3, []string{clusterName, "wan"}, nil)
Expand Down Expand Up @@ -129,7 +119,7 @@ func (c testBasicL4ExplicitDestinationCreator) NewConfig(t *testing.T) *topology
}
}

func (c testBasicL4ExplicitDestinationCreator) topologyConfigAddNodes(
func (c testBasicL4ExplicitDestinationsCreator) topologyConfigAddNodes(
t *testing.T,
cluster *topology.Cluster,
nodeName func() string,
Expand Down
22 changes: 22 additions & 0 deletions test-integ/catalogv2/helpers_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package catalogv2

import (
"strings"

"github.com/hashicorp/consul/testing/deployer/topology"
)

func clusterPrefixForUpstream(u *topology.Upstream) string {
if u.Peer == "" {
if u.ID.PartitionOrDefault() == "default" {
return strings.Join([]string{u.PortName, u.ID.Name, u.ID.Namespace, u.Cluster, "internal"}, ".")
} else {
return strings.Join([]string{u.PortName, u.ID.Name, u.ID.Namespace, u.ID.Partition, u.Cluster, "internal-v1"}, ".")
}
} else {
return strings.Join([]string{u.ID.Name, u.ID.Namespace, u.Peer, "external"}, ".")
}
}
214 changes: 214 additions & 0 deletions test-integ/catalogv2/implicit_destinations_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package catalogv2

import (
"fmt"
"testing"

pbauth "github.com/hashicorp/consul/proto-public/pbauth/v2beta1"
"github.com/hashicorp/consul/proto-public/pbresource"
libassert "github.com/hashicorp/consul/test/integration/consul-container/libs/assert"
"github.com/hashicorp/consul/test/integration/consul-container/libs/utils"
"github.com/hashicorp/consul/testing/deployer/sprawl/sprawltest"
"github.com/hashicorp/consul/testing/deployer/topology"

"github.com/hashicorp/consul/test-integ/topoutil"
)

// TestBasicL4ImplicitDestinations sets up the following:
//
// - 1 cluster (no peering / no wanfed)
// - 3 servers in that cluster
// - v2 arch is activated
// - for each tenancy, only using v2 constructs:
// - a server exposing 2 tcp ports
// - a client with transparent proxy enabled and no explicit upstreams
// - a traffic permission granting the client access to the service on all ports
//
// When this test is executed in CE it will only use the default/default
// tenancy.
//
// When this test is executed in Enterprise it will additionally test the same
// things within these tenancies:
//
// - part1/default
// - default/nsa
// - part1/nsa
func TestBasicL4ImplicitDestinations(t *testing.T) {
cfg := testBasicL4ImplicitDestinationsCreator{}.NewConfig(t)

sp := sprawltest.Launch(t, cfg)

var (
asserter = topoutil.NewAsserter(sp)

topo = sp.Topology()
cluster = topo.Clusters["dc1"]

ships = topo.ComputeRelationships()
)

clientV2 := sp.ResourceServiceClientForCluster(cluster.Name)

t.Log(topology.RenderRelationships(ships))

// Make sure things are truly in v2 not v1.
for _, name := range []string{
"static-server",
"static-client",
} {
libassert.CatalogV2ServiceHasEndpointCount(t, clientV2, name, nil, 1)
}

// Check relationships
for _, ship := range ships {
t.Run("relationship: "+ship.String(), func(t *testing.T) {
var (
svc = ship.Caller
u = ship.Upstream
)

clusterPrefix := clusterPrefixForUpstream(u)

asserter.UpstreamEndpointStatus(t, svc, clusterPrefix+".", "HEALTHY", 1)
if u.LocalPort > 0 {
asserter.HTTPServiceEchoes(t, svc, u.LocalPort, "")
}
asserter.FortioFetch2FortioName(t, svc, u, cluster.Name, u.ID)
})
}
}

type testBasicL4ImplicitDestinationsCreator struct{}

func (c testBasicL4ImplicitDestinationsCreator) NewConfig(t *testing.T) *topology.Config {
const clusterName = "dc1"

servers := topoutil.NewTopologyServerSet(clusterName+"-server", 3, []string{clusterName, "wan"}, nil)

cluster := &topology.Cluster{
Enterprise: utils.IsEnterprise(),
Name: clusterName,
Nodes: servers,
}

lastNode := 0
nodeName := func() string {
lastNode++
return fmt.Sprintf("%s-box%d", clusterName, lastNode)
}

c.topologyConfigAddNodes(t, cluster, nodeName, "default", "default")
if cluster.Enterprise {
c.topologyConfigAddNodes(t, cluster, nodeName, "part1", "default")
c.topologyConfigAddNodes(t, cluster, nodeName, "part1", "nsa")
c.topologyConfigAddNodes(t, cluster, nodeName, "default", "nsa")
}

return &topology.Config{
Images: topoutil.TargetImages(),
Networks: []*topology.Network{
{Name: clusterName},
{Name: "wan", Type: "wan"},
},
Clusters: []*topology.Cluster{
cluster,
},
}
}

func (c testBasicL4ImplicitDestinationsCreator) topologyConfigAddNodes(
t *testing.T,
cluster *topology.Cluster,
nodeName func() string,
partition,
namespace string,
) {
clusterName := cluster.Name

newServiceID := func(name string) topology.ServiceID {
return topology.ServiceID{
Partition: partition,
Namespace: namespace,
Name: name,
}
}

tenancy := &pbresource.Tenancy{
Partition: partition,
Namespace: namespace,
PeerName: "local",
}

serverNode := &topology.Node{
Kind: topology.NodeKindDataplane,
Version: topology.NodeVersionV2,
Partition: partition,
Name: nodeName(),
Services: []*topology.Service{
topoutil.NewFortioServiceWithDefaults(
clusterName,
newServiceID("static-server"),
topology.NodeVersionV2,
func(svc *topology.Service) {
svc.EnableTransparentProxy = true
},
),
},
}
clientNode := &topology.Node{
Kind: topology.NodeKindDataplane,
Version: topology.NodeVersionV2,
Partition: partition,
Name: nodeName(),
Services: []*topology.Service{
topoutil.NewFortioServiceWithDefaults(
clusterName,
newServiceID("static-client"),
topology.NodeVersionV2,
func(svc *topology.Service) {
svc.EnableTransparentProxy = true
svc.ImpliedUpstreams = []*topology.Upstream{
{
ID: newServiceID("static-server"),
PortName: "http",
},
{
ID: newServiceID("static-server"),
PortName: "http-alt",
},
}
},
),
},
}
trafficPerms := sprawltest.MustSetResourceData(t, &pbresource.Resource{
Id: &pbresource.ID{
Type: pbauth.TrafficPermissionsType,
Name: "static-server-perms",
Tenancy: tenancy,
},
}, &pbauth.TrafficPermissions{
Destination: &pbauth.Destination{
IdentityName: "static-server",
},
Action: pbauth.Action_ACTION_ALLOW,
Permissions: []*pbauth.Permission{{
Sources: []*pbauth.Source{{
IdentityName: "static-client",
Namespace: namespace,
}},
}},
})

cluster.Nodes = append(cluster.Nodes,
clientNode,
serverNode,
)

cluster.InitialResources = append(cluster.InitialResources,
trafficPerms,
)
}
25 changes: 22 additions & 3 deletions test-integ/topoutil/asserter.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,10 +234,21 @@ func (a *Asserter) fortioFetch2Upstream(
) (body []byte, res *http.Response) {
t.Helper()

// TODO: fortioSvc.ID.Normalize()? or should that be up to the caller?
var actualURL string
if upstream.Implied {
actualURL = fmt.Sprintf("http://%s--%s--%s.virtual.consul:%d/%s",
upstream.ID.Name,
upstream.ID.Namespace,
upstream.ID.Partition,
upstream.VirtualPort,
path,
)
} else {
actualURL = fmt.Sprintf("http://localhost:%d/%s", upstream.LocalPort, path)
}

url := fmt.Sprintf("http://%s/fortio/fetch2?url=%s", addr,
url.QueryEscape(fmt.Sprintf("http://localhost:%d/%s", upstream.LocalPort, path)),
url.QueryEscape(actualURL),
)

req, err := http.NewRequest(http.MethodPost, url, nil)
Expand All @@ -246,6 +257,7 @@ func (a *Asserter) fortioFetch2Upstream(
res, err = client.Do(req)
require.NoError(t, err)
defer res.Body.Close()

// not sure when these happen, suspect it's when the mesh gateway in the peer is not yet ready
require.NotEqual(t, http.StatusServiceUnavailable, res.StatusCode)
require.NotEqual(t, http.StatusGatewayTimeout, res.StatusCode)
Expand Down Expand Up @@ -281,7 +293,13 @@ func (a *Asserter) FortioFetch2HeaderEcho(t *testing.T, fortioSvc *topology.Serv
// similar to libassert.AssertFortioName,
// uses the /fortio/fetch2 endpoint to hit the debug endpoint on the upstream,
// and assert that the FORTIO_NAME == name
func (a *Asserter) FortioFetch2FortioName(t *testing.T, fortioSvc *topology.Service, upstream *topology.Upstream, clusterName string, sid topology.ServiceID) {
func (a *Asserter) FortioFetch2FortioName(
t *testing.T,
fortioSvc *topology.Service,
upstream *topology.Upstream,
clusterName string,
sid topology.ServiceID,
) {
t.Helper()

var (
Expand All @@ -295,6 +313,7 @@ func (a *Asserter) FortioFetch2FortioName(t *testing.T, fortioSvc *topology.Serv

retry.RunWith(&retry.Timer{Timeout: 60 * time.Second, Wait: time.Millisecond * 500}, t, func(r *retry.R) {
body, res := a.fortioFetch2Upstream(r, client, addr, upstream, path)

require.Equal(r, http.StatusOK, res.StatusCode)

// TODO: not sure we should retry these?
Expand Down
12 changes: 8 additions & 4 deletions test-integ/topoutil/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,14 @@ func NewFortioServiceWithDefaults(
}

if nodeVersion == topology.NodeVersionV2 {
svc.Ports = map[string]int{
"http": httpPort,
"http-alt": httpPort,
"grpc": grpcPort,
svc.Ports = map[string]*topology.Port{
// TODO(rb/v2): once L7 works in v2 switch these back
"http": {Number: httpPort, Protocol: "tcp"},
"http-alt": {Number: httpPort, Protocol: "tcp"},
"grpc": {Number: grpcPort, Protocol: "tcp"},
// "http": {Number: httpPort, Protocol: "http"},
// "http-alt": {Number: httpPort, Protocol: "http"},
// "grpc": {Number: grpcPort, Protocol: "grpc"},
}
} else {
svc.Port = httpPort
Expand Down
Loading

0 comments on commit ec38b3a

Please sign in to comment.