mirror of https://github.com/linkerd/linkerd2.git
IPv6/dual-stack integration tests (#12575)
* IPv6 integration tests This adds a new test `TestDualStack` to the deep suite that ensures requests to a dual stack service are always routed the the IPv6 endpoint. It also amends other tests in the suite for them to work in IPv6-only clusters: - skipports: replaced the booksapp with emojivoto, given the servers in the former don't bind to IPv6 addresses - endpoints: amended the regexes to include IPv6 addresses - localhost: bumped nginx for it to bind to the IPv6 loopback as well Note the `TestDualStack` test is disabled by default because Github runners don't support IPv6. To run it locally, first deploy a dual-stack cluster via: ``` kind create cluster --config test/integration/deep/kind-dualstack.yml ``` (for testing IPv6-only clusters, use the `kind-ipv6.yml` config) Then load the images and trigger the test with: ``` bin/tests --name deep-dual-stack --skip-cluster-create $PWD/target/cli/linux-amd64/linkerd ```
This commit is contained in:
parent
9b4405761f
commit
b21686a9be
|
@ -14,7 +14,9 @@ testdir="$bindir"/../test/integration
|
||||||
|
|
||||||
export default_test_names=(deep deep-native-sidecar viz external helm-upgrade uninstall upgrade-edge default-policy-deny rsa-ca)
|
export default_test_names=(deep deep-native-sidecar viz external helm-upgrade uninstall upgrade-edge default-policy-deny rsa-ca)
|
||||||
export external_resource_test_names=(external-resources)
|
export external_resource_test_names=(external-resources)
|
||||||
export all_test_names=(cluster-domain cni-calico-deep multicluster "${default_test_names[*]}" "${external_resource_test_names[*]}")
|
# TODO(alpeb): add test cni-calico-deep-dual-stack
|
||||||
|
export dual_stack_test_names=(deep-dual-stack)
|
||||||
|
export all_test_names=(cluster-domain cni-calico-deep multicluster "${default_test_names[*]}" "${external_resource_test_names[*]}" "${dual_stack_test_names[*]}")
|
||||||
images_load_default=(proxy controller policy-controller web metrics-api tap)
|
images_load_default=(proxy controller policy-controller web metrics-api tap)
|
||||||
|
|
||||||
tests_usage() {
|
tests_usage() {
|
||||||
|
@ -448,6 +450,10 @@ run_deep-native-sidecar_test() {
|
||||||
run_test "$testdir/deep/..." --native-sidecar
|
run_test "$testdir/deep/..." --native-sidecar
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_deep-dual-stack_test() {
|
||||||
|
run_test "$testdir/deep/..." --dual-stack
|
||||||
|
}
|
||||||
|
|
||||||
run_default-policy-deny_test() {
|
run_default-policy-deny_test() {
|
||||||
export default_inbound_policy='deny'
|
export default_inbound_policy='deny'
|
||||||
run_test "$testdir/install/..."
|
run_test "$testdir/install/..."
|
||||||
|
|
|
@ -0,0 +1,180 @@
|
||||||
|
package dualstack
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/linkerd/linkerd2/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IP struct {
|
||||||
|
IP string `json:"ip"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var TestHelper *testutil.TestHelper
|
||||||
|
|
||||||
|
func TestMain(m *testing.M) {
|
||||||
|
TestHelper = testutil.NewTestHelper()
|
||||||
|
// Block test execution until control plane is running
|
||||||
|
TestHelper.WaitUntilDeployReady(testutil.LinkerdDeployReplicasEdge)
|
||||||
|
os.Exit(m.Run())
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestDualStack creates an injected pod that starts two servers, one listening
|
||||||
|
// on the IPv4 wildcard address and serving the string "IPv4", and another
|
||||||
|
// listening on the IPv6 wildcard address and serving the string "IPv6". They
|
||||||
|
// are fronted by a DualStack Service. We test that we can reach those two IPs
|
||||||
|
// directly, and that making a request to the service's FQDN always hits the
|
||||||
|
// IPv6 endpoint.
|
||||||
|
func TestDualStack(t *testing.T) {
|
||||||
|
if !TestHelper.DualStack() {
|
||||||
|
t.Skip("Skipping Skip DualStack test")
|
||||||
|
}
|
||||||
|
|
||||||
|
TestHelper.WithDataPlaneNamespace(context.Background(), "dualstack-test", map[string]string{}, t, func(t *testing.T, ns string) {
|
||||||
|
out, err := TestHelper.Kubectl("",
|
||||||
|
"create", "configmap", "go-app",
|
||||||
|
"--from-file=main.go=testdata/ipfamilies-server.go",
|
||||||
|
"-n", ns,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v\noutput:\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err = TestHelper.Kubectl("",
|
||||||
|
"apply", "-f", "testdata/ipfamilies-server-client.yml",
|
||||||
|
"-n", ns,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v\noutput:\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkPods(t, ns, "ipfamilies-server")
|
||||||
|
checkPods(t, ns, "client")
|
||||||
|
|
||||||
|
var clientIPv6, serverIPv4, serverIPv6 string
|
||||||
|
|
||||||
|
t.Run("Retrieve pod IPs", func(t *testing.T) {
|
||||||
|
cmd := []string{
|
||||||
|
"get", "po",
|
||||||
|
"-o", "jsonpath='{.items[*].status.podIPs}'",
|
||||||
|
"-n", ns,
|
||||||
|
}
|
||||||
|
|
||||||
|
out, err = TestHelper.Kubectl("", append(cmd, "-l", "app=server")...)
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v\noutput:\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
var IPs []IP
|
||||||
|
out = strings.Trim(out, "'")
|
||||||
|
if err = json.Unmarshal([]byte(out), &IPs); err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "error unmarshaling JSON", "error unmarshaling JSON '%s': %s", out, err)
|
||||||
|
}
|
||||||
|
if len(IPs) != 2 {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected number of IPs", "expected 2 IPs, got %s", fmt.Sprint(len(IPs)))
|
||||||
|
}
|
||||||
|
serverIPv4 = IPs[0].IP
|
||||||
|
serverIPv6 = IPs[1].IP
|
||||||
|
|
||||||
|
out, err = TestHelper.Kubectl("", append(cmd, "-l", "app=client")...)
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v\noutput:\n%s", err, out)
|
||||||
|
}
|
||||||
|
|
||||||
|
out = strings.Trim(out, "'")
|
||||||
|
if err = json.Unmarshal([]byte(out), &IPs); err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "error unmarshaling JSON", "error unmarshaling JSON '%s': %s", out, err)
|
||||||
|
}
|
||||||
|
if len(IPs) != 2 {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected number of IPs", "expected 2 IPs, got %s", fmt.Sprint(len(IPs)))
|
||||||
|
}
|
||||||
|
clientIPv6 = IPs[1].IP
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Apply policy", func(t *testing.T) {
|
||||||
|
file, err := os.Open("testdata/ipfamilies-policy.yml")
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
manifest, err := io.ReadAll(file)
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
in := strings.ReplaceAll(string(manifest), "{IPv6}", clientIPv6)
|
||||||
|
out, err = TestHelper.KubectlApply(in, ns)
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v\noutput:\n%s", err, out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Hit IPv4 addr directly", func(t *testing.T) {
|
||||||
|
out, err = TestHelper.Kubectl("",
|
||||||
|
"exec", "deploy/client",
|
||||||
|
"-c", "curl",
|
||||||
|
"-n", ns,
|
||||||
|
"--",
|
||||||
|
"curl", "-s", "http://"+serverIPv4+":8080",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v\noutput:\n%s", err, out)
|
||||||
|
}
|
||||||
|
if out != "IPv4\n" {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected output", "expected 'IPv4', received '%s'", out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Hit IPv6 addr directly", func(t *testing.T) {
|
||||||
|
out, err = TestHelper.Kubectl("",
|
||||||
|
"exec", "deploy/client",
|
||||||
|
"-c", "curl",
|
||||||
|
"-n", ns,
|
||||||
|
"--",
|
||||||
|
"curl", "-s", "http://["+serverIPv6+"]:8080",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v\noutput:\n%s", err, out)
|
||||||
|
}
|
||||||
|
if out != "IPv6\n" {
|
||||||
|
testutil.AnnotatedFatalf(t, "expected 'IPv6', received '%s'", out)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Hit FQDN directly (should always resolve to IPv6)", func(t *testing.T) {
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
out, err = TestHelper.Kubectl("",
|
||||||
|
"exec", "deploy/client",
|
||||||
|
"-c", "curl",
|
||||||
|
"-n", ns,
|
||||||
|
"--",
|
||||||
|
"curl", "-s", "http://ipfamilies-server:8080",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: %v\noutput:\n%s", err, out)
|
||||||
|
}
|
||||||
|
if out != "IPv6\n" {
|
||||||
|
testutil.AnnotatedFatalf(t, "expected 'IPv6', received '%s'", out)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPods(t *testing.T, ns, pod string) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
if err := TestHelper.CheckPods(context.Background(), ns, pod, 1); err != nil {
|
||||||
|
//nolint:errorlint
|
||||||
|
if rce, ok := err.(*testutil.RestartCountError); ok {
|
||||||
|
testutil.AnnotatedWarn(t, "CheckPods timed-out", rce)
|
||||||
|
} else {
|
||||||
|
testutil.AnnotatedError(t, "CheckPods timed-out", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
apiVersion: policy.linkerd.io/v1beta2
|
||||||
|
kind: Server
|
||||||
|
metadata:
|
||||||
|
name: ipfamilies
|
||||||
|
spec:
|
||||||
|
podSelector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: ipfamilies-server
|
||||||
|
port: http
|
||||||
|
proxyProtocol: HTTP/1
|
||||||
|
---
|
||||||
|
apiVersion: policy.linkerd.io/v1alpha1
|
||||||
|
kind: AuthorizationPolicy
|
||||||
|
metadata:
|
||||||
|
name: ipfamilies
|
||||||
|
spec:
|
||||||
|
targetRef:
|
||||||
|
group: policy.linkerd.io
|
||||||
|
kind: Server
|
||||||
|
name: ipfamilies
|
||||||
|
requiredAuthenticationRefs:
|
||||||
|
- name: ipfamilies
|
||||||
|
kind: NetworkAuthentication
|
||||||
|
group: policy.linkerd.io
|
||||||
|
---
|
||||||
|
apiVersion: policy.linkerd.io/v1alpha1
|
||||||
|
kind: NetworkAuthentication
|
||||||
|
metadata:
|
||||||
|
name: ipfamilies
|
||||||
|
spec:
|
||||||
|
networks:
|
||||||
|
- cidr: {IPv6}/128
|
|
@ -0,0 +1,73 @@
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: ipfamilies-server
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: server
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
linkerd.io/inject: enabled
|
||||||
|
labels:
|
||||||
|
app: server
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- image: ghcr.io/alpeb/family-server:v1
|
||||||
|
image: golang:1.22-alpine
|
||||||
|
name: ipfamilies-server
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: http
|
||||||
|
protocol: TCP
|
||||||
|
command: ["/bin/sh"]
|
||||||
|
args:
|
||||||
|
- -c
|
||||||
|
- 'go run /go/src/app/main.go'
|
||||||
|
volumeMounts:
|
||||||
|
- name: go-app
|
||||||
|
mountPath: /go/src/app
|
||||||
|
volumes:
|
||||||
|
- name: go-app
|
||||||
|
configMap:
|
||||||
|
name: go-app
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: ipfamilies-server
|
||||||
|
spec:
|
||||||
|
ipFamilies:
|
||||||
|
- IPv4
|
||||||
|
- IPv6
|
||||||
|
ipFamilyPolicy: RequireDualStack
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 8080
|
||||||
|
protocol: TCP
|
||||||
|
targetPort: http
|
||||||
|
selector:
|
||||||
|
app: server
|
||||||
|
type: ClusterIP
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: client
|
||||||
|
spec:
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app: client
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
annotations:
|
||||||
|
linkerd.io/inject: enabled
|
||||||
|
labels:
|
||||||
|
app: client
|
||||||
|
spec:
|
||||||
|
containers:
|
||||||
|
- name: curl
|
||||||
|
image: curlimages/curl
|
||||||
|
command: [ "sh", "-c", "--" ]
|
||||||
|
args: [ "sleep infinity" ]
|
|
@ -0,0 +1,41 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
ipv4Handler struct{}
|
||||||
|
ipv6Handler struct{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ipv4Handler) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
fmt.Fprintf(w, "IPv4\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ipv6Handler) ServeHTTP(w http.ResponseWriter, _ *http.Request) {
|
||||||
|
fmt.Fprintf(w, "IPv6\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.Print("Server started")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ln, err := net.Listen("tcp4", "0.0.0.0:8080")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
srv := &http.Server{Handler: ipv4Handler{}}
|
||||||
|
log.Fatal(srv.Serve(ln))
|
||||||
|
}()
|
||||||
|
|
||||||
|
ln, err := net.Listen("tcp6", "[::]:8080")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
srv := &http.Server{Handler: ipv6Handler{}}
|
||||||
|
log.Fatal(srv.Serve(ln))
|
||||||
|
}
|
|
@ -108,7 +108,7 @@ func createTestCaseTable(controlNs, endpointNs string) []testCase {
|
||||||
expectedRE: `\[
|
expectedRE: `\[
|
||||||
\{
|
\{
|
||||||
"namespace": "(\S*)",
|
"namespace": "(\S*)",
|
||||||
"ip": "\d+\.\d+\.\d+\.\d+",
|
"ip": "[a-f0-9.:]+",
|
||||||
"port": 8086,
|
"port": 8086,
|
||||||
"pod": "linkerd-destination\-[a-f0-9]+\-[a-z0-9]+",
|
"pod": "linkerd-destination\-[a-f0-9]+\-[a-z0-9]+",
|
||||||
"service": "linkerd-dst\.\S*",
|
"service": "linkerd-dst\.\S*",
|
||||||
|
@ -125,7 +125,7 @@ func createTestCaseTable(controlNs, endpointNs string) []testCase {
|
||||||
expectedRE: `\[
|
expectedRE: `\[
|
||||||
\{
|
\{
|
||||||
"namespace": "(\S*)",
|
"namespace": "(\S*)",
|
||||||
"ip": "\d+\.\d+\.\d+\.\d+",
|
"ip": "[a-f0-9.:]+",
|
||||||
"port": 8080,
|
"port": 8080,
|
||||||
"pod": "linkerd-identity\-[a-f0-9]+\-[a-z0-9]+",
|
"pod": "linkerd-identity\-[a-f0-9]+\-[a-z0-9]+",
|
||||||
"service": "linkerd-identity\.\S*",
|
"service": "linkerd-identity\.\S*",
|
||||||
|
@ -142,7 +142,7 @@ func createTestCaseTable(controlNs, endpointNs string) []testCase {
|
||||||
expectedRE: `\[
|
expectedRE: `\[
|
||||||
\{
|
\{
|
||||||
"namespace": "(\S*)",
|
"namespace": "(\S*)",
|
||||||
"ip": "\d+\.\d+\.\d+\.\d+",
|
"ip": "[a-f0-9.:]+",
|
||||||
"port": 8443,
|
"port": 8443,
|
||||||
"pod": "linkerd-proxy-injector-[a-f0-9]+\-[a-z0-9]+",
|
"pod": "linkerd-proxy-injector-[a-f0-9]+\-[a-z0-9]+",
|
||||||
"service": "linkerd-proxy-injector\.\S*",
|
"service": "linkerd-proxy-injector\.\S*",
|
||||||
|
@ -159,7 +159,7 @@ func createTestCaseTable(controlNs, endpointNs string) []testCase {
|
||||||
expectedRE: `\[
|
expectedRE: `\[
|
||||||
\{
|
\{
|
||||||
"namespace": "(\S*)",
|
"namespace": "(\S*)",
|
||||||
"ip": "\d+\.\d+\.\d+\.\d+",
|
"ip": "[a-f0-9.:]+",
|
||||||
"port": 8080,
|
"port": 8080,
|
||||||
"pod": "nginx-[a-f0-9]+\-[a-z0-9]+",
|
"pod": "nginx-[a-f0-9]+\-[a-z0-9]+",
|
||||||
"service": "nginx\.\S*",
|
"service": "nginx\.\S*",
|
||||||
|
|
|
@ -126,6 +126,10 @@ func TestInstall(t *testing.T) {
|
||||||
cmd = append(cmd, "--set", "proxy.nativeSidecar=true")
|
cmd = append(cmd, "--set", "proxy.nativeSidecar=true")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if TestHelper.DualStack() {
|
||||||
|
cmd = append(cmd, "--set", "disableIPv6=false")
|
||||||
|
}
|
||||||
|
|
||||||
// Pipe cmd & args to `linkerd`
|
// Pipe cmd & args to `linkerd`
|
||||||
out, err = TestHelper.LinkerdRun(cmd...)
|
out, err = TestHelper.LinkerdRun(cmd...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
kind: Cluster
|
||||||
|
apiVersion: kind.x-k8s.io/v1alpha4
|
||||||
|
networking:
|
||||||
|
ipFamily: dual
|
|
@ -0,0 +1,4 @@
|
||||||
|
kind: Cluster
|
||||||
|
apiVersion: kind.x-k8s.io/v1alpha4
|
||||||
|
networking:
|
||||||
|
ipFamily: ipv6
|
|
@ -3,6 +3,7 @@ package localhost
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -163,7 +164,16 @@ func TestLocalhostRouting(t *testing.T) {
|
||||||
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: no IP address found for %s/%s", ns, podName)
|
testutil.AnnotatedFatalf(t, "unexpected error", "unexpected error: no IP address found for %s/%s", ns, podName)
|
||||||
}
|
}
|
||||||
|
|
||||||
statusCode, err := TestHelper.Kubectl("", append(execCommand, podIP)...)
|
addr, err := netip.ParseAddr(podIP)
|
||||||
|
if err != nil {
|
||||||
|
testutil.AnnotatedFatalf(t, "Invalid IP", "Invalid IP '%s': %s", podIP, err)
|
||||||
|
}
|
||||||
|
if addr.Is6() {
|
||||||
|
podIP = fmt.Sprintf("[%s]", podIP)
|
||||||
|
}
|
||||||
|
url := fmt.Sprintf("http://%s:80", podIP)
|
||||||
|
|
||||||
|
statusCode, err := TestHelper.Kubectl("", append(execCommand, url)...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
testutil.AnnotatedFatalf(t, "unexpected error received when calling 'kubectl exec'", "unexpected error received when calling 'kubectl exec': %v", err)
|
testutil.AnnotatedFatalf(t, "unexpected error received when calling 'kubectl exec'", "unexpected error received when calling 'kubectl exec': %v", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ spec:
|
||||||
spec:
|
spec:
|
||||||
containers:
|
containers:
|
||||||
- name: nginx
|
- name: nginx
|
||||||
image: nginx:1.14.2
|
image: nginx:1.25.5
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 80
|
- containerPort: 80
|
||||||
- name: curl
|
- name: curl
|
||||||
|
|
|
@ -15,7 +15,7 @@ var TestHelper *testutil.TestHelper
|
||||||
|
|
||||||
var (
|
var (
|
||||||
skipPortsNs = "skip-ports-test"
|
skipPortsNs = "skip-ports-test"
|
||||||
booksappDeployments = []string{"books", "traffic", "authors", "webapp"}
|
emojivotoDeployments = []string{"emoji", "vote-bot", "voting", "web"}
|
||||||
)
|
)
|
||||||
|
|
||||||
func secureRequestMatcher(dst string) *prommatch.Matcher {
|
func secureRequestMatcher(dst string) *prommatch.Matcher {
|
||||||
|
@ -65,8 +65,8 @@ func TestSkipInboundPorts(t *testing.T) {
|
||||||
"'kubectl apply' command failed\n%s", out)
|
"'kubectl apply' command failed\n%s", out)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check all booksapp deployments are up and running
|
// Check all emojivoto deployments are up and running
|
||||||
for _, deploy := range booksappDeployments {
|
for _, deploy := range emojivotoDeployments {
|
||||||
if err := TestHelper.CheckPods(ctx, ns, deploy, 1); err != nil {
|
if err := TestHelper.CheckPods(ctx, ns, deploy, 1); err != nil {
|
||||||
//nolint:errorlint
|
//nolint:errorlint
|
||||||
if rce, ok := err.(*testutil.RestartCountError); ok {
|
if rce, ok := err.(*testutil.RestartCountError); ok {
|
||||||
|
@ -81,7 +81,7 @@ func TestSkipInboundPorts(t *testing.T) {
|
||||||
// Wait for slow-cookers to start sending requests by using a short
|
// Wait for slow-cookers to start sending requests by using a short
|
||||||
// time window through RetryFor.
|
// time window through RetryFor.
|
||||||
err := testutil.RetryFor(30*time.Second, func() error {
|
err := testutil.RetryFor(30*time.Second, func() error {
|
||||||
pods, err := TestHelper.GetPods(ctx, ns, map[string]string{"app": "webapp"})
|
pods, err := TestHelper.GetPods(ctx, ns, map[string]string{"app": "web-svc"})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error getting pods\n%w", err)
|
return fmt.Errorf("error getting pods\n%w", err)
|
||||||
}
|
}
|
||||||
|
@ -94,10 +94,10 @@ func TestSkipInboundPorts(t *testing.T) {
|
||||||
return fmt.Errorf("error getting metrics for pod\n%w", err)
|
return fmt.Errorf("error getting metrics for pod\n%w", err)
|
||||||
}
|
}
|
||||||
s := prommatch.Suite{}.
|
s := prommatch.Suite{}.
|
||||||
MustContain("secure requests to authors", secureRequestMatcher("authors")).
|
MustContain("secure requests to emoji-svc", secureRequestMatcher("emoji-svc")).
|
||||||
MustContain("insecure requests to books", insecureRequestMatcher("books")).
|
MustContain("insecure requests to voting-svc", insecureRequestMatcher("voting-svc")).
|
||||||
MustNotContain("insecure requests to authors", insecureRequestMatcher("authors")).
|
MustNotContain("insecure requests to emoji-svc", insecureRequestMatcher("emoji-svc")).
|
||||||
MustNotContain("secure requests to books", secureRequestMatcher("books"))
|
MustNotContain("secure requests to voting-svc", secureRequestMatcher("voting-svc"))
|
||||||
if err := s.CheckString(metrics); err != nil {
|
if err := s.CheckString(metrics); err != nil {
|
||||||
return fmt.Errorf("error matching metrics\n%w", err)
|
return fmt.Errorf("error matching metrics\n%w", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,191 +1,207 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: emoji
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: voting
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: web
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: Service
|
kind: Service
|
||||||
metadata:
|
metadata:
|
||||||
name: webapp
|
name: emoji-svc
|
||||||
labels:
|
|
||||||
app: webapp
|
|
||||||
project: booksapp
|
|
||||||
spec:
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: grpc
|
||||||
|
port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
- name: prom
|
||||||
|
port: 8801
|
||||||
|
targetPort: 8801
|
||||||
selector:
|
selector:
|
||||||
app: webapp
|
app: emoji-svc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: voting-svc
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: grpc
|
||||||
|
port: 8080
|
||||||
|
targetPort: 8080
|
||||||
|
- name: prom
|
||||||
|
port: 8801
|
||||||
|
targetPort: 8801
|
||||||
|
selector:
|
||||||
|
app: voting-svc
|
||||||
|
---
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Service
|
||||||
|
metadata:
|
||||||
|
name: web-svc
|
||||||
|
spec:
|
||||||
|
ports:
|
||||||
|
- name: http
|
||||||
|
port: 80
|
||||||
|
targetPort: 8080
|
||||||
|
selector:
|
||||||
|
app: web-svc
|
||||||
type: ClusterIP
|
type: ClusterIP
|
||||||
ports:
|
|
||||||
- name: service
|
|
||||||
port: 7000
|
|
||||||
---
|
---
|
||||||
kind: Deployment
|
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: webapp
|
|
||||||
labels:
|
labels:
|
||||||
app: webapp
|
app.kubernetes.io/name: emoji
|
||||||
project: booksapp
|
app.kubernetes.io/part-of: emojivoto
|
||||||
app.kubernetes.io/part-of: booksapp
|
app.kubernetes.io/version: v11
|
||||||
|
name: emoji
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: webapp
|
app: emoji-svc
|
||||||
project: booksapp
|
version: v11
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: webapp
|
app: emoji-svc
|
||||||
project: booksapp
|
version: v11
|
||||||
spec:
|
spec:
|
||||||
dnsPolicy: ClusterFirst
|
|
||||||
containers:
|
containers:
|
||||||
- name: service
|
- env:
|
||||||
image: buoyantio/booksapp:v0.0.5
|
- name: GRPC_PORT
|
||||||
env:
|
value: "8080"
|
||||||
- name: DATABASE_URL
|
- name: PROM_PORT
|
||||||
value: sqlite3:db/db.sqlite3
|
value: "8801"
|
||||||
- name: AUTHORS_SITE
|
image: docker.l5d.io/buoyantio/emojivoto-emoji-svc:v11
|
||||||
value: http://authors:7001
|
name: emoji-svc
|
||||||
- name: BOOKS_SITE
|
|
||||||
value: http://books:7002
|
|
||||||
args: ["prod:webapp"]
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /ping
|
|
||||||
port: 7000
|
|
||||||
ports:
|
ports:
|
||||||
- name: service
|
- containerPort: 8080
|
||||||
containerPort: 7000
|
name: grpc
|
||||||
|
- containerPort: 8801
|
||||||
|
name: prom
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
serviceAccountName: emoji
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: authors
|
|
||||||
labels:
|
|
||||||
app: authors
|
|
||||||
project: booksapp
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: authors
|
|
||||||
ports:
|
|
||||||
- name: service
|
|
||||||
port: 7001
|
|
||||||
---
|
|
||||||
kind: Deployment
|
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: authors
|
|
||||||
labels:
|
labels:
|
||||||
app: authors
|
app.kubernetes.io/name: vote-bot
|
||||||
project: booksapp
|
app.kubernetes.io/part-of: emojivoto
|
||||||
app.kubernetes.io/part-of: booksapp
|
app.kubernetes.io/version: v11
|
||||||
|
name: vote-bot
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: authors
|
app: vote-bot
|
||||||
project: booksapp
|
version: v11
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: authors
|
app: vote-bot
|
||||||
project: booksapp
|
version: v11
|
||||||
spec:
|
spec:
|
||||||
dnsPolicy: ClusterFirst
|
|
||||||
containers:
|
containers:
|
||||||
- name: service
|
- command:
|
||||||
image: buoyantio/booksapp:v0.0.5
|
- emojivoto-vote-bot
|
||||||
env:
|
env:
|
||||||
- name: DATABASE_URL
|
- name: WEB_HOST
|
||||||
value: sqlite3:db/db.sqlite3
|
value: web-svc:80
|
||||||
- name: BOOKS_SITE
|
image: docker.l5d.io/buoyantio/emojivoto-web:v11
|
||||||
value: http://books:7002
|
name: vote-bot
|
||||||
- name: FAILURE_RATE
|
resources:
|
||||||
value: "0.0"
|
requests:
|
||||||
args: ["prod:authors"]
|
cpu: 10m
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /ping
|
|
||||||
port: 7001
|
|
||||||
ports:
|
|
||||||
- name: service
|
|
||||||
containerPort: 7001
|
|
||||||
---
|
---
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: books
|
|
||||||
labels:
|
|
||||||
app: books
|
|
||||||
project: booksapp
|
|
||||||
spec:
|
|
||||||
selector:
|
|
||||||
app: books
|
|
||||||
ports:
|
|
||||||
- name: service
|
|
||||||
port: 7002
|
|
||||||
---
|
|
||||||
kind: Deployment
|
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: books
|
|
||||||
labels:
|
labels:
|
||||||
app: books
|
app.kubernetes.io/name: voting
|
||||||
project: booksapp
|
app.kubernetes.io/part-of: emojivoto
|
||||||
app.kubernetes.io/part-of: booksapp
|
app.kubernetes.io/version: v11
|
||||||
|
name: voting
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: books
|
app: voting-svc
|
||||||
project: booksapp
|
version: v11
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
annotations:
|
annotations:
|
||||||
config.linkerd.io/skip-inbound-ports: "7002"
|
config.linkerd.io/skip-inbound-ports: "8080"
|
||||||
labels:
|
labels:
|
||||||
app: books
|
app: voting-svc
|
||||||
project: booksapp
|
version: v11
|
||||||
spec:
|
spec:
|
||||||
dnsPolicy: ClusterFirst
|
|
||||||
containers:
|
containers:
|
||||||
- name: service
|
- env:
|
||||||
image: buoyantio/booksapp:v0.0.5
|
- name: GRPC_PORT
|
||||||
env:
|
value: "8080"
|
||||||
- name: DATABASE_URL
|
- name: PROM_PORT
|
||||||
value: sqlite3:db/db.sqlite3
|
value: "8801"
|
||||||
- name: AUTHORS_SITE
|
image: docker.l5d.io/buoyantio/emojivoto-voting-svc:v11
|
||||||
value: http://authors:7001
|
name: voting-svc
|
||||||
args: ["prod:books"]
|
|
||||||
readinessProbe:
|
|
||||||
httpGet:
|
|
||||||
path: /ping
|
|
||||||
port: 7002
|
|
||||||
ports:
|
ports:
|
||||||
- name: service
|
- containerPort: 8080
|
||||||
containerPort: 7002
|
name: grpc
|
||||||
|
- containerPort: 8801
|
||||||
|
name: prom
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
serviceAccountName: voting
|
||||||
---
|
---
|
||||||
kind: Deployment
|
|
||||||
apiVersion: apps/v1
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
name: traffic
|
|
||||||
labels:
|
labels:
|
||||||
app: traffic
|
app.kubernetes.io/name: web
|
||||||
project: booksapp
|
app.kubernetes.io/part-of: emojivoto
|
||||||
app.kubernetes.io/part-of: booksapp
|
app.kubernetes.io/version: v11
|
||||||
|
name: web
|
||||||
spec:
|
spec:
|
||||||
replicas: 1
|
replicas: 1
|
||||||
selector:
|
selector:
|
||||||
matchLabels:
|
matchLabels:
|
||||||
app: traffic
|
app: web-svc
|
||||||
project: booksapp
|
version: v11
|
||||||
template:
|
template:
|
||||||
metadata:
|
metadata:
|
||||||
labels:
|
labels:
|
||||||
app: traffic
|
app: web-svc
|
||||||
project: booksapp
|
version: v11
|
||||||
spec:
|
spec:
|
||||||
dnsPolicy: ClusterFirst
|
|
||||||
containers:
|
containers:
|
||||||
- name: traffic
|
- env:
|
||||||
image: buoyantio/booksapp-traffic:v0.0.3
|
- name: WEB_PORT
|
||||||
args:
|
value: "8080"
|
||||||
- "-initial-delay=30s"
|
- name: EMOJISVC_HOST
|
||||||
- "webapp:7000"
|
value: emoji-svc:8080
|
||||||
|
- name: VOTINGSVC_HOST
|
||||||
|
value: voting-svc:8080
|
||||||
|
- name: INDEX_BUNDLE
|
||||||
|
value: dist/index_bundle.js
|
||||||
|
image: docker.l5d.io/buoyantio/emojivoto-web:v11
|
||||||
|
name: web-svc
|
||||||
|
ports:
|
||||||
|
- containerPort: 8080
|
||||||
|
name: http
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
serviceAccountName: web
|
||||||
|
|
|
@ -38,6 +38,7 @@ type TestHelper struct {
|
||||||
uninstall bool
|
uninstall bool
|
||||||
cni bool
|
cni bool
|
||||||
calico bool
|
calico bool
|
||||||
|
dualStack bool
|
||||||
nativeSidecar bool
|
nativeSidecar bool
|
||||||
defaultInboundPolicy string
|
defaultInboundPolicy string
|
||||||
httpClient http.Client
|
httpClient http.Client
|
||||||
|
@ -208,6 +209,7 @@ func NewTestHelper() *TestHelper {
|
||||||
uninstall := flag.Bool("uninstall", false, "whether to run the 'linkerd uninstall' integration test")
|
uninstall := flag.Bool("uninstall", false, "whether to run the 'linkerd uninstall' integration test")
|
||||||
cni := flag.Bool("cni", false, "whether to install linkerd with CNI enabled")
|
cni := flag.Bool("cni", false, "whether to install linkerd with CNI enabled")
|
||||||
calico := flag.Bool("calico", false, "whether to install calico CNI plugin")
|
calico := flag.Bool("calico", false, "whether to install calico CNI plugin")
|
||||||
|
dualStack := flag.Bool("dual-stack", false, "whether to run the dual-stack tests")
|
||||||
nativeSidecar := flag.Bool("native-sidecar", false, "whether to install using native sidecar injection")
|
nativeSidecar := flag.Bool("native-sidecar", false, "whether to install using native sidecar injection")
|
||||||
defaultInboundPolicy := flag.String("default-inbound-policy", "", "if non-empty, passed to --set proxy.defaultInboundPolicy at linkerd's install time")
|
defaultInboundPolicy := flag.String("default-inbound-policy", "", "if non-empty, passed to --set proxy.defaultInboundPolicy at linkerd's install time")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
@ -254,6 +256,7 @@ func NewTestHelper() *TestHelper {
|
||||||
externalPrometheus: *externalPrometheus,
|
externalPrometheus: *externalPrometheus,
|
||||||
cni: *cni,
|
cni: *cni,
|
||||||
calico: *calico,
|
calico: *calico,
|
||||||
|
dualStack: *dualStack,
|
||||||
nativeSidecar: *nativeSidecar,
|
nativeSidecar: *nativeSidecar,
|
||||||
uninstall: *uninstall,
|
uninstall: *uninstall,
|
||||||
defaultInboundPolicy: *defaultInboundPolicy,
|
defaultInboundPolicy: *defaultInboundPolicy,
|
||||||
|
@ -399,6 +402,11 @@ func (h *TestHelper) Calico() bool {
|
||||||
return h.calico
|
return h.calico
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DualStack determines whether the DualStack tests are run
|
||||||
|
func (h *TestHelper) DualStack() bool {
|
||||||
|
return h.dualStack
|
||||||
|
}
|
||||||
|
|
||||||
// NativeSidecar determines whether native sidecar injection is enabled
|
// NativeSidecar determines whether native sidecar injection is enabled
|
||||||
func (h *TestHelper) NativeSidecar() bool {
|
func (h *TestHelper) NativeSidecar() bool {
|
||||||
return h.nativeSidecar
|
return h.nativeSidecar
|
||||||
|
|
Loading…
Reference in New Issue