Skip to content
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
109 changes: 76 additions & 33 deletions acceptance/framework/connhelper/connect_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ const (
StaticClientName = "static-client"
StaticServerName = "static-server"
JobName = "job-client"

retryTimeout = 120 * time.Second
)

// ConnectHelper configures a Consul cluster for connect injection tests.
Expand All @@ -49,6 +51,7 @@ type ConnectHelper struct {

// Ctx is used to deploy Consul
Ctx environment.TestContext

// UseAppNamespace is used top optionally deploy applications into a separate namespace.
// If unset, the namespace associated with Ctx is used.
UseAppNamespace bool
Expand All @@ -62,6 +65,13 @@ type ConnectHelper struct {
ConsulClient *api.Client
}

// ConnHelperOpts allows for configuring optional parameters to be passed into the
// conn helper methods. This provides added flexibility, although not every value will be used
// by every method. See documentation for more details.
type ConnHelperOpts struct {
ClientType string
}

// Setup creates a new cluster using the New*Cluster function and assigns it
// to the consulCluster field.
func (c *ConnectHelper) Setup(t *testing.T) {
Expand Down Expand Up @@ -90,6 +100,8 @@ func (c *ConnectHelper) Upgrade(t *testing.T) {
c.consulCluster.Upgrade(t, c.helmValues())
}

// KubectlOptsForApp returns options using the -apps appended namespace if
// UseAppNamespace is enabled. Otherwise, it returns the ctx options.
func (c *ConnectHelper) KubectlOptsForApp(t *testing.T) *terratestK8s.KubectlOptions {
opts := c.Ctx.KubectlOptions(t)
if !c.UseAppNamespace {
Expand All @@ -110,7 +122,7 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) {
// deployments because golang will execute them in reverse order
// (i.e. the last registered cleanup function will be executed first).
t.Cleanup(func() {
retrier := &retry.Timer{Timeout: 30 * time.Second, Wait: 100 * time.Millisecond}
retrier := &retry.Timer{Timeout: retryTimeout, Wait: 100 * time.Millisecond}
retry.RunWith(retrier, t, func(r *retry.R) {
tokens, _, err := c.ConsulClient.ACL().TokenList(nil)
require.NoError(r, err)
Expand Down Expand Up @@ -155,7 +167,7 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) {
// Check that both static-server and static-client have been injected and
// now have 2 containers.
retry.RunWith(
&retry.Timer{Timeout: 30 * time.Second, Wait: 100 * time.Millisecond}, t,
&retry.Timer{Timeout: retryTimeout, Wait: 100 * time.Millisecond}, t,
func(r *retry.R) {
for _, labelSelector := range []string{"app=static-server", "app=static-client"} {
podList, err := c.Ctx.KubernetesClient(t).CoreV1().
Expand All @@ -171,6 +183,18 @@ func (c *ConnectHelper) DeployClientAndServer(t *testing.T) {
})
}

func (c *ConnectHelper) CreateNamespace(t *testing.T, namespace string) {
opts := c.Ctx.KubectlOptions(t)
_, err := k8s.RunKubectlAndGetOutputE(t, opts, "create", "ns", namespace)
if err != nil && strings.Contains(err.Error(), "AlreadyExists") {
return
}
require.NoError(t, err)
helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, func() {
k8s.RunKubectl(t, opts, "delete", "ns", namespace)
})
}

// DeployJob deploys a job pod to the Kubernetes
// cluster which will be used to test service mesh connectivity. If the Secure
// flag is true, a pre-check is done to ensure that the ACL tokens for the test
Expand Down Expand Up @@ -257,14 +281,7 @@ func (c *ConnectHelper) SetupAppNamespace(t *testing.T) {
opts := c.KubectlOptsForApp(t)
// If we are deploying apps in another namespace, create the namespace.

_, err := k8s.RunKubectlAndGetOutputE(t, opts, "create", "ns", opts.Namespace)
if err != nil && strings.Contains(err.Error(), "AlreadyExists") {
return
}
require.NoError(t, err)
helpers.Cleanup(t, c.Cfg.NoCleanupOnFailure, c.Cfg.NoCleanup, func() {
k8s.RunKubectl(t, opts, "delete", "ns", opts.Namespace)
})
c.CreateNamespace(t, opts.Namespace)

if c.Cfg.EnableRestrictedPSAEnforcement {
// Allow anything to run in the app namespace.
Expand All @@ -273,7 +290,6 @@ func (c *ConnectHelper) SetupAppNamespace(t *testing.T) {
"pod-security.kubernetes.io/enforce-version=v1.24",
)
}

}

// CreateResolverRedirect creates a resolver that redirects to a static-server, a corresponding k8s service,
Expand All @@ -291,54 +307,81 @@ func (c *ConnectHelper) CreateResolverRedirect(t *testing.T) {
}

// TestConnectionFailureWithoutIntention ensures the connection to the static
// server fails when no intentions are configured.
func (c *ConnectHelper) TestConnectionFailureWithoutIntention(t *testing.T, clientType ...string) {
// server fails when no intentions are configured. When provided with a ClientType option
// the client is overridden, otherwise a default will be used.
func (c *ConnectHelper) TestConnectionFailureWithoutIntention(t *testing.T, connHelperOpts ConnHelperOpts) {
logger.Log(t, "checking that the connection is not successful because there's no intention")
opts := c.KubectlOptsForApp(t)
//Default to deploying static-client. If a client type is passed in (ex. job-client), use that instead.
client := StaticClientName
if len(clientType) > 0 {
client = clientType[0]
if connHelperOpts.ClientType != "" {
client = connHelperOpts.ClientType
}

if c.Cfg.EnableTransparentProxy {
k8s.CheckStaticServerConnectionFailing(t, opts, client, "http://static-server")
} else {
k8s.CheckStaticServerConnectionFailing(t, opts, client, "http://localhost:1234")
}
}

type IntentionOpts struct {
ConnHelperOpts
SourceNamespace string
DestinationNamespace string
}

// CreateIntention creates an intention for the static-server pod to connect to
// the static-client pod.
func (c *ConnectHelper) CreateIntention(t *testing.T, clientType ...string) {
// the static-client pod. opts parameter allows for overriding of some fields. If opts is empty
// then all namespaces and clients use defaults.
func (c *ConnectHelper) CreateIntention(t *testing.T, opts IntentionOpts) {
logger.Log(t, "creating intention")
//Default to deploying static-client. If a client type is passed in (ex. job-client), use that instead.
client := StaticClientName
if len(clientType) > 0 {
client = clientType[0]
if opts.ClientType != "" {
client = opts.ClientType
}
_, _, err := c.ConsulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{
Kind: api.ServiceIntentions,
Name: StaticServerName,
Sources: []*api.SourceIntention{
{
Name: client,
Action: api.IntentionActionAllow,

sourceNamespace := c.KubectlOptsForApp(t).Namespace
if opts.SourceNamespace != "" {
sourceNamespace = opts.SourceNamespace
}

destinationNamespace := c.KubectlOptsForApp(t).Namespace
if opts.DestinationNamespace != "" {
destinationNamespace = opts.DestinationNamespace
}

retrier := &retry.Timer{Timeout: retryTimeout, Wait: 100 * time.Millisecond}
retry.RunWith(retrier, t, func(r *retry.R) {
_, _, err := c.ConsulClient.ConfigEntries().Set(&api.ServiceIntentionsConfigEntry{
Kind: api.ServiceIntentions,
Name: StaticServerName,
Namespace: destinationNamespace,
Sources: []*api.SourceIntention{
{
Namespace: sourceNamespace,
Name: client,
Action: api.IntentionActionAllow,
},
},
},
}, nil)
require.NoError(t, err)
}, nil)
require.NoError(r, err)
})
}

// TestConnectionSuccess ensures the static-server pod can connect to the
// static-client pod once the intention is set.
func (c *ConnectHelper) TestConnectionSuccess(t *testing.T, clientType ...string) {
// static-client pod once the intention is set. When provided with a ClientType option
// the client is overridden, otherwise a default will be used.
func (c *ConnectHelper) TestConnectionSuccess(t *testing.T, connHelperOpts ConnHelperOpts) {
logger.Log(t, "checking that connection is successful")
opts := c.KubectlOptsForApp(t)
//Default to deploying static-client. If a client type is passed in (ex. job-client), use that instead.
client := StaticClientName
if len(clientType) > 0 {
client = clientType[0]
if connHelperOpts.ClientType != "" {
client = connHelperOpts.ClientType
}

if c.Cfg.EnableTransparentProxy {
// todo: add an assertion that the traffic is going through the proxy
k8s.CheckStaticServerConnectionSuccessful(t, opts, client, "http://static-server")
Expand Down
6 changes: 3 additions & 3 deletions acceptance/tests/cli/cli_install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ func TestInstall(t *testing.T) {
connHelper.Install(t)
connHelper.DeployClientAndServer(t)
if c.secure {
connHelper.TestConnectionFailureWithoutIntention(t)
connHelper.CreateIntention(t)
connHelper.TestConnectionFailureWithoutIntention(t, connhelper.ConnHelperOpts{})
connHelper.CreateIntention(t, connhelper.IntentionOpts{})
}

// Run proxy list and get the two results.
Expand Down Expand Up @@ -124,7 +124,7 @@ func TestInstall(t *testing.T) {
logger.Log(t, string(proxyOut))
}

connHelper.TestConnectionSuccess(t)
connHelper.TestConnectionSuccess(t, connhelper.ConnHelperOpts{})
connHelper.TestConnectionFailureWhenUnhealthy(t)
})
}
Expand Down
6 changes: 3 additions & 3 deletions acceptance/tests/connect/connect_inject_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ func TestConnectInject(t *testing.T) {
connHelper.Install(t)
connHelper.DeployClientAndServer(t)
if c.secure {
connHelper.TestConnectionFailureWithoutIntention(t)
connHelper.CreateIntention(t)
connHelper.TestConnectionFailureWithoutIntention(t, connhelper.ConnHelperOpts{})
connHelper.CreateIntention(t, connhelper.IntentionOpts{})
}

connHelper.TestConnectionSuccess(t)
connHelper.TestConnectionSuccess(t, connhelper.ConnHelperOpts{})
connHelper.TestConnectionFailureWhenUnhealthy(t)
})
}
Expand Down
8 changes: 4 additions & 4 deletions acceptance/tests/connect/connect_proxy_lifecycle_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ func TestConnectInject_ProxyLifecycleShutdown(t *testing.T) {
})

if testCfg.secure {
connHelper.TestConnectionFailureWithoutIntention(t)
connHelper.CreateIntention(t)
connHelper.TestConnectionFailureWithoutIntention(t, connhelper.ConnHelperOpts{})
connHelper.CreateIntention(t, connhelper.IntentionOpts{})
}

connHelper.TestConnectionSuccess(t)
connHelper.TestConnectionSuccess(t, connhelper.ConnHelperOpts{})

// Get static-client pod name
ns := ctx.KubectlOptions(t).Namespace
Expand Down Expand Up @@ -278,7 +278,7 @@ func TestConnectInject_ProxyLifecycleShutdownJob(t *testing.T) {
}
})

connHelper.TestConnectionSuccess(t, connhelper.JobName)
connHelper.TestConnectionSuccess(t, connhelper.ConnHelperOpts{ClientType: connhelper.JobName})

// Get job-client pod name
ns := ctx.KubectlOptions(t).Namespace
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

resources:
- service-resolver.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

apiVersion: consul.hashicorp.com/v1alpha1
kind: ServiceResolver
metadata:
name: static-server
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

resources:
- ../../../bases/static-server

patchesStrategicMerge:
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll create a PR after this to update these files to be the new strategy that isn't deprecated

- patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

apiVersion: apps/v1
kind: Deployment
metadata:
name: static-server
spec:
template:
metadata:
annotations:
"consul.hashicorp.com/connect-inject": "true"
spec:
containers:
- name: static-server
image: docker.mirror.hashicorp.services/kschoche/http-echo:latest
args:
- -text="ns2"
- -listen=:8080
ports:
- containerPort: 8080
name: http
livenessProbe:
httpGet:
port: 8080
initialDelaySeconds: 1
failureThreshold: 1
periodSeconds: 1
startupProbe:
httpGet:
port: 8080
initialDelaySeconds: 1
failureThreshold: 30
periodSeconds: 1
readinessProbe:
exec:
command: ['sh', '-c', 'test ! -f /tmp/unhealthy']
initialDelaySeconds: 1
failureThreshold: 1
periodSeconds: 1
serviceAccountName: static-server
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

resources:
- ../../../bases/static-server

patchesStrategicMerge:
- patch.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

apiVersion: apps/v1
kind: Deployment
metadata:
name: static-server
spec:
template:
metadata:
annotations:
"consul.hashicorp.com/connect-inject": "true"
spec:
containers:
- name: static-server
image: docker.mirror.hashicorp.services/kschoche/http-echo:latest
args:
- -text="dc1"
- -listen=:8080
ports:
- containerPort: 8080
name: http
livenessProbe:
httpGet:
port: 8080
initialDelaySeconds: 1
failureThreshold: 1
periodSeconds: 1
startupProbe:
httpGet:
port: 8080
initialDelaySeconds: 1
failureThreshold: 30
periodSeconds: 1
readinessProbe:
exec:
command: ['sh', '-c', 'test ! -f /tmp/unhealthy']
initialDelaySeconds: 1
failureThreshold: 1
periodSeconds: 1
serviceAccountName: static-server
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) HashiCorp, Inc.
# SPDX-License-Identifier: MPL-2.0

resources:
- ../../../bases/static-server

patchesStrategicMerge:
- patch.yaml
Loading