Skip to content

Commit b2b8fe2

Browse files
committed
Add ipsec pod restart test
This commit adds required tests to ensure pod traffic across nodes are not impacted upon multiple reboot of ipsec dameonset pods. Signed-off-by: Periyasamy Palanisamy <[email protected]>
1 parent 2bb2450 commit b2b8fe2

File tree

6 files changed

+148
-14
lines changed

6 files changed

+148
-14
lines changed

pkg/testsuites/standard_suites.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ var staticSuites = []ginkgo.TestSuite{
260260
return strings.Contains(name, "[Suite:openshift/network/ipsec")
261261
},
262262
Parallelism: 1,
263-
TestTimeout: 60 * time.Minute,
263+
TestTimeout: 20 * time.Minute,
264264
},
265265
{
266266
Name: "openshift/network/stress",

test/extended/networking/egressip_helpers.go

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,15 +1391,6 @@ func (p *PortAllocator) allocatePort(port int) error {
13911391
return nil
13921392
}
13931393

1394-
// deleteDaemonSet deletes the Daemonset <namespace>/<dsName>.
1395-
func deleteDaemonSet(clientset kubernetes.Interface, namespace, dsName string) error {
1396-
deleteOptions := metav1.DeleteOptions{}
1397-
if err := clientset.AppsV1().DaemonSets(namespace).Delete(context.TODO(), dsName, deleteOptions); err != nil {
1398-
return fmt.Errorf("Failed to delete DaemonSet %s/%s: %v", namespace, dsName, err)
1399-
}
1400-
return nil
1401-
}
1402-
14031394
// createHostNetworkedDaemonSetAndProbe creates a host networked pod in namespace <namespace> on
14041395
// node <nodeName>. It will allocate a port to listen on and it will return
14051396
// the DaemonSet or an error.

test/extended/networking/internal_ports.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,14 @@ var _ = ginkgo.Describe("[sig-network] Internal connectivity", func() {
166166
}
167167
}
168168
}
169-
errs := parallelTest(6, testFns)
169+
errs := ParallelTest(6, testFns)
170170
o.Expect(errs).To(o.Equal([]error(nil)))
171171
})
172172
})
173173

174-
// parallelTest runs the provided fns in parallel with at most workers and returns an array of all
174+
// ParallelTest runs the provided fns in parallel with at most workers and returns an array of all
175175
// non nil errors.
176-
func parallelTest(workers int, fns []func() error) []error {
176+
func ParallelTest(workers int, fns []func() error) []error {
177177
var wg sync.WaitGroup
178178
work := make(chan func() error, workers)
179179
results := make(chan error, workers)

test/extended/networking/ipsec.go

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,17 @@ import (
1313
mg "github.com/openshift/origin/test/extended/machine_config"
1414
exutil "github.com/openshift/origin/test/extended/util"
1515
"golang.org/x/sync/errgroup"
16+
appsv1 "k8s.io/api/apps/v1"
1617
corev1 "k8s.io/api/core/v1"
1718
apierrors "k8s.io/apimachinery/pkg/api/errors"
1819
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
20+
"k8s.io/apimachinery/pkg/labels"
1921
"k8s.io/apimachinery/pkg/runtime/schema"
2022
"k8s.io/apimachinery/pkg/util/wait"
2123
"k8s.io/kubernetes/test/e2e/framework"
2224
e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
2325
e2eoutput "k8s.io/kubernetes/test/e2e/framework/pod/output"
26+
e2eskipper "k8s.io/kubernetes/test/e2e/framework/skipper"
2427
"k8s.io/kubernetes/test/e2e/framework/statefulset"
2528
admissionapi "k8s.io/pod-security-admission/api"
2629

@@ -93,6 +96,8 @@ var (
9396
rightServerCertName = "right_server"
9497
// Expiration date for certificates.
9598
certExpirationDate = time.Date(2034, time.April, 10, 0, 0, 0, 0, time.UTC)
99+
// http endpoint port for the pod traffic test
100+
port uint16 = 8080
96101
)
97102

98103
type trafficType string
@@ -652,6 +657,130 @@ var _ = g.Describe("[sig-network][Feature:IPsec]", g.Ordered, func() {
652657
})
653658
})
654659

660+
var _ = g.Describe("[sig-network][Feature:IPsec] IPsec resilience", g.Ordered, func() {
661+
oc := exutil.NewCLIWithPodSecurityLevel("ipsec", admissionapi.LevelPrivileged)
662+
f := oc.KubeFramework()
663+
var ipsecMode v1.IPsecMode
664+
665+
InOVNKubernetesContext(func() {
666+
g.BeforeAll(func() {
667+
var err error
668+
ipsecConfig, err := getIPsecConfig(oc)
669+
o.Expect(err).NotTo(o.HaveOccurred())
670+
ipsecMode = ipsecConfig.mode
671+
})
672+
673+
g.It("check pod traffic is working across nodes [apigroup:config.openshift.io] [Suite:openshift/network/ipsec]", func() {
674+
g.By("creating test pods")
675+
pods := createWebServerPods(oc, f.Namespace.Name)
676+
g.By("checking crossing connectivity over the pods")
677+
checkPodCrossConnectivity(pods)
678+
})
679+
680+
g.It("check pod traffic is working across nodes after ipsec daemonset restart [apigroup:config.openshift.io] [Suite:openshift/network/ipsec]", func() {
681+
// The IPsec daemonset manages IPsec connections between nodes for pod's east-west traffic.
682+
// The IPsec daemonset exists only in IPsec full mode, so skip this test for other IPsec modes.
683+
if ipsecMode != v1.IPsecModeFull {
684+
e2eskipper.Skipf("cluster is configured with IPsec %s mode, so skipping the test", ipsecMode)
685+
}
686+
g.By("creating test pods")
687+
pods := createWebServerPods(oc, f.Namespace.Name)
688+
g.By("checking crossing connectivity over the pods")
689+
checkPodCrossConnectivity(pods)
690+
// Restart IPsec daemonset few times and check pod traffic is not impacted.
691+
for i := 1; i <= 5; i++ {
692+
g.By(fmt.Sprintf("attempt#%d restarting IPsec pods", i))
693+
restartIPsecDaemonSet(oc)
694+
g.By("checking crossing connectivity over the pods")
695+
checkPodCrossConnectivity(pods)
696+
}
697+
})
698+
})
699+
})
700+
701+
func restartIPsecDaemonSet(oc *exutil.CLI) {
702+
g.GinkgoHelper()
703+
ds, err := getDaemonSet(oc, ovnNamespace, ovnIPsecDsName)
704+
o.Expect(err).NotTo(o.HaveOccurred())
705+
o.Expect(ds).NotTo(o.BeNil())
706+
err = deleteDaemonSet(oc.AdminKubeClient(), ovnNamespace, ovnIPsecDsName)
707+
o.Expect(err).NotTo(o.HaveOccurred())
708+
// wait until CNO reconciles IPsec daemonset.
709+
err = ensureIPsecFullEnabled(oc)
710+
o.Expect(err).NotTo(o.HaveOccurred())
711+
}
712+
713+
func createWebServerPods(oc *exutil.CLI, namespace string) []corev1.Pod {
714+
g.GinkgoHelper()
715+
immediate := int64(0)
716+
ds := &appsv1.DaemonSet{
717+
ObjectMeta: metav1.ObjectMeta{
718+
Name: "ipsec-webserver",
719+
Namespace: namespace,
720+
},
721+
Spec: appsv1.DaemonSetSpec{
722+
Selector: &metav1.LabelSelector{
723+
MatchLabels: map[string]string{
724+
"apps": "ipsec-webserver",
725+
},
726+
},
727+
Template: corev1.PodTemplateSpec{
728+
ObjectMeta: metav1.ObjectMeta{
729+
Labels: map[string]string{
730+
"apps": "ipsec-webserver",
731+
},
732+
},
733+
Spec: corev1.PodSpec{
734+
Tolerations: []corev1.Toleration{
735+
{
736+
Key: "node-role.kubernetes.io/master",
737+
Operator: corev1.TolerationOpExists,
738+
Effect: corev1.TaintEffectNoSchedule,
739+
},
740+
},
741+
TerminationGracePeriodSeconds: &immediate,
742+
Containers: []corev1.Container{e2epod.NewAgnhostContainer("agnhost-container", nil, nil, httpServerContainerCmd(port)...)},
743+
},
744+
},
745+
},
746+
}
747+
ds, err := oc.AdminKubeClient().AppsV1().DaemonSets(namespace).Create(context.Background(), ds, metav1.CreateOptions{})
748+
o.Expect(err).NotTo(o.HaveOccurred())
749+
err = wait.PollUntilContextTimeout(context.Background(), 1*time.Second,
750+
180*time.Second, true, func(ctx context.Context) (bool, error) {
751+
return isDaemonSetRunning(oc, namespace, ds.Name)
752+
})
753+
o.Expect(err).NotTo(o.HaveOccurred())
754+
755+
pods, err := oc.AdminKubeClient().CoreV1().Pods(namespace).List(context.Background(),
756+
metav1.ListOptions{LabelSelector: labels.Set(ds.Spec.Selector.MatchLabels).String()})
757+
o.Expect(err).NotTo(o.HaveOccurred())
758+
ds, err = getDaemonSet(oc, namespace, ds.Name)
759+
o.Expect(err).NotTo(o.HaveOccurred())
760+
o.Expect(len(pods.Items)).To(o.Equal(int(ds.Status.NumberAvailable)), fmt.Sprintf("%#v", pods.Items))
761+
return pods.Items
762+
}
763+
764+
func checkPodCrossConnectivity(pods []corev1.Pod) {
765+
g.GinkgoHelper()
766+
var testFns []func() error
767+
for _, sourcePod := range pods {
768+
for _, targetPod := range pods {
769+
if sourcePod.Name == targetPod.Name {
770+
// Skip if source and target pod are same, pod connectivity check is not required
771+
// for this case.
772+
continue
773+
}
774+
testFns = append(testFns, func() error {
775+
framework.Logf("Checking pod connectivity from node %s to node %s", sourcePod.Spec.NodeName, targetPod.Spec.NodeName)
776+
return connectToServer(podConfiguration{namespace: sourcePod.Namespace, name: sourcePod.Name}, targetPod.Status.PodIP, int(port))
777+
})
778+
}
779+
}
780+
errs := ParallelTest(6, testFns)
781+
o.Expect(errs).To(o.Equal([]error(nil)))
782+
}
783+
655784
func waitForIPsecConfigToComplete(oc *exutil.CLI, ipsecMode v1.IPsecMode) {
656785
g.GinkgoHelper()
657786
switch ipsecMode {

test/extended/networking/util.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import (
3434
"k8s.io/apimachinery/pkg/util/wait"
3535
"k8s.io/apiserver/pkg/storage/names"
3636
"k8s.io/client-go/dynamic"
37+
"k8s.io/client-go/kubernetes"
3738
k8sclient "k8s.io/client-go/kubernetes"
3839
"k8s.io/client-go/rest"
3940
"k8s.io/client-go/util/retry"
@@ -758,7 +759,7 @@ func isDaemonSetRunning(oc *exutil.CLI, namespace, name string) (bool, error) {
758759
}
759760
// Be sure that it has ds pod running in each node.
760761
desired, scheduled, ready := ds.Status.DesiredNumberScheduled, ds.Status.CurrentNumberScheduled, ds.Status.NumberReady
761-
return desired == scheduled && desired == ready, nil
762+
return ds.Status.ObservedGeneration == ds.Generation && desired == scheduled && desired == ready, nil
762763
}
763764

764765
func getDaemonSet(oc *exutil.CLI, namespace, name string) (*appsv1.DaemonSet, error) {
@@ -769,6 +770,15 @@ func getDaemonSet(oc *exutil.CLI, namespace, name string) (*appsv1.DaemonSet, er
769770
return ds, err
770771
}
771772

773+
// deleteDaemonSet deletes the Daemonset <namespace>/<dsName>.
774+
func deleteDaemonSet(clientset kubernetes.Interface, namespace, dsName string) error {
775+
deleteOptions := metav1.DeleteOptions{}
776+
if err := clientset.AppsV1().DaemonSets(namespace).Delete(context.TODO(), dsName, deleteOptions); err != nil {
777+
return fmt.Errorf("failed to delete DaemonSet %s/%s: %v", namespace, dsName, err)
778+
}
779+
return nil
780+
}
781+
772782
func createIPsecCertsMachineConfig(oc *exutil.CLI) (*mcfgv1.MachineConfig, error) {
773783
nsCertMachineConfig, err := oc.MachineConfigurationClient().MachineconfigurationV1().MachineConfigs().Get(context.Background(),
774784
nsCertMachineConfigName, metav1.GetOptions{})

test/extended/util/annotate/generated/zz_generated.annotations.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)