Skip to content

Commit

Permalink
connectivity: Introduce local redirect policy tests
Browse files Browse the repository at this point in the history
Tests LRP connectivity scenarios with a configured
skipRedirectFromBackend flag:
 - client pod to LRP frontend
 - LRP backend to LRP frontend

Signed-off-by: Aditi Ghag <[email protected]>
  • Loading branch information
aditighag committed Jun 7, 2024
1 parent ddd4eb6 commit 63c6d3a
Show file tree
Hide file tree
Showing 10 changed files with 549 additions and 13 deletions.
1 change: 1 addition & 0 deletions connectivity/builder/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ func concurrentTests(connTests []*check.ConnectivityTest) error {
podToK8sOnControlplane{},
podToControlplaneHostCidr{},
podToK8sOnControlplaneCidr{},
localRedirectPolicy{},
}
return injectTests(tests, connTests...)
}
Expand Down
53 changes: 53 additions & 0 deletions connectivity/builder/local_redirect_policy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright Authors of Cilium

package builder

import (
_ "embed"

"github.com/cilium/cilium-cli/connectivity/check"
"github.com/cilium/cilium-cli/connectivity/tests"
"github.com/cilium/cilium-cli/utils/features"
)

var (
//go:embed manifests/local-redirect-policy.yaml
localRedirectPolicyYAML string
)

type localRedirectPolicy struct{}

func (t localRedirectPolicy) build(ct *check.ConnectivityTest, _ map[string]string) {
lrpFrontendIP := "169.254.169.254"
lrpFrontendIPSkipRedirect := "169.254.169.255"
newTest("local-redirect-policy", ct).
WithCiliumLocalRedirectPolicy(check.CiliumLocalRedirectPolicyParams{
Policy: localRedirectPolicyYAML,
Name: "lrp-address-matcher",
FrontendIP: lrpFrontendIP,
SkipRedirectFromBackend: false,
}).
WithCiliumLocalRedirectPolicy(check.CiliumLocalRedirectPolicyParams{
Policy: localRedirectPolicyYAML,
Name: "lrp-address-matcher-skip-redirect-from-backend",
FrontendIP: lrpFrontendIPSkipRedirect,
SkipRedirectFromBackend: true,
}).
WithFeatureRequirements(features.RequireEnabled(features.LocalRedirectPolicy)).
WithFeatureRequirements(features.RequireEnabled(features.KPRSocketLB)).
WithScenarios(
tests.LRP(false),
tests.LRP(true),
).
WithExpectations(func(a *check.Action) (egress, ingress check.Result) {
if a.Scenario().Name() == "lrp-skip-redirect-from-backend" {
if a.Source().HasLabel("lrp", "backend") &&
a.Destination().Address(features.IPFamilyV4) == lrpFrontendIPSkipRedirect {
return check.ResultCurlTimeout, check.ResultNone
}
return check.ResultOK, check.ResultNone
}
return check.ResultOK, check.ResultNone
})
}
21 changes: 21 additions & 0 deletions connectivity/builder/manifests/local-redirect-policy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
apiVersion: cilium.io/v2
kind: CiliumLocalRedirectPolicy
metadata:
name: # set by WithCiliumLocalRedirectPolicy()
spec:
redirectFrontend:
addressMatcher:
ip: # set by WithCiliumLocalRedirectPolicy()
toPorts:
- port: "80"
name: "tcp"
protocol: TCP
redirectBackend:
localEndpointSelector:
matchLabels:
lrp: backend
toPorts:
- port: "8080"
name: "tcp-8080"
protocol: TCP
skipRedirectFromBackend: # set by WithCiliumLocalRedirectPolicy()
12 changes: 12 additions & 0 deletions connectivity/check/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ type ConnectivityTest struct {
ingressService map[string]Service
k8sService Service
externalWorkloads map[string]ExternalWorkload
lrpClientPods map[string]Pod
lrpBackendPods map[string]Pod

hostNetNSPodsByNode map[string]Pod
secondaryNetworkNodeIPv4 map[string]string // node name => secondary ip
Expand Down Expand Up @@ -204,6 +206,8 @@ func NewConnectivityTest(client *k8s.Client, p Parameters, version string, logge
echoExternalPods: make(map[string]Pod),
clientPods: make(map[string]Pod),
clientCPPods: make(map[string]Pod),
lrpClientPods: make(map[string]Pod),
lrpBackendPods: make(map[string]Pod),
perfClientPods: []Pod{},
perfServerPod: []Pod{},
PerfResults: []common.PerfSummary{},
Expand Down Expand Up @@ -1117,6 +1121,14 @@ func (ct *ConnectivityTest) EchoPods() map[string]Pod {
return ct.echoPods
}

func (ct *ConnectivityTest) LrpClientPods() map[string]Pod {
return ct.lrpClientPods
}

func (ct *ConnectivityTest) LrpBackendPods() map[string]Pod {
return ct.lrpBackendPods
}

// EchoServices returns all the non headless services
func (ct *ConnectivityTest) EchoServices() map[string]Service {
svcs := map[string]Service{}
Expand Down
87 changes: 87 additions & 0 deletions connectivity/check/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ const (
kindEchoExternalNodeName = "echo-external-node"
kindClientName = "client"
kindPerfName = "perf"
lrpBackendDeploymentName = "lrp-backend"
lrpClientDeploymentName = "lrp-client"
kindLrpName = "lrp"

hostNetNSDeploymentName = "host-netns"
hostNetNSDeploymentNameNonCilium = "host-netns-non-cilium" // runs on non-Cilium test nodes
Expand Down Expand Up @@ -934,6 +937,63 @@ func (ct *ConnectivityTest) deploy(ctx context.Context) error {
}
}
}

if ct.Features[features.LocalRedirectPolicy].Enabled {
ct.Logf("✨ [%s] Deploying lrp-client deployment...", ct.clients.src.ClusterName())
lrpClientDeployment := newDeployment(deploymentParameters{
Name: lrpClientDeploymentName,
Kind: kindLrpName,
Image: ct.params.CurlImage,
Command: []string{"/usr/bin/pause"},
Labels: map[string]string{"lrp": "client"},
Annotations: ct.params.DeploymentAnnotations.Match(lrpClientDeploymentName),
NodeSelector: ct.params.NodeSelector,
})
_, err = ct.clients.src.CreateServiceAccount(ctx, ct.params.TestNamespace, k8s.NewServiceAccount(lrpClientDeploymentName), metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("unable to create service account %s: %s", lrpClientDeployment, err)
}
_, err = ct.clients.src.CreateDeployment(ctx, ct.params.TestNamespace, lrpClientDeployment, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("unable to create deployment %s: %s", lrpClientDeployment, err)
}
ct.Logf("✨ [%s] Deploying lrp-backend deployment...", ct.clients.src.ClusterName())
containerPort := 8080
lrpBackendDeployment := newDeployment(deploymentParameters{
Name: lrpBackendDeploymentName,
Kind: kindLrpName,
Image: ct.params.JSONMockImage,
NamedPort: "tcp-8080",
Port: containerPort,
ReadinessProbe: newLocalReadinessProbe(containerPort, "/"),
Labels: map[string]string{"lrp": "backend"},
Annotations: ct.params.DeploymentAnnotations.Match(lrpBackendDeploymentName),
Affinity: &corev1.Affinity{
PodAffinity: &corev1.PodAffinity{
RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{
{
LabelSelector: &metav1.LabelSelector{
MatchExpressions: []metav1.LabelSelectorRequirement{
{Key: "name", Operator: metav1.LabelSelectorOpIn, Values: []string{lrpClientDeploymentName}},
},
},
TopologyKey: corev1.LabelHostname,
},
},
},
},
NodeSelector: ct.params.NodeSelector,
})
_, err = ct.clients.src.CreateServiceAccount(ctx, ct.params.TestNamespace, k8s.NewServiceAccount(lrpBackendDeploymentName), metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("unable to create service account %s: %s", lrpBackendDeployment, err)
}
_, err = ct.clients.src.CreateDeployment(ctx, ct.params.TestNamespace, lrpBackendDeployment, metav1.CreateOptions{})
if err != nil {
return fmt.Errorf("unable to create deployment %s: %s", lrpBackendDeployment, err)
}
}

return nil
}

Expand Down Expand Up @@ -1087,6 +1147,11 @@ func (ct *ConnectivityTest) deploymentList() (srcList []string, dstList []string
srcList = append(srcList, echoExternalNodeDeploymentName)
}

if ct.Features[features.LocalRedirectPolicy].Enabled {
srcList = append(srcList, lrpClientDeploymentName)
srcList = append(srcList, lrpBackendDeploymentName)
}

return srcList, dstList
}

Expand Down Expand Up @@ -1182,6 +1247,28 @@ func (ct *ConnectivityTest) validateDeployment(ctx context.Context) error {
return nil
}

if ct.Features[features.LocalRedirectPolicy].Enabled {
lrpPods, err := ct.client.ListPods(ctx, ct.params.TestNamespace, metav1.ListOptions{LabelSelector: "kind=" + kindLrpName})
if err != nil {
return fmt.Errorf("unable to list lrp pods: %w", err)
}
for _, lrpPod := range lrpPods.Items {
if v, hasLabel := lrpPod.GetLabels()["lrp"]; hasLabel {
if v == "backend" {
ct.lrpBackendPods[lrpPod.Name] = Pod{
K8sClient: ct.client,
Pod: lrpPod.DeepCopy(),
}
} else if v == "client" {
ct.lrpClientPods[lrpPod.Name] = Pod{
K8sClient: ct.client,
Pod: lrpPod.DeepCopy(),
}
}
}
}
}

clientPods, err := ct.client.ListPods(ctx, ct.params.TestNamespace, metav1.ListOptions{LabelSelector: "kind=" + kindClientName})
if err != nil {
return fmt.Errorf("unable to list client pods: %s", err)
Expand Down
58 changes: 57 additions & 1 deletion connectivity/check/peer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import (
"net/url"
"strconv"

corev1 "k8s.io/api/core/v1"

"github.com/cilium/cilium/api/v1/flow"
ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2"
corev1 "k8s.io/api/core/v1"

"github.com/cilium/cilium-cli/k8s"
"github.com/cilium/cilium-cli/utils/features"
Expand Down Expand Up @@ -497,6 +498,61 @@ func (he httpEndpoint) FlowFilters() []*flow.FlowFilter {
return nil
}

type LRPFrontend struct {
name string
ip string
port string
}

func NewLRPFrontend(frontend ciliumv2.RedirectFrontend) *LRPFrontend {
var lf LRPFrontend
if f := frontend.AddressMatcher; f != nil {
lf.ip = f.IP
lf.port = f.ToPorts[0].Port
lf.name = fmt.Sprintf("%s:%s", lf.ip, lf.port)

return &lf
}

return nil
}

func (l LRPFrontend) Name() string {
return l.name
}

func (l LRPFrontend) Scheme() string {
return "http"
}

func (l LRPFrontend) Path() string {
return ""
}

func (l LRPFrontend) Address(features.IPFamily) string {
return l.ip
}

func (l LRPFrontend) Port() uint32 {
p, err := strconv.Atoi(l.port)
if err != nil {
return 0
}
return uint32(p)
}

func (l LRPFrontend) HasLabel(string, string) bool {
return false
}

func (l LRPFrontend) Labels() map[string]string {
return nil
}

func (l LRPFrontend) FlowFilters() []*flow.FlowFilter {
return nil
}

// EchoIPPod is a Kubernetes Pod that prints back the client IP, acting as a peer in a connectivity test.
type EchoIPPod struct {
Pod
Expand Down
Loading

0 comments on commit 63c6d3a

Please sign in to comment.