Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -724,7 +724,7 @@ test-integration: ## run all integration tests.
go test -mod=readonly -buildvcs=false -timeout 1h -coverpkg=./... -race -covermode atomic -coverprofile=coverage.out -tags=integration ./test/integration...

test-validate-state:
cd test/integration/load && go test -count 1 -timeout 30m -tags load -run ^TestValidateState -tags=load -restart-case=$(RESTART_CASE) -os=$(OS)
cd test/integration/load && go test -mod=readonly -count=1 -timeout 30m -tags load -run ^TestValidateState -tags=load -restart-case=$(RESTART_CASE) -os=$(OS)
cd ../../..

test-cyclonus: ## run the cyclonus test for npm.
Expand Down
50 changes: 8 additions & 42 deletions test/integration/load/load_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ package load
import (
"context"
"flag"
"fmt"
"testing"
"time"

Expand Down Expand Up @@ -36,13 +35,6 @@ var noopDeploymentMap = map[string]string{
"linux": manifestDir + "/noop-deployment-linux.yaml",
}

// Todo: Add the validation for the data path function for the linux/windows client.
type stateValidator interface {
ValidateStateFile(context.Context) error
ValidateRestartNetwork(context.Context) error
// ValidateDataPath() error
}

/*
In order to run the scale tests, you need a k8s cluster and its kubeconfig.
If no kubeconfig is passed, the test will attempt to find one in the default location for kubectl config.
Expand All @@ -60,10 +52,10 @@ todo: consider adding the following scenarios
- [x] Test the CNS Local cache.
- [x] Test the Cilium state file.
- [x] Test the Node restart.
- [ ] Test based on operating system.
- [ ] Test the HNS state file.
- [ ] Parameterize the os, cni and number of iterations.
- [ ] Add deployment yaml for windows.
- [x] Test based on operating system.
- [x] Test the HNS state file.
- [x] Parameterize the os, cni and number of iterations.
- [x] Add deployment yaml for windows.
*/
func TestLoad(t *testing.T) {
clientset, err := k8sutils.MustGetClientset()
Expand Down Expand Up @@ -139,39 +131,13 @@ func TestValidateState(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Minute)
defer cancel()

var validator stateValidator

t.Log("Validating the state file")
switch *osType {
case "linux":
validator, err = validate.CreateLinuxValidator(ctx, clientset, config, namespace, *cniType, *restartCase)
if err != nil {
t.Fatal(err)
}
case "windows":
validator, err = validate.CreateWindowsValidator(ctx, clientset, config, namespace, *cniType, *restartCase)
if err != nil {
t.Fatal(err)
}
default:
t.Fatalf("unknown os type %s", *osType)
}

err = validator.ValidateStateFile(ctx)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is linux specific, I moved it linux validate file instead of load test.

validator, err := validate.CreateValidator(ctx, clientset, config, namespace, *cniType, *restartCase, *osType)
if err != nil {
t.Fatal(err)
}

// We are restarting the systmemd network and checking that the connectivity works after the restart. For more details: https://github.com/cilium/cilium/issues/18706
t.Log("Validating the restart network scenario")
t.Run(fmt.Sprintf("validate network restart - %s", *osType), func(t *testing.T) {
if *osType == "windows" {
t.Skip("validate network restart not implemented on Windows")
}
if err := validator.ValidateRestartNetwork(ctx); err != nil {
t.Fatal(err)
}
})
if err := validator.Validate(ctx); err != nil {
t.Fatal(err)
}
}

// TestScaleDeployment scales the deployment up/down based on the replicas passed.
Expand Down
137 changes: 9 additions & 128 deletions test/validate/linux_validate.go
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
package validate

import (
"context"
"encoding/json"
"log"

"github.com/Azure/azure-container-networking/cns"
restserver "github.com/Azure/azure-container-networking/cns/restserver"
k8sutils "github.com/Azure/azure-container-networking/test/internal/k8sutils"
"github.com/pkg/errors"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
)

const (
privilegedDaemonSetPath = "../manifests/load/privileged-daemonset.yaml"
privilegedLabelSelector = "app=privileged-daemonset"
privilegedNamespace = "kube-system"

cnsLabelSelector = "k8s-app=azure-cns"
ciliumLabelSelector = "k8s-app=cilium"
)
Expand All @@ -31,12 +22,15 @@ var (

type stateFileIpsFunc func([]byte) (map[string]string, error)

type LinuxValidator struct {
clientset *kubernetes.Clientset
config *rest.Config
namespace string
cni string
restartCase bool
var linuxChecksMap = map[string][]check{
"cilium": {
{"cns", cnsStateFileIps, cnsLabelSelector, privilegedNamespace, cnsStateFileCmd},
{"cilium", ciliumStateFileIps, ciliumLabelSelector, privilegedNamespace, ciliumStateFileCmd},
{"cns cache", cnsCacheStateFileIps, cnsLabelSelector, privilegedNamespace, cnsLocalCacheCmd},
},
"cniv2": {
{"cns cache", cnsCacheStateFileIps, cnsLabelSelector, privilegedNamespace, cnsLocalCacheCmd},
},
}

type CnsState struct {
Expand Down Expand Up @@ -64,79 +58,6 @@ type Address struct {
Addr string `json:"ipv4"`
}

func CreateLinuxValidator(ctx context.Context, clienset *kubernetes.Clientset, config *rest.Config, namespace, cni string, restartCase bool) (*LinuxValidator, error) {
// deploy privileged pod
privilegedDaemonSet, err := k8sutils.MustParseDaemonSet(privilegedDaemonSetPath)
if err != nil {
return nil, errors.Wrap(err, "unable to parse daemonset")
}
daemonsetClient := clienset.AppsV1().DaemonSets(privilegedNamespace)
if err := k8sutils.MustCreateDaemonset(ctx, daemonsetClient, privilegedDaemonSet); err != nil {
return nil, errors.Wrap(err, "unable to create daemonset")
}
if err := k8sutils.WaitForPodsRunning(ctx, clienset, privilegedNamespace, privilegedLabelSelector); err != nil {
return nil, errors.Wrap(err, "error while waiting for pods to be running")
}

return &LinuxValidator{
clientset: clienset,
config: config,
namespace: namespace,
cni: cni,
restartCase: restartCase,
}, nil
}

// Todo: Based on cni version validate different state files
func (v *LinuxValidator) ValidateStateFile(ctx context.Context) error {
checkSet := make(map[string][]check) // key is cni type, value is a list of check
// TODO: add cniv1 when adding Linux related test cases
checkSet["cilium"] = []check{
{"cns", cnsStateFileIps, cnsLabelSelector, privilegedNamespace, cnsStateFileCmd},
{"cilium", ciliumStateFileIps, ciliumLabelSelector, privilegedNamespace, ciliumStateFileCmd},
{"cns cache", cnsCacheStateFileIps, cnsLabelSelector, privilegedNamespace, cnsLocalCacheCmd},
}

checkSet["cniv2"] = []check{
{"cns cache", cnsCacheStateFileIps, cnsLabelSelector, privilegedNamespace, cnsLocalCacheCmd},
}

for _, check := range checkSet[v.cni] {
err := v.validateIPs(ctx, check.stateFileIps, check.cmd, check.name, check.podNamespace, check.podLabelSelector)
if err != nil {
return err
}
}
return nil
}

func (v *LinuxValidator) ValidateRestartNetwork(ctx context.Context) error {
nodes, err := k8sutils.GetNodeList(ctx, v.clientset)
if err != nil {
return errors.Wrapf(err, "failed to get node list")
}

for index := range nodes.Items {
// get the privileged pod
pod, err := k8sutils.GetPodsByNode(ctx, v.clientset, privilegedNamespace, privilegedLabelSelector, nodes.Items[index].Name)
if err != nil {
return errors.Wrapf(err, "failed to get privileged pod")
}

privelegedPod := pod.Items[0]
// exec into the pod to get the state file
_, err = k8sutils.ExecCmdOnPod(ctx, v.clientset, privilegedNamespace, privelegedPod.Name, restartNetworkCmd, v.config)
if err != nil {
return errors.Wrapf(err, "failed to exec into privileged pod")
}
err = k8sutils.WaitForPodsRunning(ctx, v.clientset, "", "")
if err != nil {
return errors.Wrapf(err, "failed to wait for pods running")
}
}
return nil
}

func cnsStateFileIps(result []byte) (map[string]string, error) {
var cnsResult CnsState
err := json.Unmarshal(result, &cnsResult)
Expand Down Expand Up @@ -188,43 +109,3 @@ func cnsCacheStateFileIps(result []byte) (map[string]string, error) {
}
return cnsPodIps, nil
}

func (v *LinuxValidator) validateIPs(ctx context.Context, stateFileIps stateFileIpsFunc, cmd []string, checkType, namespace, labelSelector string) error {
log.Printf("Validating %s state file", checkType)
nodes, err := k8sutils.GetNodeList(ctx, v.clientset)
if err != nil {
return errors.Wrapf(err, "failed to get node list")
}

for index := range nodes.Items {
// get the privileged pod
pod, err := k8sutils.GetPodsByNode(ctx, v.clientset, namespace, labelSelector, nodes.Items[index].Name)
if err != nil {
return errors.Wrapf(err, "failed to get privileged pod")
}
podName := pod.Items[0].Name
// exec into the pod to get the state file
result, err := k8sutils.ExecCmdOnPod(ctx, v.clientset, namespace, podName, cmd, v.config)
if err != nil {
return errors.Wrapf(err, "failed to exec into privileged pod")
}
filePodIps, err := stateFileIps(result)
if err != nil {
return errors.Wrapf(err, "failed to get pod ips from state file")
}
if len(filePodIps) == 0 && v.restartCase {
log.Printf("No pods found on node %s", nodes.Items[index].Name)
continue
}
// get the pod ips
podIps := getPodIPsWithoutNodeIP(ctx, v.clientset, nodes.Items[index])

check := compareIPs(filePodIps, podIps)

if !check {
return errors.Wrapf(errors.New("State file validation failed"), "for %s on node %s", checkType, nodes.Items[index].Name)
}
}
log.Printf("State file validation for %s passed", checkType)
return nil
}
Loading