diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..38fe0a20be --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,42 @@ +version: 2 +jobs: + setup-and-test: + # docker: + # - image: golang:1.10 + machine: + image: circleci/classic:latest + steps: + - checkout + - run: + name: Setup-and-test + command: | + sudo -E env "PATH=$PATH" apt-get update + sudo -E env "PATH=$PATH" apt-get install -y ebtables + sudo -E env "PATH=$PATH" apt-get install -y ipset + mkdir -p /home/circleci/go1-10 + mkdir --parents /home/circleci/.goproject/src/github.com/Azure/azure-container-networking + wget https://storage.googleapis.com/golang/go1.10.2.linux-amd64.tar.gz + tar -C /home/circleci/go1-10 -xvf go1.10.2.linux-amd64.tar.gz + rm go1.10.2.linux-amd64.tar.gz + mv * /home/circleci/.goproject/src/github.com/Azure/azure-container-networking + cd /home/circleci/.goproject/src/github.com/Azure/azure-container-networking + export GOROOT='/home/circleci/go1-10/go' + export GOPATH='/home/circleci/.goproject' + export PATH=$GOROOT/bin:$PATH + go get ./... + go get github.com/docker/libnetwork/driverapi + go get github.com/gorilla/mux + sudo -E env "PATH=$PATH" go test ./ipam/ + sudo -E env "PATH=$PATH" go test ./log/ + sudo -E env "PATH=$PATH" go test ./netlink/ + sudo -E env "PATH=$PATH" go test ./store/ + sudo -E env "PATH=$PATH" go test ./telemetry/ + sudo -E env "PATH=$PATH" go test ./cni/ipam/ + sudo -E env "PATH=$PATH" go test ./cnm/network/ + sudo -E env "PATH=$PATH" go test ./cns/ipamclient/ + #sudo -E env "PATH=$PATH" go test ./cns/restserver/ +workflows: + version: 2 + run-tests: + jobs: + - setup-and-test \ No newline at end of file diff --git a/cnm/ipam/ipam_test.go b/cnm/ipam/ipam_test.go index 19eab96333..c83231fe5b 100644 --- a/cnm/ipam/ipam_test.go +++ b/cnm/ipam/ipam_test.go @@ -101,7 +101,7 @@ func handleIpamQuery(w http.ResponseWriter, r *http.Request) { // Decodes plugin's responses to test requests. func decodeResponse(w *httptest.ResponseRecorder, response interface{}) error { if w.Code != http.StatusOK { - return fmt.Errorf("Request failed with HTTP error %s", w.Code) + return fmt.Errorf("Request failed with HTTP error %d", w.Code) } if w.Body == nil { diff --git a/cnm/network/network_test.go b/cnm/network/network_test.go index 4bae7a3424..d9005aa0ab 100644 --- a/cnm/network/network_test.go +++ b/cnm/network/network_test.go @@ -23,8 +23,13 @@ import ( var plugin NetPlugin var mux *http.ServeMux -var anyInterface = "test0" +var anyInterface = "dummy" var anySubnet = "192.168.1.0/24" +var ipnet = net.IPNet{IP: net.ParseIP("192.168.1.0"), Mask: net.IPv4Mask(255, 255, 255, 0)} +var networkID = "N1" + +// endpoint ID must contain 7 characters +var endpointID = "E1-xxxx" // Wraps the test run with plugin setup and teardown. func TestMain(m *testing.M) { @@ -52,7 +57,7 @@ func TestMain(m *testing.M) { err = netlink.AddLink(&netlink.DummyLink{ LinkInfo: netlink.LinkInfo{ Type: netlink.LINK_TYPE_DUMMY, - Name: "dummy", + Name: anyInterface, }, }) @@ -67,6 +72,12 @@ func TestMain(m *testing.M) { os.Exit(4) } + err = netlink.AddIpAddress(anyInterface, net.ParseIP("192.168.1.4"), &ipnet) + if err != nil { + fmt.Printf("Failed to add test IP address, err:%v.\n", err) + os.Exit(5) + } + // Get the internal http mux as test hook. mux = plugin.(*netPlugin).Listener.GetMux() @@ -83,7 +94,7 @@ func TestMain(m *testing.M) { // Decodes plugin's responses to test requests. func decodeResponse(w *httptest.ResponseRecorder, response interface{}) error { if w.Code != http.StatusOK { - return fmt.Errorf("Request failed with HTTP error %s", w.Code) + return fmt.Errorf("Request failed with HTTP error %d", w.Code) } if w.Body == nil { @@ -144,7 +155,7 @@ func TestCreateNetwork(t *testing.T) { _, pool, _ := net.ParseCIDR(anySubnet) info := &remoteApi.CreateNetworkRequest{ - NetworkID: "N1", + NetworkID: networkID, IPv4Data: []driverApi.IPAMData{ { Pool: pool, @@ -164,23 +175,25 @@ func TestCreateNetwork(t *testing.T) { err = decodeResponse(w, &resp) - if err != nil || resp.Err != "" { + if err != nil || resp.Response.Err != "" { t.Errorf("CreateNetwork response is invalid %+v", resp) } } -// Tests NetworkDriver.DeleteNetwork functionality. -func TestDeleteNetwork(t *testing.T) { +// Tests NetworkDriver.CreateEndpoint functionality. +func TestCreateEndpoint(t *testing.T) { var body bytes.Buffer - var resp remoteApi.DeleteNetworkResponse + var resp remoteApi.CreateEndpointResponse - info := &remoteApi.DeleteNetworkRequest{ - NetworkID: "N1", + info := &remoteApi.CreateEndpointRequest{ + NetworkID: networkID, + EndpointID: endpointID, + Interface: &remoteApi.EndpointInterface{Address: anySubnet}, } json.NewEncoder(&body).Encode(info) - req, err := http.NewRequest(http.MethodGet, deleteNetworkPath, &body) + req, err := http.NewRequest(http.MethodGet, createEndpointPath, &body) if err != nil { t.Fatal(err) } @@ -190,8 +203,8 @@ func TestDeleteNetwork(t *testing.T) { err = decodeResponse(w, &resp) - if err != nil || resp.Err != "" { - t.Errorf("DeleteNetwork response is invalid %+v", resp) + if err != nil || resp.Response.Err != "" { + t.Errorf("CreateEndpoint response is invalid %+v", resp) } } @@ -201,8 +214,8 @@ func TestEndpointOperInfo(t *testing.T) { var resp remoteApi.EndpointInfoResponse info := &remoteApi.EndpointInfoRequest{ - NetworkID: "N1", - EndpointID: "E1", + NetworkID: networkID, + EndpointID: endpointID, } json.NewEncoder(&body).Encode(info) @@ -216,8 +229,33 @@ func TestEndpointOperInfo(t *testing.T) { mux.ServeHTTP(w, req) err = decodeResponse(w, &resp) - if err != nil || resp.Err != "" { t.Errorf("EndpointOperInfo response is invalid %+v", resp) } } + +// Tests NetworkDriver.DeleteNetwork functionality. +func TestDeleteNetwork(t *testing.T) { + var body bytes.Buffer + var resp remoteApi.DeleteNetworkResponse + + info := &remoteApi.DeleteNetworkRequest{ + NetworkID: networkID, + } + + json.NewEncoder(&body).Encode(info) + + req, err := http.NewRequest(http.MethodGet, deleteNetworkPath, &body) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + err = decodeResponse(w, &resp) + + if err != nil || resp.Err != "" { + t.Errorf("DeleteNetwork response is invalid %+v", resp) + } +} diff --git a/cns/restserver/restserver_test.go b/cns/restserver/restserver_test.go index 1d9c421a62..b55c11d4e3 100644 --- a/cns/restserver/restserver_test.go +++ b/cns/restserver/restserver_test.go @@ -6,18 +6,78 @@ package restserver import ( "bytes" "encoding/json" + "encoding/xml" "fmt" "net/http" "net/http/httptest" + "net/url" "os" "testing" "github.com/Azure/azure-container-networking/cns" "github.com/Azure/azure-container-networking/cns/common" + "github.com/Azure/azure-container-networking/cns/imdsclient" + acncommon "github.com/Azure/azure-container-networking/common" ) -var service HTTPService -var mux *http.ServeMux +type IPAddress struct { + XMLName xml.Name `xml:"IPAddress"` + Address string `xml:"Address,attr"` + IsPrimary bool `xml:"IsPrimary,attr"` +} +type IPSubnet struct { + XMLName xml.Name `xml:"IPSubnet"` + Prefix string `xml:"Prefix,attr"` + IPAddress []IPAddress +} + +type Interface struct { + XMLName xml.Name `xml:"Interface"` + MacAddress string `xml:"MacAddress,attr"` + IsPrimary bool `xml:"IsPrimary,attr"` + IPSubnet []IPSubnet +} + +type xmlDocument struct { + XMLName xml.Name `xml:"Interfaces"` + Interface []Interface +} + +var ( + service HTTPService + mux *http.ServeMux + hostQueryForProgrammedVersionResponse = `{"httpStatusCode":"200","networkContainerId":"eab2470f-test-test-test-b3cd316979d5","version":"1"}` + hostQueryResponse = xmlDocument{ + XMLName: xml.Name{Local: "Interfaces"}, + Interface: []Interface{Interface{ + XMLName: xml.Name{Local: "Interface"}, + MacAddress: "*", + IsPrimary: true, + IPSubnet: []IPSubnet{ + IPSubnet{XMLName: xml.Name{Local: "IPSubnet"}, + Prefix: "10.0.0.0/16", + IPAddress: []IPAddress{ + IPAddress{ + XMLName: xml.Name{Local: "IPAddress"}, + Address: "10.0.0.4", + IsPrimary: true}, + }}, + }, + }}, + } +) + +func getInterfaceInfo(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/xml") + output, _ := xml.Marshal(hostQueryResponse) + w.Write(output) +} + +func getContainerInfo(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json; charset=UTF-8") + w.WriteHeader(http.StatusOK) + w.Write([]byte(hostQueryForProgrammedVersionResponse)) +} // Wraps the test run with service setup and teardown. func TestMain(m *testing.M) { @@ -33,6 +93,11 @@ func TestMain(m *testing.M) { // Configure test mode. service.(*httpRestService).Name = "cns-test-server" + service.(*httpRestService).imdsClient.HostQueryURL = imdsclient.HostQueryURL + service.(*httpRestService).imdsClient.HostQueryURLForProgrammedVersion = imdsclient.HostQueryURLForProgrammedVersion + // Following HostQueryURL and HostQueryURLForProgrammedVersion are only for mock environment. + // service.(*httpRestService).imdsClient.HostQueryURL = "http://localhost:9000/getInterface" + // service.(*httpRestService).imdsClient.HostQueryURLForProgrammedVersion = "http://localhost:9000/machine/plugins/?comp=nmagent&type=NetworkManagement/interfaces/%s/networkContainers/%s/authenticationToken/%s/api-version/%s" // Start the service. err = service.Start(&config) @@ -44,6 +109,26 @@ func TestMain(m *testing.M) { // Get the internal http mux as test hook. mux = service.(*httpRestService).Listener.GetMux() + // Setup mock nmagent server + u, err := url.Parse("tcp://localhost:9000") + if err != nil { + fmt.Println(err.Error()) + } + + nmAgentServer, err := acncommon.NewListener(u) + if err != nil { + fmt.Println(err.Error()) + } + + nmAgentServer.AddHandler("/getInterface", getInterfaceInfo) + nmAgentServer.AddHandler("machine/plugins/?comp=nmagent&type=NetworkManagement/interfaces/{interface}/networkContainers/{networkContainer}/authenticationToken/{authToken}/api-version/{version}", getContainerInfo) + + err = nmAgentServer.Start(make(chan error, 1)) + if err != nil { + fmt.Printf("Failed to start agent, err:%v.\n", err) + return + } + // Run tests. exitCode := m.Run() @@ -71,7 +156,7 @@ func setEnv(t *testing.T) *httptest.ResponseRecorder { envRequestJSON := new(bytes.Buffer) json.NewEncoder(envRequestJSON).Encode(envRequest) - req, err := http.NewRequest(http.MethodPost, cns.V1Prefix+cns.SetEnvironmentPath, envRequestJSON) + req, err := http.NewRequest(http.MethodPost, cns.V2Prefix+cns.SetEnvironmentPath, envRequestJSON) if err != nil { t.Fatal(err) } diff --git a/log/logger_test.go b/log/logger_test.go index bd9842909d..10128ca91a 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -27,21 +27,21 @@ func TestLogFileRotatesWhenSizeLimitIsReached(t *testing.T) { l.Close() - fn := logName + ".log" + fn := l.GetLogDirectory() + logName + ".log" _, err := os.Stat(fn) if err != nil { t.Errorf("Failed to find active log file.") } os.Remove(fn) - fn = logName + ".log.1" + fn = l.GetLogDirectory() + logName + ".log.1" _, err = os.Stat(fn) if err != nil { t.Errorf("Failed to find the 1st rotated log file.") } os.Remove(fn) - fn = logName + ".log.2" + fn = l.GetLogDirectory() + logName + ".log.2" _, err = os.Stat(fn) if err == nil { t.Errorf("Found the 2nd rotated log file which should have been deleted.") diff --git a/netlink/netlink_test.go b/netlink/netlink_test.go index 14e70494f5..6105c41615 100644 --- a/netlink/netlink_test.go +++ b/netlink/netlink_test.go @@ -11,7 +11,7 @@ import ( const ( ifName = "nltest" ifName2 = "nltest2" - dummyName = "dummy0" + dummyName = "dummy1" ) // AddDummyInterface creates a dummy test interface used during actual tests. diff --git a/network/network_linux.go b/network/network_linux.go index 65a46bb3f0..5c336a2a2b 100644 --- a/network/network_linux.go +++ b/network/network_linux.go @@ -210,7 +210,6 @@ func (nm *networkManager) connectExternalInterface(extIf *externalInterface, nwI bridge, err := net.InterfaceByName(bridgeName) if err != nil { // Create the bridge. - if err := networkClient.CreateBridge(); err != nil { log.Printf("Error while creating bridge %+v", err) return err