distros-test-framework/pkg/assert/pod.go

126 lines
3.8 KiB
Go

package assert
import (
"fmt"
"net"
"strings"
"github.com/rancher/distros-test-framework/shared"
. "github.com/onsi/gomega"
"github.com/onsi/gomega/types"
)
type PodAssertFunc func(g Gomega, pod shared.Pod)
const (
statusCompleted = "Completed"
statusRunning = "Running"
)
// PodAssertRestart custom assertion func that asserts that pods are not restarting with no reason.
//
// controller, scheduler, helm-install pods can be restarted occasionally when cluster started if only once.
func PodAssertRestart() PodAssertFunc {
return func(g Gomega, pod shared.Pod) {
if strings.Contains(pod.NameSpace, "kube-system") &&
strings.Contains(pod.Name, "controller") &&
strings.Contains(pod.Name, "scheduler") {
g.Expect(pod.Restarts).Should(SatisfyAny(Equal("0"),
Equal("1")),
"could be restarted occasionally when cluster started", pod.Name)
}
}
}
// PodAssertReady custom assertion func that asserts that the pod is with correct numbers of ready containers.
func PodAssertReady() PodAssertFunc {
return func(g Gomega, pod shared.Pod) {
g.ExpectWithOffset(1, pod.Ready).To(checkReadyFields(),
"should have equal values in n/n format")
}
}
// checkReadyFields is a custom matcher that checks if the input string is in N/N format and the same quantity.
func checkReadyFields() types.GomegaMatcher {
return WithTransform(func(s string) (bool, error) {
var a, b int
n, err := fmt.Sscanf(s, "%d/%d", &a, &b)
if err != nil || n != 2 {
return false, fmt.Errorf("failed to parse format: %v", err)
}
return a == b, nil
}, BeTrue())
}
// ValidatePodIPByLabel validates expected pod IP by label.
func ValidatePodIPByLabel(cluster *shared.Cluster, labels, expected []string) {
Eventually(func() error {
for i, label := range labels {
if len(labels) > 0 {
res, _ := shared.KubectlCommand(
cluster,
"host",
"get",
"pods -l "+label,
`-o=jsonpath='{range .items[*]}{.status.podIPs[*].ip}{" "}{end}'`)
ips := strings.Split(res, " ")
if strings.Contains(ips[0], expected[i]) {
return nil
}
}
}
return nil
}, "180s", "30s").Should(Succeed(),
"failed to validate expected: %s on %s", expected, labels)
}
// ValidatePodIPsByLabel validates expected pod IPs by label.
func ValidatePodIPsByLabel(label string, expected []string) {
cmd := "kubectl get pods -l " + label +
` -o jsonpath='{range .items[*]}{.status.podIPs[*].ip}{" "}{end}'` +
" --kubeconfig=" + shared.KubeConfigFile
Eventually(func() error {
res, _ := shared.RunCommandHost(cmd)
ips := strings.Split(res, " ")
Expect(len(ips)).ShouldNot(BeZero())
Expect(len(expected)).ShouldNot(BeZero())
for i, ip := range ips {
_, subnet, _ := net.ParseCIDR(expected[i])
if subnet.Contains(net.ParseIP(ip)) {
return nil
}
}
return nil
}, "180s", "5s").Should(Succeed(),
"failed to validate podIPs in expected range %s for label %s",
expected, label)
}
// PodStatusRunning checks status of pods is Running when searched by namespace and label.
func PodStatusRunning(namespace, label string) {
cmd := "kubectl get pods -n " + namespace + " -l " + label +
" --field-selector=status.phase=Running --kubeconfig=" + shared.KubeConfigFile
Eventually(func(g Gomega) {
err := ValidateOnHost(cmd, statusRunning)
g.Expect(err).NotTo(HaveOccurred(), err)
}, "30s", "5s").Should(Succeed())
}
// ValidateIntraNSPodConnectivity ensures that one pod, the "server", can be reached from another, the "client"
// within the same namespace.
func ValidateIntraNSPodConnectivity(namespace, clientPodName, serverPodIP, expectedResult string) {
execCommand := fmt.Sprintf(
"kubectl exec -n %s pod/%s --kubeconfig=%s -- wget -O - http://%s",
namespace, clientPodName, shared.KubeConfigFile, serverPodIP)
err := ValidateOnHost(
execCommand,
expectedResult,
)
Expect(err).NotTo(HaveOccurred(), err)
}