diff --git a/.circleci/config.yml b/.circleci/config.yml index 664deed..27ed333 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -17,6 +17,16 @@ defaults-machine: &defaults-machine GOPATH: ~/go ORG: ciscoappnetworking +# Golang testing environment. +defaults-go-tester: &defaults-go-tester + working_directory: /go/src/github.com/cisco-app-networking/nsm-nse + docker: + - image: cimg/go:1.15 + environment: + GOPATH: /home/circleci/go + ORG: ciscoappnetworking + + commands: publish-steps: parameters: @@ -158,6 +168,21 @@ e2e-kind-kiknos-test: &e2e-kind-kiknos-test - store_artifacts: path: /tmp/cluster_state +run-all-go-tests: &run-all-go-tests + steps: + - checkout: + path: ~/go/src/github.com/cisco-app-networking/nsm-nse + - run: + name: Run all go unit tests + working_directory: ~/go/src/github.com/cisco-app-networking/nsm-nse/ + # runs all the go tests recursively (excluding test/e2e and cmd/vl3-nse directories) + command: | + go test $(go list ./... | grep -v /test/e2e | grep -v cmd/vl3-nse) + #- run: + # name: Run all go unit tests with coverage tests + # working_directory: ~/go/src/github.com/cisco-app-networking/nsm-nse/ + # command: | + # go test $(go list ./... | grep -v /test/e2e | grep -v cmd/vl3-nse) -coverprofile .testCoverage.txt jobs: build-NSEs: @@ -206,6 +231,10 @@ jobs: - publish-steps: tag: ${CIRCLE_TAG} + go-test-integration: + <<: *defaults-go-tester + <<: *run-all-go-tests + orbs: helm: circleci/helm@0.2.1 @@ -230,6 +259,7 @@ workflows: only: - master - /release.*/ + - go-test-integration test-and-push-tagged: jobs: - build-NSEs: @@ -264,4 +294,10 @@ workflows: ignore: /.*/ tags: only: /^v[0-9]+\.[0-9]+\.[0-9]+-vl3/ + - go-test-integration: + filters: + branches: + ignore: /.*/ + tags: + only: /^v[0-9]+\.[0-9]+\.[0-9]+-vl3/ diff --git a/go.mod b/go.mod index 7beda64..703feea 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/sirupsen/logrus v1.6.0 github.com/stretchr/testify v1.4.0 go.ligato.io/vpp-agent/v3 v3.2.0 + golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0 golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e google.golang.org/grpc v1.29.1 gopkg.in/yaml.v2 v2.3.0 diff --git a/go.sum b/go.sum index d8856c3..c585b02 100644 --- a/go.sum +++ b/go.sum @@ -407,6 +407,7 @@ github.com/onsi/ginkgo v1.12.1 h1:mFwc4LvZ0xpSvDZ3E+k8Yte0hLOMxXUlP+yXtJqkYfQ= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/gomega v1.1.0/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= diff --git a/pkg/universal-cnf/config/composite_test.go b/pkg/universal-cnf/config/composite_test.go new file mode 100644 index 0000000..58c53b9 --- /dev/null +++ b/pkg/universal-cnf/config/composite_test.go @@ -0,0 +1,283 @@ +package config + +import ( + "fmt" + "os" + "testing" + + "github.com/cisco-app-networking/nsm-nse/pkg/nseconfig" + "github.com/networkservicemesh/networkservicemesh/controlplane/api/connection" + "github.com/networkservicemesh/networkservicemesh/controlplane/api/connectioncontext" + "github.com/networkservicemesh/networkservicemesh/controlplane/api/networkservice" + "github.com/sirupsen/logrus" + "go.ligato.io/vpp-agent/v3/proto/ligato/vpp" + "golang.org/x/net/context" + + . "github.com/onsi/gomega" + + vpp_interfaces "go.ligato.io/vpp-agent/v3/proto/ligato/vpp/interfaces" +) + +const ( + // fields of type nseconfig.Endpoint + ucnfeName string = "" + nseName string = "" + nseControlName string = "" + nseControlAddress string = "" + nseControlAccessToken string = "" + nseControlConnectivityDomain string = "" + vl3Ifname string = "" + ipamDefaultPrefixPool string = "" + ipamServerAddress string = "" + ipamPrefixLength int = 0 + + // fields of type connection.Connection + connId string = "" + connNetworkService string = "uncf" + machanismCls string = "" + mechanismType string = "MEMIF" + paraDescription string = "" + paraName string = "test" + paraNetnsInode string = "" + paraSocketfile = paraName + "/memif.sock" + srcIP string = "192.168.22.2/30" + dstIP string = "192.168.22.1/30" + srcRequired bool = true + dstRequired bool = true + dstRoutePrefix string = "192.168.0.0/16" + labelsNamespace string = "default" + ucnfPodName string = "helloworld-ucnf-" + labelsPodName = ucnfPodName + "test" + controlPlaneName string = "-control-plane" + clusterName string = "kind-2" + pathSegmentName = clusterName + controlPlaneName +) + +var ( + // fields of type nseconfig.Endpoint + ucnfeLabels = map[string]string{} + ipamRoute = []string{""} + vl3NameServers = []string{""} + vl3DNSZones = []string{""} +) + +// mock implementor +type MockUcnfBackend struct { + newDPConfig func() *vpp.ConfigData + newUniversalCNFBackEnd func() error + processClient func(dpconfig interface{}, ifName string, conn *connection.Connection) error + processDPConfig func(dpconfig interface{}, update bool) error + processEndpoints func(dpconfig interface{}, serviceName, ifName string, conn *connection.Connection) error +} + +/* +mock functions returning the defined overriding functions otherwise will return nil by default +*/ +func (m *MockUcnfBackend) NewDPConfig() *vpp.ConfigData { + if m.newDPConfig == nil { + return &vpp.ConfigData{} + } + return m.newDPConfig() +} + +func (m *MockUcnfBackend) NewUniversalCNFBackend() error { + if m.newUniversalCNFBackEnd == nil { + return nil + } + return m.newUniversalCNFBackEnd() +} + +func (m *MockUcnfBackend) ProcessClient(dpconfig interface{}, ifName string, conn *connection.Connection) error { + if m.processClient == nil { + return nil + } + return m.processClient(dpconfig, ifName, conn) +} + +func (m *MockUcnfBackend) ProcessEndpoint(dpconfig interface{}, serviceName, ifName string, conn *connection.Connection) error { + if m.processEndpoints == nil { + return nil + } + return m.processEndpoints(dpconfig, serviceName, ifName, conn) +} + +func (m *MockUcnfBackend) ProcessDPConfig(dpconfig interface{}, update bool) error { + if m.processDPConfig == nil { + return nil + } + return m.processDPConfig(dpconfig, update) +} + +func createNseConfigEndpoint() (e *nseconfig.Endpoint) { + e = &nseconfig.Endpoint{ + Name: ucnfeName, + Labels: ucnfeLabels, + NseName: nseName, + NseControl: &nseconfig.NseControl{ + Name: nseControlName, + Address: nseControlAddress, + AccessToken: nseControlAccessToken, + ConnectivityDomain: nseControlConnectivityDomain, + }, + VL3: nseconfig.VL3{ + IPAM: nseconfig.IPAM{ + DefaultPrefixPool: ipamDefaultPrefixPool, + PrefixLength: ipamPrefixLength, + Routes: ipamRoute, + ServerAddress: ipamServerAddress, + }, + Ifname: vl3Ifname, + NameServers: vl3NameServers, + DNSZones: vl3DNSZones, + }, + } + return +} + +func createNsmConnection() (conn *connection.Connection) { + conn = &connection.Connection{ + Id: connId, + NetworkService: connNetworkService, + Mechanism: &connection.Mechanism{ + Cls: machanismCls, + Type: mechanismType, + Parameters: map[string]string{ + "description": paraDescription, + "name": paraName, + "netnsInode": paraNetnsInode, + "socketfile": paraSocketfile, + }, + }, + Context: &connectioncontext.ConnectionContext{ + IpContext: &connectioncontext.IPContext{ + SrcIpAddr: srcIP, + DstIpAddr: dstIP, + SrcIpRequired: srcRequired, + DstIpRequired: dstRequired, + DstRoutes: []*connectioncontext.Route{ + {Prefix: dstRoutePrefix}, + }, + }, + }, + Labels: map[string]string{ + "namespace": labelsNamespace, + "podName": labelsPodName, + }, + Path: &connection.Path{ + PathSegments: []*connection.PathSegment{ + {Name: pathSegmentName}, + }, + }, + } + return +} + +/* +assigning an empty interface to dpConfig to prevent crashing, it cannot be a null pointer +*/ +func createVppDpConfig() (dpConfig *vpp.ConfigData) { + dpConfig = &vpp.ConfigData{ + Interfaces: []*vpp_interfaces.Interface{}, + } + return +} + +func initUcnfEndpoint() (ucnfe UniversalCNFEndpoint) { + ucnfe = UniversalCNFEndpoint{ + endpoint: createNseConfigEndpoint(), + backend: &MockUcnfBackend{}, + dpConfig: createVppDpConfig(), + } + return +} + +func TestMain(m *testing.M) { + runTests := m.Run() + os.Exit(runTests) +} + +/* +description is the log describing what the test do +positiveTest is a flag to determine whether positive or negative tests +*/ +type ucnfBackendTest struct { + mockBackend *MockUcnfBackend + description string + positiveTest bool +} + +/* +run either positive or negative tests depending on the flag + */ +func runTest(g *GomegaWithT, err error, flag bool){ + if !flag{ + g.Expect(err).Should(HaveOccurred(), fmt.Sprintf("ERROR : %v", err)) + } else { + g.Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("ERROR : %v", err)) + } +} + +/* +this test ensures that the Request function can get return values fromProcessEndpoint function and ProcessDPConfig function +*/ +func TestRequest(t *testing.T) { + g := NewWithT(t) + ctx := context.TODO() + r := &networkservice.NetworkServiceRequest{} + ucnfe := initUcnfEndpoint() + + tables := []ucnfBackendTest{ + {mockBackend: &MockUcnfBackend{processDPConfig: func(dpconfig interface{}, update bool) error { + return nil + }, + }, description: "method ProcessDPConfig() should return nil", positiveTest: true}, + {mockBackend: &MockUcnfBackend{processEndpoints: func(dpconfig interface{}, serviceName, ifName string, conn *connection.Connection) error { + return nil + }, + }, description: "method ProcessEndpoints() should return nil", positiveTest: true}, + {mockBackend: &MockUcnfBackend{processDPConfig: func(dpconfig interface{}, update bool) error { + return fmt.Errorf("failed to run ProcessDPConfig() method") + }, + }, description: "method ProcessDPConfig() should return err", positiveTest: false}, + {mockBackend: &MockUcnfBackend{processEndpoints: func(dpconfig interface{}, serviceName, ifName string, conn *connection.Connection) error { + return fmt.Errorf("failed to run ProcessEndpoint() method") + }, + }, description: "method ProcessEndpoints() should return err", positiveTest: false}, + } + + for _, table := range tables { + ucnfe.backend = table.mockBackend + logrus.Printf(table.description) + _, err := ucnfe.Request(ctx, r) + runTest(g, err, table.positiveTest) + } +} + +/* +this test ensures that the Close function can get return values of ProcessDPConfig function +*/ +func TestClose(t *testing.T) { + g := NewWithT(t) + ctx := context.TODO() + ucnfe := initUcnfEndpoint() + conn := createNsmConnection() + + tables := []ucnfBackendTest{ + {mockBackend: &MockUcnfBackend{processDPConfig: func(dpconfig interface{}, update bool) error { + return nil + }, + }, description: "method ProcessDPConfig() should return nil", positiveTest: true}, + {mockBackend: &MockUcnfBackend{processDPConfig: func(dpconfig interface{}, update bool) error { + return fmt.Errorf("failed to run ProcessDPConfig() method") + }, + }, description: "method ProcessDPConfig() should return err", positiveTest: false}, + } + + for _, table := range tables { + ucnfe.backend = table.mockBackend + logrus.Printf(table.description) + _, err := ucnfe.Close(ctx, conn) + runTest(g, err, table.positiveTest) + } +} +