Skip to content

Commit 0445c30

Browse files
committed
integration tests for CLI
1 parent cb62cac commit 0445c30

File tree

455 files changed

+221645
-5176
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

455 files changed

+221645
-5176
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
build
1+
build/*
22
output
33
tmp
44
cover.out

.golangci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ linters:
1616
- stylecheck
1717
- typecheck
1818
- unused
19+
- ginkgolinter
1920
run:
2021
go: "1.22"
2122
linters-settings:

e2e/integration-tests/cli.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package integrationtests
2+
3+
import (
4+
"context"
5+
"io/fs"
6+
"os"
7+
"time"
8+
9+
"k8s.io/apimachinery/pkg/api/errors"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
"k8s.io/apimachinery/pkg/util/wait"
12+
"k8s.io/client-go/kubernetes"
13+
)
14+
15+
func isCLIReady(clientset *kubernetes.Clientset, cliNS string) (bool, error) {
16+
var collectorReady, cliDaemonsetReady bool
17+
err := wait.PollUntilContextTimeout(context.Background(), 30*time.Second, 300*time.Second, false, func(context.Context) (done bool, err error) {
18+
if !collectorReady {
19+
collectorPod, err := getNamespacePods(clientset, cliNS, &metav1.ListOptions{FieldSelector: "status.phase=Running", LabelSelector: "run=collector"})
20+
if err != nil {
21+
return false, err
22+
}
23+
24+
if len(collectorPod) > 0 {
25+
collectorReady = true
26+
}
27+
}
28+
29+
daemonset := "netobserv-cli"
30+
cliDaemonset, err := getDaemonSet(clientset, daemonset, cliNS)
31+
32+
if err != nil {
33+
if errors.IsNotFound(err) {
34+
return false, nil
35+
}
36+
return false, err
37+
}
38+
if cliDaemonset.Status.DesiredNumberScheduled == cliDaemonset.Status.NumberReady {
39+
cliDaemonsetReady = true
40+
}
41+
return collectorReady && cliDaemonsetReady, nil
42+
})
43+
if err != nil {
44+
return false, err
45+
}
46+
return true, nil
47+
}
48+
49+
// get latest flows.json file
50+
func getFlowsJSONFile() (string, error) {
51+
// var files []fs.DirEntry
52+
var files []string
53+
outputDir := "./output/flow/"
54+
dirFS := os.DirFS(outputDir)
55+
files, err := fs.Glob(dirFS, "*.json")
56+
if err != nil {
57+
return "", err
58+
}
59+
// this could be problematic if two tests are running in parallel with --copy=true
60+
var mostRecentFile fs.FileInfo
61+
for _, file := range files {
62+
fileInfo, err := os.Stat(outputDir + file)
63+
if err != nil {
64+
return "", nil
65+
}
66+
if mostRecentFile == nil || fileInfo.ModTime().After(mostRecentFile.ModTime()) {
67+
mostRecentFile = fileInfo
68+
}
69+
}
70+
return outputDir + mostRecentFile.Name(), nil
71+
}

e2e/integration-tests/cluster.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package integrationtests
2+
3+
import (
4+
"context"
5+
"os"
6+
7+
appsv1 "k8s.io/api/apps/v1"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
"k8s.io/client-go/kubernetes"
10+
"k8s.io/client-go/tools/clientcmd"
11+
)
12+
13+
func getNewClient() (*kubernetes.Clientset, error) {
14+
config, err := clientcmd.BuildConfigFromFlags("", os.Getenv("KUBECONFIG"))
15+
if err != nil {
16+
return nil, err
17+
}
18+
clientset, err := kubernetes.NewForConfig(config)
19+
20+
if err != nil {
21+
return nil, err
22+
}
23+
24+
return clientset, nil
25+
}
26+
27+
func getClusterNodes(clientset *kubernetes.Clientset, options *metav1.ListOptions) ([]string, error) {
28+
nodes, err := clientset.CoreV1().Nodes().List(context.TODO(), *options)
29+
var allNodes []string
30+
if err != nil {
31+
return allNodes, err
32+
}
33+
for i := range nodes.Items {
34+
allNodes = append(allNodes, nodes.Items[i].Name)
35+
}
36+
return allNodes, nil
37+
}
38+
39+
func getDaemonSet(clientset *kubernetes.Clientset, daemonset string, ns string) (*appsv1.DaemonSet, error) {
40+
ds, err := clientset.AppsV1().DaemonSets(ns).Get(context.TODO(), daemonset, metav1.GetOptions{})
41+
42+
if err != nil {
43+
return nil, err
44+
}
45+
46+
return ds, nil
47+
}
48+
49+
func getNamespacePods(clientset *kubernetes.Clientset, namespace string, options *metav1.ListOptions) ([]string, error) {
50+
pods, err := clientset.CoreV1().Pods(namespace).List(context.TODO(), *options)
51+
var allPods []string
52+
if err != nil {
53+
return allPods, err
54+
}
55+
for i := range pods.Items {
56+
allPods = append(allPods, pods.Items[i].Name)
57+
}
58+
return allPods, nil
59+
}

e2e/integration-tests/flows.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package integrationtests
2+
3+
type Flowlog struct {
4+
// Source
5+
SrcPort int
6+
SrcK8sType string `json:"SrcK8S_Type"`
7+
SrcK8sName string `json:"SrcK8S_Name"`
8+
SrcK8sHostName string `json:"SrcK8S_HostName"`
9+
SrcK8sOwnerType string `json:"SrcK8S_OwnerType"`
10+
SrcAddr string
11+
SrcMac string
12+
SrcK8sNamespace string `json:"SrcK8S_Namespace"`
13+
// Destination
14+
DstPort int
15+
DstK8sType string `json:"DstK8S_Type"`
16+
DstK8sName string `json:"DstK8S_Name"`
17+
DstK8sHostName string `json:"DstK8S_HostName"`
18+
DstK8sOwnerType string `json:"DstK8S_OwnerType"`
19+
DstAddr string
20+
DstMac string
21+
DstK8sHostIP string `json:"DstK8S_HostIP,omitempty"`
22+
DstK8sNamespace string `json:"DstK8S_Namespace"`
23+
// Protocol
24+
Proto int
25+
IcmpCode int
26+
IcmpType int
27+
Dscp int
28+
// TODO: check if Flags supposed to be []string?
29+
Flags int
30+
// Time
31+
TimeReceived int
32+
TimeFlowEndMs int
33+
TimeFlowStartMs int
34+
// Interface
35+
IfDirection int
36+
IfDirections []int
37+
Interfaces []string
38+
Etype int
39+
// Others
40+
Packets int
41+
Bytes int
42+
Duplicate bool
43+
AgentIP string
44+
Sampling int
45+
HashID string `json:"_HashId,omitempty"`
46+
IsFirst bool `json:"_IsFirst,omitempty"`
47+
RecordType string `json:"_RecordType,omitempty"`
48+
NumFlowLogs int `json:"numFlowLogs,omitempty"`
49+
K8SClusterName string `json:"K8S_ClusterName,omitempty"`
50+
// Zone
51+
SrcK8SZone string `json:"SrcK8S_Zone,omitempty"`
52+
DstK8SZone string `json:"DstK8S_Zone,omitempty"`
53+
// DNS
54+
DNSLatencyMs int `json:"DnsLatencyMs,omitempty"`
55+
DNSFlagsResponseCode string `json:"DnsFlagsResponseCode,omitempty"`
56+
// Packet Drop
57+
PktDropBytes int `json:"PktDropBytes,omitempty"`
58+
PktDropPackets int `json:"PktDropPackets,omitempty"`
59+
PktDropLatestState string `json:"PktDropLatestState,omitempty"`
60+
PktDropLatestDropCause string `json:"PktDropLatestDropCause,omitempty"`
61+
// RTT
62+
TimeFlowRttNs int `json:"TimeFlowRttNs,omitempty"`
63+
// Packet Translation
64+
XlatDstAddr string `json:"XlatDstAddr,omitempty"`
65+
XlatDstK8sName string `json:"XlatDstK8S_Name,omitempty"`
66+
XlatDstK8sNamespace string `json:"XlatDstK8S_Namespace,omitempty"`
67+
XlatDstK8sType string `json:"XlatDstK8S_Type,omitempty"`
68+
XlatDstPort int `json:"XlatDstPort,omitempty"`
69+
XlatSrcAddr string `json:"XlatSrcAddr,omitempty"`
70+
XlatSrcK8sName string `json:"XlatSrcK8S_Name,omitempty"`
71+
XlatSrcK8sNamespace string `json:"XlatSrcK8S_Namespace,omitempty"`
72+
ZoneID int `json:"ZoneId,omitempty"`
73+
// Network Events
74+
NetworkEvents []NetworkEvent `json:"NetworkEvents,omitempty"`
75+
// Secondary Network
76+
SrcK8sNetworkName string `json:"SrcK8S_NetworkName,omitempty"`
77+
DstK8sNetworkName string `json:"DstK8S_NetworkName,omitempty"`
78+
// UDN
79+
Udns []string `json:"Udns,omitempty"`
80+
}
81+
type NetworkEvent struct {
82+
Action string `json:"Action,omitempty"`
83+
Type string `json:"Type,omitempty"`
84+
Name string `json:"Name,omitempty"`
85+
Namespace string `json:"Namespace,omitempty"`
86+
Direction string `json:"Direction,omitempty"`
87+
Feature string `json:"Feature,omitempty"`
88+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package integrationtests
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"os"
7+
"os/exec"
8+
9+
g "github.com/onsi/ginkgo/v2"
10+
o "github.com/onsi/gomega"
11+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
12+
)
13+
14+
var _ = g.Describe("NetObserv CLI e2e integration test suite", g.Serial, func() {
15+
cliNS := "netobserv-cli"
16+
17+
g.It("Verify all CLI pods are deployed", g.Label("Sanity"), func() {
18+
cliArgs := []string{"flows", "--copy=false"}
19+
cmd := exec.Command(ocNetObservBinPath, cliArgs...)
20+
err := cmd.Start()
21+
o.Expect(err).NotTo(o.HaveOccurred())
22+
// cleanup()
23+
defer func() {
24+
cliArgs := []string{"cleanup"}
25+
cmd := exec.Command(ocNetObservBinPath, cliArgs...)
26+
err := cmd.Run()
27+
o.Expect(err).NotTo(o.HaveOccurred())
28+
}()
29+
var allPods []string
30+
clientset, err := getNewClient()
31+
o.Expect(err).NotTo(o.HaveOccurred())
32+
nodes, err := getClusterNodes(clientset, &metav1.ListOptions{})
33+
// agent pods + collector pods
34+
totalExpectedPods := len(nodes) + 1
35+
o.Expect(err).NotTo(o.HaveOccurred())
36+
_, err = isCLIReady(clientset, cliNS)
37+
o.Expect(err).NotTo(o.HaveOccurred(), "CLI didn't come ready")
38+
allPods, err = getNamespacePods(clientset, cliNS, &metav1.ListOptions{})
39+
o.Expect(err).NotTo(o.HaveOccurred())
40+
o.Expect(allPods).To(o.HaveLen(totalExpectedPods), fmt.Sprintf("Number of CLI pods are not as expected %d", totalExpectedPods))
41+
})
42+
43+
g.It("Verify regexes filters are applied", func() {
44+
// capture upto 500KB
45+
nsfilter := "openshift-monitoring"
46+
cliArgs := []string{"flows", "--regexes=SrcK8S_Namespace~" + nsfilter, "--copy=true", "--max-bytes=500000"}
47+
cmd := exec.Command(ocNetObservBinPath, cliArgs...)
48+
err := cmd.Run()
49+
o.Expect(err).NotTo(o.HaveOccurred())
50+
// cleanup()
51+
defer func() {
52+
cliArgs := []string{"cleanup"}
53+
cmd := exec.Command(ocNetObservBinPath, cliArgs...)
54+
err := cmd.Run()
55+
o.Expect(err).NotTo(o.HaveOccurred())
56+
}()
57+
o.Expect(cmd.ProcessState.ExitCode()).To(o.BeNumerically("==", 0), "oc-netobserv returned non-zero exit code")
58+
flowsJsonfile, err := getFlowsJSONFile()
59+
o.Expect(err).NotTo(o.HaveOccurred())
60+
flowsFile, err := os.Open(flowsJsonfile)
61+
o.Expect(err).NotTo(o.HaveOccurred())
62+
defer flowsFile.Close()
63+
decoder := json.NewDecoder(flowsFile)
64+
_, err = decoder.Token()
65+
o.Expect(err).NotTo(o.HaveOccurred())
66+
var flow Flowlog
67+
for decoder.More() {
68+
err := decoder.Decode(&flow)
69+
o.Expect(err).NotTo(o.HaveOccurred())
70+
if flow.SrcK8sNamespace != nsfilter {
71+
o.Expect(true).To(o.BeFalse(), fmt.Sprintf("Flow %v does not meet regexes condition SrcK8S_Namespace~%s", flow, nsfilter))
72+
}
73+
}
74+
})
75+
})
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package integrationtests
2+
3+
import (
4+
"os"
5+
"os/exec"
6+
"strings"
7+
"testing"
8+
9+
g "github.com/onsi/ginkgo/v2"
10+
o "github.com/onsi/gomega"
11+
)
12+
13+
var ocNetObservBinPath string
14+
15+
func TestIntegrationTests(t *testing.T) {
16+
o.RegisterFailHandler(g.Fail)
17+
g.RunSpecs(t, "IntegrationTests Suite")
18+
}
19+
20+
var _ = g.BeforeSuite(func() {
21+
// kubeconfig env var and see if the cluster is reachable.
22+
if kubeconfig := os.Getenv("KUBECONFIG"); kubeconfig == "" {
23+
g.Skip("Set KUBECONFIG env variable")
24+
}
25+
26+
cmd := exec.Command("which", "oc-netobserv")
27+
out, err := cmd.Output()
28+
o.Expect(err).NotTo(o.HaveOccurred())
29+
ocNetObservBinPath = strings.TrimSuffix(string(out), "\n")
30+
})

0 commit comments

Comments
 (0)