From f838cbe8600dcf021f37718fdc4c90fa64008a7e Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Fri, 2 Jun 2017 16:23:08 -0700 Subject: [PATCH 01/34] Create container network service (does not contain integration with IPAM and Docker) --- cns/api.go | 115 +++++++++++++ cns/client/examples.go | 225 +++++++++++++++++++++++++ cns/main/main.go | 151 +++++++++++++++++ cns/restserver/httprest.go | 266 +++++++++++++++++++++++++++++ cns/restserver/httprest_test.go | 290 ++++++++++++++++++++++++++++++++ cns/service.go | 103 ++++++++++++ common/service.go | 79 +++++++++ 7 files changed, 1229 insertions(+) create mode 100644 cns/api.go create mode 100644 cns/client/examples.go create mode 100644 cns/main/main.go create mode 100644 cns/restserver/httprest.go create mode 100644 cns/restserver/httprest_test.go create mode 100644 cns/service.go create mode 100644 common/service.go diff --git a/cns/api.go b/cns/api.go new file mode 100644 index 0000000000..13d545b982 --- /dev/null +++ b/cns/api.go @@ -0,0 +1,115 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package cns + +// Container Network Service remote API Contract +const ( + SetEnvironmentPath = "/Network/Environment" + CreateNetworkPath = "/Network/Create" + DeleteNetworkPath = "/Network/Delete" + ReserveIPAddressPath = "/Network/IP/Reserve" + ReleaseIPAddressPath = "/Network/IP/Release" + GetHostLocalIPPath = "/Network/IP/HostLocal" + GetIPAddressUtilizationPath = "/Network/IP/Utilization" + GetAvailableIPAddressesPath = "/Network/IPAddresses/Available" + GetReservedIPAddressesPath = "/Network/IPAddresses/Reserved" + GetGhostIPAddressesPath = "/Network/IPAddresses/Ghost" + GetAllIPAddressesPath = "/Network/IPAddresses/All" + GetHealthReportPath = "/Network/Health" +) + +// SetEnvironmentRequest describes the Request to set the environment in HNS. +type SetEnvironmentRequest struct { + Location string + NetworkType string +} + +// OverlayConfiguration describes configuration for all the nodes that are part of overlay. +type OverlayConfiguration struct { + NodeCount int + LocalNodeIP string + OverlaySubent Subnet + NodeConfig []NodeConfiguration +} + +// CreateNetworkRequest describes request to create the network. +type CreateNetworkRequest struct { + NetworkName string + OverlayConfiguration OverlayConfiguration +} + +// DeleteNetworkRequest describes request to delete the network. +type DeleteNetworkRequest struct { + NetworkName string +} + +// ReserveIPAddressRequest describes request to reserve an IP Address +type ReserveIPAddressRequest struct { + ReservationID string +} + +// ReserveIPAddressResponse describes response to reserve an IP address. +type ReserveIPAddressResponse struct { + Response Response + IPAddress string +} + +// ReleaseIPAddressRequest describes request to release an IP Address. +type ReleaseIPAddressRequest struct { + ReservationID string +} + +// IPAddressesUtilizationResponse describes response for ip address utilization. +type IPAddressesUtilizationResponse struct { + Response Response + Available int + Reserved int + Ghost int +} + +// GetIPAddressesResponse describes response containing requested ip addresses. +type GetIPAddressesResponse struct { + Response Response + IPAddresses []IPAddress +} + +// HostLocalIPAddressResponse describes reponse that returns the host local IP Address. +type HostLocalIPAddressResponse struct { + Response Response + IPAddress string +} + +// IPAddress Contains information about an ip address. +type IPAddress struct { + IPAddress string + ReservationID string + IsGhost bool +} + +// Subnet contains the ip address and the number of bits in prefix. +type Subnet struct { + IPAddress string + PrefixLength int +} + +// NodeConfiguration describes confguration for a node in overlay network. +type NodeConfiguration struct { + NodeIP string + NodeID string + NodeSubnet Subnet +} + +// Response describes generic response from CNS. +type Response struct { + ReturnCode int + Message string +} + +// OptionMap describes generic options that can be passed to CNS. +type OptionMap map[string]interface{} + +// Response to a failed request. +type errorResponse struct { + Err string +} \ No newline at end of file diff --git a/cns/client/examples.go b/cns/client/examples.go new file mode 100644 index 0000000000..e7d57c9807 --- /dev/null +++ b/cns/client/examples.go @@ -0,0 +1,225 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package main + +import ( + "bytes" + "fmt" + "encoding/json" + "net/http" + + "github.com/Azure/azure-container-networking/cns" +) + +const ( + defaultCNSServerURL = "http://localhost:10090" +) + +func setEnvironment() error{ + envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} + envRequestJSON := new(bytes.Buffer) + json.NewEncoder(envRequestJSON).Encode(envRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.SetEnvironmentPath, + "application/json; charset=utf-8", + envRequestJSON) + if err != nil { + fmt.Printf("Error received in Set Env: %v ", err.Error()) + return err + } + var setEnvironmentResponse cns.Response + err = json.NewDecoder(res.Body).Decode(&setEnvironmentResponse) + if err != nil{ + fmt.Printf("Error received in decoding response from SetEnvironment: %v ", err.Error()) + return err + } + fmt.Printf("Response for SetEnvironment: %+v\n", setEnvironmentResponse) + return nil +} + +func createNetwork() error{ + netRequest := cns.CreateNetworkRequest{NetworkName:"azurenet"} + netRequestJSON := new(bytes.Buffer) + json.NewEncoder(netRequestJSON).Encode(netRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.CreateNetworkPath, + "application/json; charset=utf-8", + netRequestJSON) + if err != nil { + fmt.Printf("Error received in CreateNetwork post: %v ", err.Error()) + return err + } + + var createNetworkResponse cns.Response + err = json.NewDecoder(res.Body).Decode(&createNetworkResponse) + if err != nil{ + fmt.Printf("Error received in decoding response from CreateNEtwork: %v ", err.Error()) + return err + } + fmt.Printf("Response for CreateNetwork: %+v\n", createNetworkResponse) + return nil +} + +func reserveIPAddress() error{ + reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID:"ip01"} + reserveIPRequestJSON := new(bytes.Buffer) + json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.ReserveIPAddressPath, + "application/json; charset=utf-8", + reserveIPRequestJSON) + if err != nil { + fmt.Printf("Error received in reserveIPAddress post: %v ", err.Error()) + return err + } + var reserveIPAddressResponse cns.ReserveIPAddressResponse + err = json.NewDecoder(res.Body).Decode(&reserveIPAddressResponse) + if err != nil{ + fmt.Printf("Error received in decoding response from reserveIPAddress: %v ", err.Error()) + return err + } + fmt.Printf("Response for reserveIPAddress: %+v\n", reserveIPAddressResponse) + return nil +} + +func releaseIPAddress() error{ + releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID:"ip01"} + releaseIPAddressRequestJSON := new(bytes.Buffer) + json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.ReleaseIPAddressPath, + "application/json; charset=utf-8", + releaseIPAddressRequestJSON) + if err != nil { + fmt.Printf("Error received in releaseIPAddress post: %v ", err.Error()) + return err + } + var releaseIPAddressResponse cns.Response + err = json.NewDecoder(res.Body).Decode(&releaseIPAddressResponse) + if err != nil{ + fmt.Printf("Error received in decoding response from releaseIPAddress: %v ", err.Error()) + return err + } + fmt.Printf("Response for releaseIPAddress: %+v\n", releaseIPAddressResponse) + return nil +} + +func getIPAddressUtilization() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetIPAddressUtilizationPath) + if err != nil { + fmt.Printf("Error received in getIPAddressUtilization GET: %v ", err.Error()) + return err + } + var iPAddressesUtilizationResponse cns.IPAddressesUtilizationResponse + err = json.NewDecoder(res.Body).Decode(&iPAddressesUtilizationResponse) + if err != nil { + fmt.Printf("Error received in decoding response from getIPAddressUtilization: %v ", err.Error()) + return err + } + fmt.Printf("Response for getIPAddressUtilization: %+v\n", iPAddressesUtilizationResponse) + return nil +} + +func getHostLocalIP() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetHostLocalIPPath) + if err != nil { + fmt.Printf("Error received in getHostLocalIP GET: %v ", err.Error()) + return err + } + var hostLocalIPAddressResponse cns.HostLocalIPAddressResponse + err = json.NewDecoder(res.Body).Decode(&hostLocalIPAddressResponse) + if err != nil { + fmt.Printf("Error received in decoding response from getHostLocalIP: %v ", err.Error()) + return err + } + fmt.Printf("Response for getHostLocalIP: %+v\n", hostLocalIPAddressResponse) + return nil +} + +func getAvailableIPAddresses() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetAvailableIPAddressesPath) + if err != nil { + fmt.Printf("Error received in GetAvailable IP Addresses: %v ", err.Error()) + return err + } + var getIPAddressesResponse cns.GetIPAddressesResponse + err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) + if err != nil { + fmt.Printf("Error received in decoding response from GetAvailableIPAddresses: %v ", err.Error()) + return err + } + fmt.Printf("Response for GetAvailableIPAddresses: %+v\n", getIPAddressesResponse) + return nil +} + +func getReservedIPAddresses() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetReservedIPAddressesPath) + if err != nil { + fmt.Printf("Error received in GetReserved IP Addresses: %v ", err.Error()) + return err + } + var getIPAddressesResponse cns.GetIPAddressesResponse + err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) + if err != nil { + fmt.Printf("Error received in decoding response from getReservedIPAddresses: %v ", err.Error()) + return err + } + fmt.Printf("Response for getReservedIPAddresses: %+v\n", getIPAddressesResponse) + return nil +} + +func getGhostIPAddresses() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetGhostIPAddressesPath) + if err != nil { + fmt.Printf("Error received in GetGhost IP Addresses: %v ", err.Error()) + return err + } + var getIPAddressesResponse cns.GetIPAddressesResponse + err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) + if err != nil { + fmt.Printf("Error received in decoding response from getGhostIPAddresses: %v ", err.Error()) + return err + } + fmt.Printf("Response for getGhostIPAddresses: %+v\n", getIPAddressesResponse) + return nil +} + +func getAllIPAddresses() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetAllIPAddressesPath) + if err != nil { + fmt.Printf("Error received in GetAll IP Addresses: %v ", err.Error()) + return err + } + var getIPAddressesResponse cns.GetIPAddressesResponse + err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) + if err != nil { + fmt.Printf("Error received in decoding response from getAllIPAddresses: %v ", err.Error()) + return err + } + fmt.Printf("Response for getAllIPAddresses: %+v\n", getIPAddressesResponse) + return nil +} + +func main() { + setEnvironment() + createNetwork() + reserveIPAddress() + releaseIPAddress() + getAvailableIPAddresses() + getReservedIPAddresses() + getGhostIPAddresses() + getAllIPAddresses() + getIPAddressUtilization() + getHostLocalIP() +} diff --git a/cns/main/main.go b/cns/main/main.go new file mode 100644 index 0000000000..7a0ebc79af --- /dev/null +++ b/cns/main/main.go @@ -0,0 +1,151 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package main + +import ( + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/Azure/azure-container-networking/cns/restserver" + "github.com/Azure/azure-container-networking/common" + "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/platform" + "github.com/Azure/azure-container-networking/store" +) + +const ( + // Service name. + name = "azure-cns" +) + +// Version is populated by make during build. +var version string + +// Command line arguments for CNM plugin. +var args = common.ArgumentList{ + { + Name: common.OptAPIServerURL, + Shorthand: common.OptAPIServerURLAlias, + Description: "Set the API server URL", + Type: "string", + DefaultValue: "", + }, + { + Name: common.OptLogLevel, + Shorthand: common.OptLogLevelAlias, + Description: "Set the logging level", + Type: "int", + DefaultValue: common.OptLogLevelInfo, + ValueMap: map[string]interface{}{ + common.OptLogLevelInfo: log.LevelInfo, + common.OptLogLevelDebug: log.LevelDebug, + }, + }, + { + Name: common.OptLogTarget, + Shorthand: common.OptLogTargetAlias, + Description: "Set the logging target", + Type: "int", + DefaultValue: common.OptLogTargetFile, + ValueMap: map[string]interface{}{ + common.OptLogTargetSyslog: log.TargetSyslog, + common.OptLogTargetStderr: log.TargetStderr, + common.OptLogTargetFile: log.TargetLogfile, + }, + }, + { + Name: common.OptVersion, + Shorthand: common.OptVersionAlias, + Description: "Print version information", + Type: "bool", + DefaultValue: false, + }, +} + +// Prints description and version information. +func printVersion() { + fmt.Printf("Azure Container Network Service\n") + fmt.Printf("Version %v\n", version) +} + +// Main is the entry point for CNS. +func main() { + // Initialize and parse command line arguments. + common.ParseArgs(&args, printVersion) + + url := common.GetArg(common.OptAPIServerURL).(string) + logLevel := common.GetArg(common.OptLogLevel).(int) + logTarget := common.GetArg(common.OptLogTarget).(int) + vers := common.GetArg(common.OptVersion).(bool) + + if vers { + printVersion() + os.Exit(0) + } + + // Initialize CNS. + var config common.ServiceConfig + config.Version = version + config.Name = name + + // Create a channel to receive unhandled errors from CNS. + config.ErrChan = make(chan error, 1) + + // Create CNS object. + httpRestService, err := restserver.NewHTTPRestService(&config) + if err != nil { + fmt.Printf("Failed to create CNS object, err:%v.\n", err) + return + } + + // Create the key value store. + config.Store, err = store.NewJsonFileStore(platform.RuntimePath + name + ".json") + if err != nil { + fmt.Printf("Failed to create store: %v\n", err) + return + } + + // Create logging provider. + log.SetName(name) + log.SetLevel(logLevel) + err = log.SetTarget(logTarget) + if err != nil { + fmt.Printf("Failed to configure logging: %v\n", err) + return + } + + // Log platform information. + log.Printf("Running on %v", platform.GetOSInfo()) + + // Set CNS options. + httpRestService.SetOption(common.OptAPIServerURL, url) + + // Start CNS. + if httpRestService != nil { + err = httpRestService.Start(&config) + if err != nil { + fmt.Printf("Failed to start CNS, err:%v.\n", err) + return + } + } + + // Relay these incoming signals to OS signal channel. + osSignalChannel := make(chan os.Signal, 1) + signal.Notify(osSignalChannel, os.Interrupt, os.Kill, syscall.SIGTERM) + + // Wait until receiving a signal. + select { + case sig := <-osSignalChannel: + log.Printf("CNS Received OS signal <" + sig.String() + ">, shutting down.") + case err := <-config.ErrChan: + log.Printf("CNS Received unhandled error %v, shutting down.", err) + } + + // Cleanup. + if httpRestService != nil { + httpRestService.Stop() + } +} diff --git a/cns/restserver/httprest.go b/cns/restserver/httprest.go new file mode 100644 index 0000000000..7c6533172a --- /dev/null +++ b/cns/restserver/httprest.go @@ -0,0 +1,266 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package restserver + +import ( + "net/http" + + "github.com/Azure/azure-container-networking/cns" + "github.com/Azure/azure-container-networking/common" + "github.com/Azure/azure-container-networking/log" +) + +// httpRestService represents http listener for CNS - Container Networking Service. +type httpRestService struct { + *cns.Service +} + +// HTTPService describes the min API interface that every service should have. +type HTTPService interface { + common.ServiceAPI +} + +// NewHTTPRestService creates a new HTTP Service object. +func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { + service, err := cns.NewService(config.Name, config.Version) + if err != nil { + return nil, err + } + + return &httpRestService{ + Service: service, + }, nil +} + +// Start starts the CNS listener. +func (service *httpRestService) Start(config *common.ServiceConfig) error { + + err := service.Initialize(config) + if err != nil { + log.Printf("[Azure CNS] Failed to initialize base service, err:%v.", err) + return err + } + + // Add handlers. + listener := service.Listener + listener.AddHandler(cns.SetEnvironmentPath, service.setEnvironment) + listener.AddHandler(cns.CreateNetworkPath, service.createNetwork) + listener.AddHandler(cns.DeleteNetworkPath, service.deleteNetwork) + listener.AddHandler(cns.ReserveIPAddressPath, service.reserveIPAddress) + listener.AddHandler(cns.ReleaseIPAddressPath, service.releaseIPAddress) + listener.AddHandler(cns.GetHostLocalIPPath, service.getHostLocalIP) + listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) + listener.AddHandler(cns.GetAvailableIPAddressesPath, service.getAvailableIPAddresses) + listener.AddHandler(cns.GetReservedIPAddressesPath, service.getReservedIPAddresses) + listener.AddHandler(cns.GetGhostIPAddressesPath, service.getGhostIPAddresses) + listener.AddHandler(cns.GetAllIPAddressesPath, service.getAllIPAddresses) + listener.AddHandler(cns.GetHealthReportPath, service.getHealthReport) + + log.Printf("[Azure CNS] Listening.") + + return nil +} + +// Stop stops the CNS. +func (service *httpRestService) Stop() { + service.Uninitialize() + log.Printf("[Azure CNS] Service stopped.") +} + +// Handles requests to set the environment type. +func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Request) { + var req cns.SetEnvironmentRequest + err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { + return + } + + switch r.Method { + case "GET": + log.Printf("[Azure CNS] GET received for SetEnvironment.") + case "POST": + log.Printf("[Azure CNS] POST received for SetEnvironment.") + default: + } + + resp := &cns.Response{ReturnCode: 0} + err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) +} + +// Handles CreateNetwork requests. +func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Request) { + var req cns.CreateNetworkRequest + + err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { + return + } + switch r.Method { + case "POST": + default: + } + + resp := &cns.Response{ReturnCode: 0} + err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) +} + +// Handles DeleteNetwork requests. +func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Request) { + var req cns.DeleteNetworkRequest + + err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { + return + } + switch r.Method { + case "POST": + default: + } + + resp := &cns.Response{ReturnCode: 0} + err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) +} + +// Handles ip reservation requests. +func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http.Request) { + var req cns.ReserveIPAddressRequest + + err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { + return + } + switch r.Method { + case "POST": + default: + } + + resp := cns.Response{ReturnCode: 0} + reserveResp := &cns.ReserveIPAddressResponse{Response: resp, IPAddress: "0.0.0.0"} + err = service.Listener.Encode(w, &reserveResp) + log.Response(service.Name, reserveResp, err) +} + +// Handles release ip reservation requests. +func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http.Request) { + var req cns.ReleaseIPAddressRequest + + err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { + return + } + switch r.Method { + case "POST": + default: + } + + resp := &cns.Response{ReturnCode: 0} + err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) +} + +// Retrieves the host local ip address. +func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getHostLocalIP", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + hostLocalIPResponse := &cns.HostLocalIPAddressResponse{ + Response: resp, + IPAddress: "0.0.0.0", + } + err := service.Listener.Encode(w, &hostLocalIPResponse) + log.Response(service.Name, hostLocalIPResponse, err) +} + +// Handles ip address utiliztion requests. +func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getIPAddressUtilization", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + utilResponse := &cns.IPAddressesUtilizationResponse{ + Response: resp, + Available: 0, + } + err := service.Listener.Encode(w, &utilResponse) + log.Response(service.Name, utilResponse, err) +} + +// Handles retrieval of ip addresses that are available to be reserved from ipam driver. +func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getAvailableIPAddresses", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + ipResp := &cns.GetIPAddressesResponse{Response: resp} + err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) +} + +// Handles retrieval of reserved ip addresses from ipam driver. +func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getReservedIPAddresses", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + ipResp := &cns.GetIPAddressesResponse{Response: resp} + err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) +} + +// Handles retrieval of ghost ip addresses from ipam driver. +func (service *httpRestService) getGhostIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getGhostIPAddresses", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + ipResp := &cns.GetIPAddressesResponse{Response: resp} + err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) +} + +// Handles retrieval of all ip addresses from ipam driver. +func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getAllIPAddresses", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + ipResp := &cns.GetIPAddressesResponse{Response: resp} + err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) +} + +// Handles health report requests. +func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getHealthReport", nil) + + switch r.Method { + case "GET": + default: + } + + resp := &cns.Response{ReturnCode: 0} + err := service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) +} diff --git a/cns/restserver/httprest_test.go b/cns/restserver/httprest_test.go new file mode 100644 index 0000000000..4a4486bc30 --- /dev/null +++ b/cns/restserver/httprest_test.go @@ -0,0 +1,290 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package restserver + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/Azure/azure-container-networking/common" + "github.com/Azure/azure-container-networking/cns" +) + +var service HTTPService +var mux *http.ServeMux + +// Wraps the test run with service setup and teardown. +func TestMain(m *testing.M) { + var config common.ServiceConfig + var err error + + // Create the service. + service, err = NewHTTPRestService(&config) + if err != nil { + fmt.Printf("Failed to create CNS object %v\n", err) + os.Exit(1) + } + + // Configure test mode. + service.(*httpRestService).Name = "cns-test-server" + + // Start the service. + err = service.Start(&config) + if err != nil { + fmt.Printf("Failed to start CNS %v\n", err) + os.Exit(2) + } + + // Get the internal http mux as test hook. + mux = service.(*httpRestService).Listener.GetMux() + + // Run tests. + exitCode := m.Run() + + // Cleanup. + service.Stop() + + os.Exit(exitCode) +} + +// Decodes service'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 %d", w.Code) + } + + if w.Body == nil { + return fmt.Errorf("Response body is empty") + } + + return json.NewDecoder(w.Body).Decode(&response) +} + +// Tests CreateNetwork functionality. +func TestCreateNetwork(t *testing.T) { + fmt.Println("Test: CreateNetwork") + + var body bytes.Buffer + var resp cns.Response + + info := &cns.CreateNetworkRequest{ + NetworkName: "azurenet", + } + + json.NewEncoder(&body).Encode(info) + + req, err := http.NewRequest(http.MethodGet, cns.CreateNetworkPath, &body) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + err = decodeResponse(w, &resp) + + if err != nil || resp.ReturnCode != 0 { + t.Errorf("CreateNetwork response is invalid %+v", resp) + } else { + fmt.Printf ("CreateNetwork Responded with %+v\n", resp); + } +} + +func TestSetEnvironment(t *testing.T) { + fmt.Println("Test: SetEnvironment") + + var resp cns.Response + envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} + envRequestJSON := new(bytes.Buffer) + json.NewEncoder(envRequestJSON).Encode(envRequest) + + req, err := http.NewRequest(http.MethodGet, cns.SetEnvironmentPath, envRequestJSON) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + err = decodeResponse(w, &resp) + + if err != nil || resp.ReturnCode != 0 { + t.Errorf("SetEnvironment response is invalid %+v", resp) + } else { + fmt.Printf ("SetEnvironment Responded with %+v\n", resp); + } +} + +func TestReserveIPAddress(t *testing.T){ + fmt.Println("Test: ReserveIPAddress") + + reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID:"ip01"} + reserveIPRequestJSON := new(bytes.Buffer) + json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) + + envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} + envRequestJSON := new(bytes.Buffer) + json.NewEncoder(envRequestJSON).Encode(envRequest) + + req, err := http.NewRequest(http.MethodGet, cns.SetEnvironmentPath, envRequestJSON) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var reserveIPAddressResponse cns.ReserveIPAddressResponse + err = decodeResponse(w, &reserveIPAddressResponse) + + if err != nil || reserveIPAddressResponse.Response.ReturnCode != 0 { + t.Errorf("SetEnvironment response is invalid %+v", reserveIPAddressResponse) + } else { + fmt.Printf ("SetEnvironment Responded with %+v\n", reserveIPAddressResponse); + } +} + +func TestReleaseIPAddress(t *testing.T){ + fmt.Println("Test: ReleaseIPAddress") + releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID:"ip01"} + releaseIPAddressRequestJSON := new(bytes.Buffer) + json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) + + req, err := http.NewRequest(http.MethodGet, cns.ReleaseIPAddressPath, releaseIPAddressRequestJSON) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var releaseIPAddressResponse cns.Response + err = decodeResponse(w, &releaseIPAddressResponse) + + if err != nil || releaseIPAddressResponse.ReturnCode != 0 { + t.Errorf("SetEnvironment response is invalid %+v", releaseIPAddressResponse) + } else { + fmt.Printf ("SetEnvironment Responded with %+v\n", releaseIPAddressResponse); + } +} +func TestGetIPAddressUtilization(t *testing.T){ + fmt.Println("Test: GetIPAddressUtilization") + + req, err := http.NewRequest(http.MethodGet, cns.GetIPAddressUtilizationPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var iPAddressesUtilizationResponse cns.IPAddressesUtilizationResponse + err = decodeResponse(w, &iPAddressesUtilizationResponse) + + if err != nil || iPAddressesUtilizationResponse.Response.ReturnCode != 0 { + t.Errorf("GetIPAddressUtilization response is invalid %+v", iPAddressesUtilizationResponse) + } else { + fmt.Printf ("GetIPAddressUtilization Responded with %+v\n", iPAddressesUtilizationResponse); + } +} + +func TestGetHostLocalIP(t *testing.T){ + fmt.Println("Test: GetHostLocalIP") + req, err := http.NewRequest(http.MethodGet, cns.GetHostLocalIPPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var hostLocalIPAddressResponse cns.HostLocalIPAddressResponse + err = decodeResponse(w, &hostLocalIPAddressResponse) + + if err != nil || hostLocalIPAddressResponse.Response.ReturnCode != 0 { + t.Errorf("GetHostLocalIP response is invalid %+v", hostLocalIPAddressResponse) + } else { + fmt.Printf ("GetHostLocalIP Responded with %+v\n", hostLocalIPAddressResponse); + } +} + +func TestGetAvailableIPAddresses(t *testing.T){ + fmt.Println("Test: GetAvailableIPAddresses") + req, err := http.NewRequest(http.MethodGet, cns.GetAvailableIPAddressesPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var getIPAddressesResponse cns.GetIPAddressesResponse + err = decodeResponse(w, &getIPAddressesResponse) + + if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { + t.Errorf("GetAvailableIPAddresses response is invalid %+v", getIPAddressesResponse) + } else { + fmt.Printf ("GetAvailableIPAddresses Responded with %+v\n", getIPAddressesResponse); + } +} + +func TestGetReservedIPAddresses(t *testing.T){ + fmt.Println("Test: GetReservedIPAddresses") + req, err := http.NewRequest(http.MethodGet, cns.GetReservedIPAddressesPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var getIPAddressesResponse cns.GetIPAddressesResponse + err = decodeResponse(w, &getIPAddressesResponse) + + if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { + t.Errorf("GetReservedIPAddresses response is invalid %+v", getIPAddressesResponse) + } else { + fmt.Printf ("GetReservedIPAddresses Responded with %+v\n", getIPAddressesResponse); + } +} + +func TestGetGhostIPAddresses(t *testing.T){ + fmt.Println("Test: GetGhostIPAddresses") + req, err := http.NewRequest(http.MethodGet, cns.GetGhostIPAddressesPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var getIPAddressesResponse cns.GetIPAddressesResponse + err = decodeResponse(w, &getIPAddressesResponse) + + if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { + t.Errorf("GetGhostIPAddresses response is invalid %+v", getIPAddressesResponse) + } else { + fmt.Printf ("GetGhostIPAddresses Responded with %+v\n", getIPAddressesResponse); + } +} + +func TestGetAllIPAddresses(t *testing.T){ + fmt.Println("Test: GetAllIPAddresses") + req, err := http.NewRequest(http.MethodGet, cns.GetAllIPAddressesPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var getIPAddressesResponse cns.GetIPAddressesResponse + err = decodeResponse(w, &getIPAddressesResponse) + + if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { + t.Errorf("GetAllIPAddresses response is invalid %+v", getIPAddressesResponse) + } else { + fmt.Printf ("GetAllIPAddresses Responded with %+v\n", getIPAddressesResponse); + } +} + diff --git a/cns/service.go b/cns/service.go new file mode 100644 index 0000000000..cd22fcc163 --- /dev/null +++ b/cns/service.go @@ -0,0 +1,103 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package cns + +import ( + "net/http" + "net/url" + + "github.com/Azure/azure-container-networking/common" + "github.com/Azure/azure-container-networking/log" +) + +const ( + // Default CNS server URL. + defaultAPIServerURL = "tcp://localhost:10090" + genericData = "com.microsoft.azure.network.generic" +) + +// Service defines Container Networking Service. +type Service struct { + *common.Service + EndpointType string + Listener *common.Listener +} + +// NewService creates a new Service object. +func NewService(name, version string) (*Service, error) { + service, err := common.NewService(name, version) + + if err != nil { + return nil, err + } + + return &Service{ + Service: service, + }, nil +} + +// GetAPIServerURL returns the API server URL. +func (service *Service) getAPIServerURL() string { + urls, _ := service.GetOption(common.OptAPIServerURL).(string) + if urls == "" { + urls = defaultAPIServerURL + } + + return urls +} + +// Initialize initializes the service and starts the listener. +func (service *Service) Initialize(config *common.ServiceConfig) error { + log.Debugf("[Azure CNS] Going to initialize a service with config: %+v", config) + + // Initialize the base service. + service.Service.Initialize(config) + + // Initialize the listener. + if config.Listener == nil { + // Fetch and parse the API server URL. + u, err := url.Parse(service.getAPIServerURL()) + if err != nil { + return err + } + + // Create the listener. + listener, err := common.NewListener(u) + if err != nil { + return err + } + + // Start the listener. + err = listener.Start(config.ErrChan) + if err != nil { + return err + } + + config.Listener = listener + } + + service.Listener = config.Listener + + log.Debugf("[Azure CNS] Successfully initialized a service with config: %+v", config) + return nil +} + +// Uninitialize cleans up the plugin. +func (service *Service) Uninitialize() { + service.Listener.Stop() + service.Service.Uninitialize() +} + +// ParseOptions returns generic options from a libnetwork request. +func (service *Service) ParseOptions(options OptionMap) OptionMap { + opt, _ := options[genericData].(OptionMap) + return opt +} + +// SendErrorResponse sends and logs an error response. +func (service *Service) SendErrorResponse(w http.ResponseWriter, errMsg error) { + resp := errorResponse{errMsg.Error()} + err := service.Listener.Encode(w, &resp) + log.Response(service.Name, &resp, err) +} diff --git a/common/service.go b/common/service.go new file mode 100644 index 0000000000..179617993c --- /dev/null +++ b/common/service.go @@ -0,0 +1,79 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package common + +import ( + "errors" + "github.com/Azure/azure-container-networking/store" + "github.com/Azure/azure-container-networking/log" +) + +// Service implements behavior common to all services. +type Service struct { + Name string + Version string + Options map[string]interface{} + ErrChan chan error + Store store.KeyValueStore +} + +// ServiceAPI defines base interface. +type ServiceAPI interface { + Start(*ServiceConfig) error + Stop() + GetOption(string) interface{} + SetOption(string, interface{}) +} + +// ServiceConfig specifies common configuration. +type ServiceConfig struct { + Name string + Version string + Listener *Listener + ErrChan chan error + Store store.KeyValueStore +} + +// NewService creates a new Service object. +func NewService(name, version string) (*Service, error) { + + log.Debugf("[Azure CNS] Going to create a service object with name: %v. version: %v.", name, version) + svc := &Service{ + Name: name, + Version: version, + Options: make(map[string]interface{}), + } + log.Debugf("[Azure CNS] Finished creating service object with name: %v. version: %v.", name, version) + return svc, nil +} + +// Initialize initializes the service. +func (service *Service) Initialize(config *ServiceConfig) error { + if(config == nil){ + err := "[Azure CNS Errror] Initialize called with nil ServiceConfig." + log.Printf(err) + return errors.New(err) + } + + log.Debugf("[Azure CNS] Going to initialize the service: %+v with config: %+v.", service, config) + service.ErrChan = config.ErrChan + service.Store = config.Store + service.Version = config.Version + log.Debugf("[Azure CNS] nitialized service: %+v with config: %+v.", service, config) + return nil +} + +// Uninitialize cleans up the service. +func (service *Service) Uninitialize() { +} + +// GetOption gets the option value for the given key. +func (service *Service) GetOption(key string) interface{} { + return service.Options[key] +} + +// SetOption sets the option value for the given key. +func (service *Service) SetOption(key string, value interface{}) { + service.Options[key] = value +} From 4d2cba369f8ffc58086f4817dd9e3d4739560b6a Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Wed, 7 Jun 2017 15:26:28 -0700 Subject: [PATCH 02/34] Integrate CNS with Docker --- cns/client/examples.go | 25 +++ cns/dockerclient/api.go | 36 ++++ cns/dockerclient/dockerclient.go | 146 +++++++++++++++++ cns/dockerclient/dockerclient_linux.go | 10 ++ cns/dockerclient/dockerclient_windows.go | 10 ++ cns/imdsclient/api.go | 47 ++++++ cns/imdsclient/imdsclient.go | 67 ++++++++ cns/main/main.go | 18 +- cns/restserver/api.go | 18 ++ cns/restserver/{httprest.go => restserver.go} | 155 ++++++++++++++++-- .../{httprest_test.go => restserver_test.go} | 32 +++- cns/service.go | 5 +- common/service.go | 30 ++-- 13 files changed, 559 insertions(+), 40 deletions(-) create mode 100644 cns/dockerclient/api.go create mode 100644 cns/dockerclient/dockerclient.go create mode 100644 cns/dockerclient/dockerclient_linux.go create mode 100644 cns/dockerclient/dockerclient_windows.go create mode 100644 cns/imdsclient/api.go create mode 100644 cns/imdsclient/imdsclient.go create mode 100644 cns/restserver/api.go rename cns/restserver/{httprest.go => restserver.go} (61%) rename cns/restserver/{httprest_test.go => restserver_test.go} (91%) diff --git a/cns/client/examples.go b/cns/client/examples.go index e7d57c9807..e1072ead63 100644 --- a/cns/client/examples.go +++ b/cns/client/examples.go @@ -63,6 +63,30 @@ func createNetwork() error{ return nil } +func deleteNetwork() error{ + netRequest := cns.DeleteNetworkRequest{NetworkName:"azurenet"} + netRequestJSON := new(bytes.Buffer) + json.NewEncoder(netRequestJSON).Encode(netRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.DeleteNetworkPath, + "application/json; charset=utf-8", + netRequestJSON) + if err != nil { + fmt.Printf("Error received in DeleteNetwork post: %v ", err.Error()) + return err + } + + var deleteNetworkResponse cns.Response + err = json.NewDecoder(res.Body).Decode(&deleteNetworkResponse) + if err != nil{ + fmt.Printf("Error received in decoding response from DeleteNetwork: %v ", err.Error()) + return err + } + fmt.Printf("Response for DeleteNetwork: %+v\n", deleteNetworkResponse) + return nil +} + func reserveIPAddress() error{ reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID:"ip01"} reserveIPRequestJSON := new(bytes.Buffer) @@ -214,6 +238,7 @@ func getAllIPAddresses() error{ func main() { setEnvironment() createNetwork() + deleteNetwork() reserveIPAddress() releaseIPAddress() getAvailableIPAddresses() diff --git a/cns/dockerclient/api.go b/cns/dockerclient/api.go new file mode 100644 index 0000000000..06a1be9303 --- /dev/null +++ b/cns/dockerclient/api.go @@ -0,0 +1,36 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package dockerclient + +import ( +) + +const ( + createNetworkPath = "/Networks/Create" + inspectNetworkPath = "/Networks/" +) + +// Config describes subnet/gateway for ipam +type Config struct { + Subnet string + Gateway string +} + +// IPAM describes ipam details +type IPAM struct { + Driver string + Config *Config + Options map[string] string +} + +// NetworkConfiguration describes configuration for docker entwork create +type NetworkConfiguration struct +{ + Name string + Driver string + IPAM *IPAM + Internal bool + Options map[string]string + Labels map[string]string +} \ No newline at end of file diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go new file mode 100644 index 0000000000..48072cb45f --- /dev/null +++ b/cns/dockerclient/dockerclient.go @@ -0,0 +1,146 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package dockerclient + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + + "github.com/Azure/azure-container-networking/cns/imdsclient" + "github.com/Azure/azure-container-networking/log" +) + +const ( + defaultDockerConnectionURL = "http://127.0.0.1:2375" + defaultIpamPlugin = "azure-vnet-ipam" +) + +// DockerClient specifies a client to connect to docker +type DockerClient struct { + connectionURL string + imdsClient *imdsclient.ImdsClient +} + +// NewDockerClient create a new docker client +func NewDockerClient(url string) (*DockerClient, error) { + return &DockerClient{ + connectionURL: url, + imdsClient: &imdsclient.ImdsClient{}, + }, nil +} + +// NewDefaultDockerClient create a new docker client +func NewDefaultDockerClient() (*DockerClient, error) { + return &DockerClient{ + connectionURL: defaultDockerConnectionURL, + imdsClient: &imdsclient.ImdsClient{}, + }, nil +} + +// NetworkExists tries to retrieve a network from docker (if it exists) +func (dockerClient *DockerClient) NetworkExists(networkName string) error { + log.Printf("[Azure CNS] NetworkExists") + + res, err := http.Post( + dockerClient.connectionURL+inspectNetworkPath+networkName, + "application/json; charset=utf-8", + nil) + + if err != nil { + log.Printf("[Azure CNS] Error received from http Post for docker network inspect %v %v", networkName, err.Error()) + return err + } + + // network exists + if res.StatusCode == 200 { + return nil + } + + // network not found + if res.StatusCode == 404 { + return fmt.Errorf("Network not found") + } + + return fmt.Errorf("Unknown return code from docker inspect %d", res.StatusCode) +} + +// CreateNetwork creates a network using docker network create +func (dockerClient *DockerClient) CreateNetwork(networkName string) error { + log.Printf("[Azure CNS] CreateNetwork") + + primaryNic, err := dockerClient.imdsClient.GetPrimaryInterfaceInfoFromHost() + if err != nil { + return err + } + + config := &Config{ + Subnet: primaryNic.Subnet, + Gateway: primaryNic.Gateway, + } + ipamConfig := &IPAM{ + Driver: defaultIpamPlugin, + Config: config, + } + netConfig := &NetworkConfiguration{ + Name: networkName, + Driver: defaultNetworkPlugin, + IPAM: ipamConfig, + Internal: true, + } + + log.Printf("[Azure CNS] Going to create network with config: %+v", netConfig) + + netConfigJSON := new(bytes.Buffer) + err = json.NewEncoder(netConfigJSON).Encode(netConfig) + if err != nil { + return err + } + + res, err := http.Post( + dockerClient.connectionURL+createNetworkPath, + "application/json; charset=utf-8", + netConfigJSON) + + if err != nil { + log.Printf("[Azure CNS] Error received from http Post for docker network create %v", networkName) + return err + } + if res.StatusCode != 200 { + return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v", res.StatusCode) + } + + return nil +} + +// DeleteNetwork creates a network using docker network create +func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { + log.Printf("[Azure CNS] DeleteNetwork") + + url := dockerClient.connectionURL+inspectNetworkPath+networkName + req, err := http.NewRequest( + "DELETE", + url, + nil) + if err != nil { + log.Printf("[Azure CNS] Error received while creating http DELETE request for network delete %v %v", networkName, err.Error()) + return err + } + req.Header.Set("Content-Type", "application/json; charset=utf-8") + client := &http.Client{} + res, err := client.Do(req) + + // network successfully deleted + if res.StatusCode == 204 { + return nil + } + + // network not found + if res.StatusCode == 404 { + return fmt.Errorf("[Azure CNS] Network not found %v", networkName) + } + + return fmt.Errorf("[Azure CNS] Unknown return code from docker delete network %v: ret = %d", networkName, res.StatusCode) +} diff --git a/cns/dockerclient/dockerclient_linux.go b/cns/dockerclient/dockerclient_linux.go new file mode 100644 index 0000000000..18eadf5eee --- /dev/null +++ b/cns/dockerclient/dockerclient_linux.go @@ -0,0 +1,10 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +// +build linux + +package dockerclient + +const ( + defaultNetworkPlugin = "azure-vnet" +) diff --git a/cns/dockerclient/dockerclient_windows.go b/cns/dockerclient/dockerclient_windows.go new file mode 100644 index 0000000000..94ac90796d --- /dev/null +++ b/cns/dockerclient/dockerclient_windows.go @@ -0,0 +1,10 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +// +build windows + +package dockerclient + +const ( + defaultNetworkPlugin = "l2tunnel" +) diff --git a/cns/imdsclient/api.go b/cns/imdsclient/api.go new file mode 100644 index 0000000000..2b7d9b8c5a --- /dev/null +++ b/cns/imdsclient/api.go @@ -0,0 +1,47 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package imdsclient + +import ( + "encoding/xml" +) + +const ( + hostQueryURL = "http://169.254.169.254/machine/plugins?comp=nmagent&type=getinterfaceinfov1" +) + +// ImdsClient cna be used to connect to VM Host agent in Azure +type ImdsClient struct { + primaryInterface *InterfaceInfo +} + +// InterfaceInfo specifies the information about an interface as retunred by Host Agent +type InterfaceInfo struct { + Subnet string + Gateway string + IsPrimary bool + PrimaryIP string + SecondaryIPs[] string +} + +// Azure host agent XML document format. +type xmlDocument struct { + XMLName xml.Name `xml:"Interfaces"` + Interface []struct { + XMLName xml.Name `xml:"Interface"` + MacAddress string `xml:"MacAddress,attr"` + IsPrimary bool `xml:"IsPrimary,attr"` + + IPSubnet []struct { + XMLName xml.Name `xml:"IPSubnet"` + Prefix string `xml:"Prefix,attr"` + + IPAddress []struct { + XMLName xml.Name `xml:"IPAddress"` + Address string `xml:"Address,attr"` + IsPrimary bool `xml:"IsPrimary,attr"` + } + } + } +} \ No newline at end of file diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go new file mode 100644 index 0000000000..07213dc055 --- /dev/null +++ b/cns/imdsclient/imdsclient.go @@ -0,0 +1,67 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package imdsclient + +import ( + "fmt" + "strings" + "encoding/xml" + "net/http" + + "github.com/Azure/azure-container-networking/log" +) + +// GetPrimaryInterfaceInfoFromHost retrieves subnet and gateway of primary NIC from Host +func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, error) { + log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromHost") + interfaceInfo := &InterfaceInfo{} + resp, err := http.Get(hostQueryURL) + if(err != nil){ + return nil, err + } + + log.Printf("[Azure CNS] Response received from NMAgent: %v", resp.Body) + + var doc xmlDocument + decoder := xml.NewDecoder(resp.Body) + err = decoder.Decode(&doc) + if err != nil { + return nil, err + } + + foundInterface := false + + // For each interface... + for _, i := range doc.Interface { + // Find primary Interface + if i.IsPrimary { + // Get the first subnet + for _, s := range i.IPSubnet { + interfaceInfo.Subnet = s.Prefix + malformedSubnetError := fmt.Errorf("Malformed subnet received from host %s", s.Prefix) + + st := strings.Split(s.Prefix, "/") + if(len(st) != 2){ + return nil, malformedSubnetError + } + + ip := strings.Split(st[0], ".") + if(len(ip) != 4){ + return nil, malformedSubnetError + } + + interfaceInfo.Gateway = fmt.Sprintf("%s.%s.%s.1", ip[0], ip[1], ip[2]) + foundInterface = true + break; + } + break; + } + } + var er error + er = nil + if (!foundInterface) { + er = fmt.Errorf("Unable to find primary NIC") + } + return interfaceInfo, er +} \ No newline at end of file diff --git a/cns/main/main.go b/cns/main/main.go index 7a0ebc79af..cb8c1cc277 100644 --- a/cns/main/main.go +++ b/cns/main/main.go @@ -92,22 +92,24 @@ func main() { config.Name = name // Create a channel to receive unhandled errors from CNS. - config.ErrChan = make(chan error, 1) + config.ErrChan = make(chan error, 1) - // Create CNS object. - httpRestService, err := restserver.NewHTTPRestService(&config) + // Create the key value store. + var err error + config.Store, err = store.NewJsonFileStore(platform.RuntimePath + name + ".json") if err != nil { - fmt.Printf("Failed to create CNS object, err:%v.\n", err) + fmt.Printf("Failed to create store: %v\n", err) return } - // Create the key value store. - config.Store, err = store.NewJsonFileStore(platform.RuntimePath + name + ".json") + // Create CNS object. + httpRestService, err := restserver.NewHTTPRestService(&config) if err != nil { - fmt.Printf("Failed to create store: %v\n", err) + fmt.Printf("Failed to create CNS object, err:%v.\n", err) return } + // Create logging provider. log.SetName(name) log.SetLevel(logLevel) @@ -119,7 +121,7 @@ func main() { // Log platform information. log.Printf("Running on %v", platform.GetOSInfo()) - + // Set CNS options. httpRestService.SetOption(common.OptAPIServerURL, url) diff --git a/cns/restserver/api.go b/cns/restserver/api.go new file mode 100644 index 0000000000..47869100c7 --- /dev/null +++ b/cns/restserver/api.go @@ -0,0 +1,18 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package restserver + +// Container Network Service remote API Contract +const ( + Success = 0 + UnsupportedNetworkType = 1 + InvalidParameter = 2 + UnsupportedEnvironment = 3 + UnreachableHost = 4 + ReservationNotFound = 5 + MalformedSubnet = 8 + UnreachableDockerDaemon = 9 + UnspecifiedNetworkName = 10 + UnexpectedError = 99 +) diff --git a/cns/restserver/httprest.go b/cns/restserver/restserver.go similarity index 61% rename from cns/restserver/httprest.go rename to cns/restserver/restserver.go index 7c6533172a..e651aa2685 100644 --- a/cns/restserver/httprest.go +++ b/cns/restserver/restserver.go @@ -4,16 +4,35 @@ package restserver import ( + "time" "net/http" "github.com/Azure/azure-container-networking/cns" + "github.com/Azure/azure-container-networking/cns/dockerclient" "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/store" + "fmt" +) + +const ( + // Key against which CNS state is persisted. + storeKey = "ContainerNetworkService" ) // httpRestService represents http listener for CNS - Container Networking Service. type httpRestService struct { *cns.Service + dockerClient *dockerclient.DockerClient + store store.KeyValueStore + state httpRestServiceState +} + +// httpRestServiceState contrains the state we would like to persist +type httpRestServiceState struct { + Location string + NetworkType string + TimeStamp time.Time } // HTTPService describes the min API interface that every service should have. @@ -23,13 +42,20 @@ type HTTPService interface { // NewHTTPRestService creates a new HTTP Service object. func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { - service, err := cns.NewService(config.Name, config.Version) + service, err := cns.NewService(config.Name, config.Version, config.Store) if err != nil { return nil, err } + dc, err := dockerclient.NewDefaultDockerClient() + if(err != nil){ + return nil, err + } + return &httpRestService{ Service: service, + store: service.Service.Store, + dockerClient: dc, }, nil } @@ -38,7 +64,7 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { err := service.Initialize(config) if err != nil { - log.Printf("[Azure CNS] Failed to initialize base service, err:%v.", err) + log.Printf("[Azure CNS] Failed to initialize base service, err:%v.", err) return err } @@ -57,19 +83,19 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { listener.AddHandler(cns.GetAllIPAddressesPath, service.getAllIPAddresses) listener.AddHandler(cns.GetHealthReportPath, service.getHealthReport) - log.Printf("[Azure CNS] Listening.") - + log.Printf("[Azure CNS] Listening.") return nil } // Stop stops the CNS. func (service *httpRestService) Stop() { service.Uninitialize() - log.Printf("[Azure CNS] Service stopped.") + log.Printf("[Azure CNS] Service stopped.") } // Handles requests to set the environment type. func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] setEnvironment") var req cns.SetEnvironmentRequest err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) @@ -78,10 +104,11 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re } switch r.Method { - case "GET": - log.Printf("[Azure CNS] GET received for SetEnvironment.") case "POST": - log.Printf("[Azure CNS] POST received for SetEnvironment.") + log.Printf("[Azure CNS] POST received for SetEnvironment.") + service.state.Location = req.Location + service.state.NetworkType = req.NetworkType + service.saveState() default: } @@ -92,8 +119,10 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re // Handles CreateNetwork requests. func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] createNetwork") var req cns.CreateNetworkRequest - + returnCode := 0 + returnMessage := "" err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) if err != nil { @@ -101,18 +130,40 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req } switch r.Method { case "POST": + dc := service.dockerClient + err := dc.NetworkExists(req.NetworkName) + + // Network does not exist + if(err != nil) { + log.Printf("[Azure CNS] Goign to create network with name %v", req.NetworkName) + err := dc.CreateNetwork(req.NetworkName) + if(err != nil) { + returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) + returnCode = UnexpectedError + } + } else { + log.Printf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) + } + default: + returnMessage = "[Azure CNS] Error. CreateNetwork did not receive a POST." + returnCode = InvalidParameter } - resp := &cns.Response{ReturnCode: 0} + resp := &cns.Response{ + ReturnCode: returnCode, + Message: returnMessage, + } err = service.Listener.Encode(w, &resp) log.Response(service.Name, resp, err) } // Handles DeleteNetwork requests. func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] deleteNetwork") var req cns.DeleteNetworkRequest - + returnCode := 0 + returnMessage := "" err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) if err != nil { @@ -120,16 +171,37 @@ func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Req } switch r.Method { case "POST": + dc := service.dockerClient + err := dc.NetworkExists(req.NetworkName) + + // Network does exist + if(err == nil) { + log.Printf("[Azure CNS] Goign to delete network with name %v", req.NetworkName) + err := dc.DeleteNetwork(req.NetworkName) + if(err != nil) { + returnMessage = fmt.Sprintf("[Azure CNS] Error. DeleteNetwork failed %v.", err.Error()) + returnCode = UnexpectedError + } + } else { + log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v", req.NetworkName) + } + default: + returnMessage = "[Azure CNS] Error. DeleteNetwork did not receive a POST." + returnCode = InvalidParameter } - resp := &cns.Response{ReturnCode: 0} + resp := &cns.Response{ + ReturnCode: returnCode, + Message: returnMessage, + } err = service.Listener.Encode(w, &resp) log.Response(service.Name, resp, err) } // Handles ip reservation requests. func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] reserveIPAddress") var req cns.ReserveIPAddressRequest err := service.Listener.Decode(w, r, &req) @@ -150,6 +222,7 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. // Handles release ip reservation requests. func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] releaseIPAddress") var req cns.ReleaseIPAddressRequest err := service.Listener.Decode(w, r, &req) @@ -169,6 +242,7 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. // Retrieves the host local ip address. func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getHostLocalIP") log.Request(service.Name, "getHostLocalIP", nil) switch r.Method { case "GET": @@ -185,6 +259,7 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re // Handles ip address utiliztion requests. func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getIPAddressUtilization") log.Request(service.Name, "getIPAddressUtilization", nil) switch r.Method { case "GET": @@ -201,6 +276,7 @@ func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r // Handles retrieval of ip addresses that are available to be reserved from ipam driver. func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getAvailableIPAddresses") log.Request(service.Name, "getAvailableIPAddresses", nil) switch r.Method { case "GET": @@ -214,6 +290,7 @@ func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r // Handles retrieval of reserved ip addresses from ipam driver. func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getReservedIPAddresses") log.Request(service.Name, "getReservedIPAddresses", nil) switch r.Method { case "GET": @@ -227,6 +304,7 @@ func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r // Handles retrieval of ghost ip addresses from ipam driver. func (service *httpRestService) getGhostIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getGhostIPAddresses") log.Request(service.Name, "getGhostIPAddresses", nil) switch r.Method { case "GET": @@ -238,8 +316,9 @@ func (service *httpRestService) getGhostIPAddresses(w http.ResponseWriter, r *ht log.Response(service.Name, ipResp, err) } -// Handles retrieval of all ip addresses from ipam driver. +// getAllIPAddresses retrieves all ip addresses from ipam driver. func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getAllIPAddresses") log.Request(service.Name, "getAllIPAddresses", nil) switch r.Method { case "GET": @@ -253,8 +332,8 @@ func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http // Handles health report requests. func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.Request) { - log.Request(service.Name, "getHealthReport", nil) - + log.Printf("[Azure CNS] getHealthReport") + log.Request(service.Name, "getHealthReport", nil) switch r.Method { case "GET": default: @@ -264,3 +343,49 @@ func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.R err := service.Listener.Encode(w, &resp) log.Response(service.Name, resp, err) } + +// saveState writes CNS state to persistent store. +func (service *httpRestService) saveState() error { + log.Printf("[Azure CNS] saveState") + // Skip if a store is not provided. + if service.store == nil { + log.Printf("[Azure CNS] store not initialized.") + return nil + } + + // Update time stamp. + service.state.TimeStamp = time.Now() + err := service.store.Write(storeKey, &service.state) + if err == nil { + log.Printf("[Azure CNS] State saved successfully.\n") + } else { + log.Printf("[Azure CNS] Failed to save state., err:%v\n", err) + } + return err +} + +// restoreState restores CNS state from persistent store. +func (service *httpRestService) restoreState() error { + log.Printf("[Azure CNS] restoreState") + // Skip if a store is not provided. + if service.store == nil { + log.Printf("[Azure CNS] store not initialized.") + return nil + } + + // Read any persisted state. + err := service.store.Read(storeKey, &service.state) + if err != nil { + if err == store.ErrKeyNotFound { + // Nothing to restore. + log.Printf("[Azure CNS] No state to restore.\n") + return nil + } + + log.Printf("[Azure CNS] Failed to restore state, err:%v\n", err) + return err + } + + log.Printf("[Azure CNS] Restored state, %+v\n", service.state) + return nil +} diff --git a/cns/restserver/httprest_test.go b/cns/restserver/restserver_test.go similarity index 91% rename from cns/restserver/httprest_test.go rename to cns/restserver/restserver_test.go index 4a4486bc30..74e05b7ce4 100644 --- a/cns/restserver/httprest_test.go +++ b/cns/restserver/restserver_test.go @@ -79,7 +79,7 @@ func TestCreateNetwork(t *testing.T) { json.NewEncoder(&body).Encode(info) - req, err := http.NewRequest(http.MethodGet, cns.CreateNetworkPath, &body) + req, err := http.NewRequest(http.MethodPost, cns.CreateNetworkPath, &body) if err != nil { t.Fatal(err) } @@ -96,6 +96,36 @@ func TestCreateNetwork(t *testing.T) { } } +// Tests CreateNetwork functionality. +func TestDeleteNetwork(t *testing.T) { + fmt.Println("Test: DeleteNetwork") + + var body bytes.Buffer + var resp cns.Response + + info := &cns.DeleteNetworkRequest{ + NetworkName: "azurenet", + } + + json.NewEncoder(&body).Encode(info) + + req, err := http.NewRequest(http.MethodPost, cns.DeleteNetworkPath, &body) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + err = decodeResponse(w, &resp) + + if err != nil || resp.ReturnCode != 0 { + t.Errorf("DeleteNetwork response is invalid %+v", resp) + } else { + fmt.Printf ("DeleteNetwork Responded with %+v\n", resp); + } +} + func TestSetEnvironment(t *testing.T) { fmt.Println("Test: SetEnvironment") diff --git a/cns/service.go b/cns/service.go index cd22fcc163..91639b09f0 100644 --- a/cns/service.go +++ b/cns/service.go @@ -9,6 +9,7 @@ import ( "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/store" ) const ( @@ -25,8 +26,8 @@ type Service struct { } // NewService creates a new Service object. -func NewService(name, version string) (*Service, error) { - service, err := common.NewService(name, version) +func NewService(name, version string, store store.KeyValueStore) (*Service, error) { + service, err := common.NewService(name, version, store) if err != nil { return nil, err diff --git a/common/service.go b/common/service.go index 179617993c..ff3cf10922 100644 --- a/common/service.go +++ b/common/service.go @@ -5,8 +5,9 @@ package common import ( "errors" - "github.com/Azure/azure-container-networking/store" + "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/store" ) // Service implements behavior common to all services. @@ -28,39 +29,40 @@ type ServiceAPI interface { // ServiceConfig specifies common configuration. type ServiceConfig struct { - Name string - Version string + Name string + Version string Listener *Listener ErrChan chan error Store store.KeyValueStore } // NewService creates a new Service object. -func NewService(name, version string) (*Service, error) { - - log.Debugf("[Azure CNS] Going to create a service object with name: %v. version: %v.", name, version) +func NewService(name, version string, store store.KeyValueStore) (*Service, error) { + + log.Debugf("[Azure CNS] Going to create a service object with name: %v. version: %v.", name, version) svc := &Service{ - Name: name, - Version: version, - Options: make(map[string]interface{}), - } + Name: name, + Version: version, + Options: make(map[string]interface{}), + Store: store, + } log.Debugf("[Azure CNS] Finished creating service object with name: %v. version: %v.", name, version) return svc, nil } // Initialize initializes the service. func (service *Service) Initialize(config *ServiceConfig) error { - if(config == nil){ + if config == nil { err := "[Azure CNS Errror] Initialize called with nil ServiceConfig." log.Printf(err) return errors.New(err) } - - log.Debugf("[Azure CNS] Going to initialize the service: %+v with config: %+v.", service, config) + + log.Debugf("[Azure CNS] Going to initialize the service: %+v with config: %+v.", service, config) service.ErrChan = config.ErrChan service.Store = config.Store service.Version = config.Version - log.Debugf("[Azure CNS] nitialized service: %+v with config: %+v.", service, config) + log.Debugf("[Azure CNS] nitialized service: %+v with config: %+v.", service, config) return nil } From 55fd07090be8d5512bb24c3804d81bc52284d02d Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Thu, 8 Jun 2017 10:45:31 -0700 Subject: [PATCH 03/34] Update docker API paths and IPAM config --- cns/client/examples.go | 4 +- cns/dockerclient/api.go | 26 ++++----- cns/dockerclient/dockerclient.go | 39 +++++++++----- cns/imdsclient/api.go | 4 +- cns/imdsclient/imdsclient.go | 8 +-- cns/restserver/api.go | 2 +- cns/restserver/restserver.go | 91 +++++++++++++++++++++----------- 7 files changed, 106 insertions(+), 68 deletions(-) diff --git a/cns/client/examples.go b/cns/client/examples.go index e1072ead63..be3f7ee561 100644 --- a/cns/client/examples.go +++ b/cns/client/examples.go @@ -238,7 +238,7 @@ func getAllIPAddresses() error{ func main() { setEnvironment() createNetwork() - deleteNetwork() + /*deleteNetwork() reserveIPAddress() releaseIPAddress() getAvailableIPAddresses() @@ -246,5 +246,5 @@ func main() { getGhostIPAddresses() getAllIPAddresses() getIPAddressUtilization() - getHostLocalIP() + getHostLocalIP()*/ } diff --git a/cns/dockerclient/api.go b/cns/dockerclient/api.go index 06a1be9303..4cb8acb7e7 100644 --- a/cns/dockerclient/api.go +++ b/cns/dockerclient/api.go @@ -7,30 +7,30 @@ import ( ) const ( - createNetworkPath = "/Networks/Create" - inspectNetworkPath = "/Networks/" + createNetworkPath = "/networks/create" + inspectNetworkPath = "/networks/" ) -// Config describes subnet/gateway for ipam +// Config describes subnet/gateway for ipam. type Config struct { Subnet string - Gateway string } // IPAM describes ipam details type IPAM struct { Driver string - Config *Config - Options map[string] string + Config []Config } -// NetworkConfiguration describes configuration for docker entwork create -type NetworkConfiguration struct -{ +// NetworkConfiguration describes configuration for docker network create. +type NetworkConfiguration struct { Name string Driver string - IPAM *IPAM + IPAM IPAM Internal bool - Options map[string]string - Labels map[string]string -} \ No newline at end of file +} + +// DockerErrorResponse defines the error response retunred by docker. +type DockerErrorResponse struct { + message string +} diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go index 48072cb45f..5aed627118 100644 --- a/cns/dockerclient/dockerclient.go +++ b/cns/dockerclient/dockerclient.go @@ -15,16 +15,16 @@ import ( const ( defaultDockerConnectionURL = "http://127.0.0.1:2375" - defaultIpamPlugin = "azure-vnet-ipam" + defaultIpamPlugin = "azure-vnet" ) -// DockerClient specifies a client to connect to docker +// DockerClient specifies a client to connect to docker. type DockerClient struct { connectionURL string imdsClient *imdsclient.ImdsClient } -// NewDockerClient create a new docker client +// NewDockerClient create a new docker client. func NewDockerClient(url string) (*DockerClient, error) { return &DockerClient{ connectionURL: url, @@ -32,7 +32,7 @@ func NewDockerClient(url string) (*DockerClient, error) { }, nil } -// NewDefaultDockerClient create a new docker client +// NewDefaultDockerClient create a new docker client. func NewDefaultDockerClient() (*DockerClient, error) { return &DockerClient{ connectionURL: defaultDockerConnectionURL, @@ -40,7 +40,7 @@ func NewDefaultDockerClient() (*DockerClient, error) { }, nil } -// NetworkExists tries to retrieve a network from docker (if it exists) +// NetworkExists tries to retrieve a network from docker (if it exists). func (dockerClient *DockerClient) NetworkExists(networkName string) error { log.Printf("[Azure CNS] NetworkExists") @@ -56,18 +56,20 @@ func (dockerClient *DockerClient) NetworkExists(networkName string) error { // network exists if res.StatusCode == 200 { + log.Debugf("[Azure CNS] Network with name %v already exists. Docker return code: %v", networkName, res.StatusCode) return nil } // network not found if res.StatusCode == 404 { + log.Debugf("[Azure CNS] Network with name %v does not exist. Docker return code: %v", networkName, res.StatusCode) return fmt.Errorf("Network not found") } return fmt.Errorf("Unknown return code from docker inspect %d", res.StatusCode) } -// CreateNetwork creates a network using docker network create +// CreateNetwork creates a network using docker network create. func (dockerClient *DockerClient) CreateNetwork(networkName string) error { log.Printf("[Azure CNS] CreateNetwork") @@ -78,16 +80,17 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { config := &Config{ Subnet: primaryNic.Subnet, - Gateway: primaryNic.Gateway, } + configs := make([]Config, 1) + configs[0] = *config ipamConfig := &IPAM{ Driver: defaultIpamPlugin, - Config: config, + Config: configs, } netConfig := &NetworkConfiguration{ Name: networkName, Driver: defaultNetworkPlugin, - IPAM: ipamConfig, + IPAM: *ipamConfig, Internal: true, } @@ -108,14 +111,22 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { log.Printf("[Azure CNS] Error received from http Post for docker network create %v", networkName) return err } - if res.StatusCode != 200 { - return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v", res.StatusCode) + if res.StatusCode != 201 { + var createNetworkResponse DockerErrorResponse + err = json.NewDecoder(res.Body).Decode(&createNetworkResponse) + var ermsg string + ermsg = "" + if(err != nil){ + ermsg = err.Error() + } + return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v - %v - %v", res.StatusCode, createNetworkResponse.message, ermsg) + } return nil } -// DeleteNetwork creates a network using docker network create +// DeleteNetwork creates a network using docker network create. func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { log.Printf("[Azure CNS] DeleteNetwork") @@ -132,12 +143,12 @@ func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { client := &http.Client{} res, err := client.Do(req) - // network successfully deleted + // network successfully deleted. if res.StatusCode == 204 { return nil } - // network not found + // network not found. if res.StatusCode == 404 { return fmt.Errorf("[Azure CNS] Network not found %v", networkName) } diff --git a/cns/imdsclient/api.go b/cns/imdsclient/api.go index 2b7d9b8c5a..dec8d918fa 100644 --- a/cns/imdsclient/api.go +++ b/cns/imdsclient/api.go @@ -11,12 +11,12 @@ const ( hostQueryURL = "http://169.254.169.254/machine/plugins?comp=nmagent&type=getinterfaceinfov1" ) -// ImdsClient cna be used to connect to VM Host agent in Azure +// ImdsClient cna be used to connect to VM Host agent in Azure. type ImdsClient struct { primaryInterface *InterfaceInfo } -// InterfaceInfo specifies the information about an interface as retunred by Host Agent +// InterfaceInfo specifies the information about an interface as returned by Host Agent. type InterfaceInfo struct { Subnet string Gateway string diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go index 07213dc055..8351f82cf1 100644 --- a/cns/imdsclient/imdsclient.go +++ b/cns/imdsclient/imdsclient.go @@ -12,7 +12,7 @@ import ( "github.com/Azure/azure-container-networking/log" ) -// GetPrimaryInterfaceInfoFromHost retrieves subnet and gateway of primary NIC from Host +// GetPrimaryInterfaceInfoFromHost retrieves subnet and gateway of primary NIC from Host. func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, error) { log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromHost") interfaceInfo := &InterfaceInfo{} @@ -32,11 +32,11 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, foundInterface := false - // For each interface... + // For each interface. for _, i := range doc.Interface { - // Find primary Interface + // Find primary Interface. if i.IsPrimary { - // Get the first subnet + // Get the first subnet. for _, s := range i.IPSubnet { interfaceInfo.Subnet = s.Prefix malformedSubnetError := fmt.Errorf("Malformed subnet received from host %s", s.Prefix) diff --git a/cns/restserver/api.go b/cns/restserver/api.go index 47869100c7..8ef2e122d4 100644 --- a/cns/restserver/api.go +++ b/cns/restserver/api.go @@ -3,7 +3,7 @@ package restserver -// Container Network Service remote API Contract +// Container Network Service remote API Contract. const ( Success = 0 UnsupportedNetworkType = 1 diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index e651aa2685..458ee3de67 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -4,6 +4,7 @@ package restserver import ( + "fmt" "time" "net/http" @@ -12,7 +13,6 @@ import ( "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" - "fmt" ) const ( @@ -28,10 +28,11 @@ type httpRestService struct { state httpRestServiceState } -// httpRestServiceState contrains the state we would like to persist +// httpRestServiceState contains the state we would like to persist. type httpRestServiceState struct { - Location string - NetworkType string + Location string + NetworkType string + Initialized bool TimeStamp time.Time } @@ -107,7 +108,8 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re case "POST": log.Printf("[Azure CNS] POST received for SetEnvironment.") service.state.Location = req.Location - service.state.NetworkType = req.NetworkType + service.state.NetworkType = req.NetworkType + service.state.Initialized = true service.saveState() default: } @@ -120,40 +122,65 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re // Handles CreateNetwork requests. func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] createNetwork") - var req cns.CreateNetworkRequest + var err error returnCode := 0 returnMessage := "" - err := service.Listener.Decode(w, r, &req) - log.Request(service.Name, &req, err) - if err != nil { - return - } - switch r.Method { - case "POST": - dc := service.dockerClient - err := dc.NetworkExists(req.NetworkName) - - // Network does not exist - if(err != nil) { - log.Printf("[Azure CNS] Goign to create network with name %v", req.NetworkName) - err := dc.CreateNetwork(req.NetworkName) - if(err != nil) { - returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) - returnCode = UnexpectedError - } - } else { - log.Printf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) - } - - default: - returnMessage = "[Azure CNS] Error. CreateNetwork did not receive a POST." + + if(service.state.Initialized) { + + var req cns.CreateNetworkRequest + err = service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. Unable to decode input request.") returnCode = InvalidParameter + } else { + switch r.Method { + case "POST": + dc := service.dockerClient + err = dc.NetworkExists(req.NetworkName) + + // Network does not exist. + if(err != nil) { + switch service.state.NetworkType { + case "Underlay": + switch service.state.Location { + case "Azure": + log.Printf("[Azure CNS] Goign to create network with name %v.", req.NetworkName) + err = dc.CreateNetwork(req.NetworkName) + if(err != nil) { + returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) + returnCode = UnexpectedError + } + case "StandAlone": + returnMessage = fmt.Sprintf("[Azure CNS] Error. Underlay network is not supported in StandAlone environment. %v.", err.Error()) + returnCode = UnsupportedEnvironment + } + case "Overlay": + returnMessage = fmt.Sprintf("[Azure CNS] Error. Overlay support not yet available. %v.", err.Error()) + returnCode = UnsupportedEnvironment + } + } else { + log.Printf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) + } + + default: + returnMessage = "[Azure CNS] Error. CreateNetwork did not receive a POST." + returnCode = InvalidParameter + } + } + + } else { + returnMessage = fmt.Sprintf("[Azure CNS] Error. CNS is not yet initialized with environment.") + returnCode = UnsupportedEnvironment } resp := &cns.Response{ ReturnCode: returnCode, Message: returnMessage, } + err = service.Listener.Encode(w, &resp) log.Response(service.Name, resp, err) } @@ -176,14 +203,14 @@ func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Req // Network does exist if(err == nil) { - log.Printf("[Azure CNS] Goign to delete network with name %v", req.NetworkName) + log.Printf("[Azure CNS] Goign to delete network with name %v.", req.NetworkName) err := dc.DeleteNetwork(req.NetworkName) if(err != nil) { returnMessage = fmt.Sprintf("[Azure CNS] Error. DeleteNetwork failed %v.", err.Error()) returnCode = UnexpectedError } } else { - log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v", req.NetworkName) + log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v.", req.NetworkName) } default: From 260344f0835c3b8958153df8a54ed1ecf37fa4dd Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Thu, 8 Jun 2017 14:47:02 -0700 Subject: [PATCH 04/34] Implement GetHostLocalIp --- cns/api.go | 2 +- cns/client/examples.go | 4 +-- cns/dockerclient/dockerclient.go | 6 ++-- cns/imdsclient/imdsclient.go | 23 ++++++++++++--- cns/restserver/api.go | 1 + cns/restserver/restserver.go | 49 ++++++++++++++++++++++++------- cns/restserver/restserver_test.go | 24 +++++++-------- 7 files changed, 77 insertions(+), 32 deletions(-) diff --git a/cns/api.go b/cns/api.go index 13d545b982..43546b31ef 100644 --- a/cns/api.go +++ b/cns/api.go @@ -19,7 +19,7 @@ const ( GetHealthReportPath = "/Network/Health" ) -// SetEnvironmentRequest describes the Request to set the environment in HNS. +// SetEnvironmentRequest describes the Request to set the environment in CNS. type SetEnvironmentRequest struct { Location string NetworkType string diff --git a/cns/client/examples.go b/cns/client/examples.go index be3f7ee561..e1072ead63 100644 --- a/cns/client/examples.go +++ b/cns/client/examples.go @@ -238,7 +238,7 @@ func getAllIPAddresses() error{ func main() { setEnvironment() createNetwork() - /*deleteNetwork() + deleteNetwork() reserveIPAddress() releaseIPAddress() getAvailableIPAddresses() @@ -246,5 +246,5 @@ func main() { getGhostIPAddresses() getAllIPAddresses() getIPAddressUtilization() - getHostLocalIP()*/ + getHostLocalIP() } diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go index 5aed627118..5a2eccec47 100644 --- a/cns/dockerclient/dockerclient.go +++ b/cns/dockerclient/dockerclient.go @@ -33,10 +33,10 @@ func NewDockerClient(url string) (*DockerClient, error) { } // NewDefaultDockerClient create a new docker client. -func NewDefaultDockerClient() (*DockerClient, error) { +func NewDefaultDockerClient(imdsClient *imdsclient.ImdsClient) (*DockerClient, error) { return &DockerClient{ connectionURL: defaultDockerConnectionURL, - imdsClient: &imdsclient.ImdsClient{}, + imdsClient: imdsClient, }, nil } @@ -77,7 +77,7 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { if err != nil { return err } - + config := &Config{ Subnet: primaryNic.Subnet, } diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go index 8351f82cf1..fe1a0e42ae 100644 --- a/cns/imdsclient/imdsclient.go +++ b/cns/imdsclient/imdsclient.go @@ -30,12 +30,14 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, return nil, err } - foundInterface := false + foundPrimaryInterface := false // For each interface. for _, i := range doc.Interface { // Find primary Interface. if i.IsPrimary { + interfaceInfo.IsPrimary = true + // Get the first subnet. for _, s := range i.IPSubnet { interfaceInfo.Subnet = s.Prefix @@ -52,16 +54,29 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, } interfaceInfo.Gateway = fmt.Sprintf("%s.%s.%s.1", ip[0], ip[1], ip[2]) - foundInterface = true + for _,ip := range s.IPAddress { + if ip.IsPrimary == true { + interfaceInfo.PrimaryIP = ip.Address + } + } + imdsClient.primaryInterface = interfaceInfo break; } + + foundPrimaryInterface = true break; } } var er error er = nil - if (!foundInterface) { + if (!foundPrimaryInterface) { er = fmt.Errorf("Unable to find primary NIC") - } + } return interfaceInfo, er +} + +// GetPrimaryInterfaceInfoFromMemory retrieves subnet and gateway of primary NIC that is saved in memory. +func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromMemory() (*InterfaceInfo, error) { + log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromMemory") + return imdsClient.primaryInterface, nil } \ No newline at end of file diff --git a/cns/restserver/api.go b/cns/restserver/api.go index 8ef2e122d4..af669a7985 100644 --- a/cns/restserver/api.go +++ b/cns/restserver/api.go @@ -14,5 +14,6 @@ const ( MalformedSubnet = 8 UnreachableDockerDaemon = 9 UnspecifiedNetworkName = 10 + NotFound = 14 UnexpectedError = 99 ) diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 458ee3de67..7760ecb483 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -10,6 +10,7 @@ import ( "github.com/Azure/azure-container-networking/cns" "github.com/Azure/azure-container-networking/cns/dockerclient" + "github.com/Azure/azure-container-networking/cns/imdsclient" "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" @@ -24,6 +25,7 @@ const ( type httpRestService struct { *cns.Service dockerClient *dockerclient.DockerClient + imdsClient *imdsclient.ImdsClient store store.KeyValueStore state httpRestServiceState } @@ -47,8 +49,8 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { if err != nil { return nil, err } - - dc, err := dockerclient.NewDefaultDockerClient() + imdsClient := &imdsclient.ImdsClient{} + dc, err := dockerclient.NewDefaultDockerClient(imdsClient) if(err != nil){ return nil, err } @@ -57,6 +59,7 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { Service: service, store: service.Service.Store, dockerClient: dc, + imdsClient: imdsClient, }, nil } @@ -78,11 +81,13 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { listener.AddHandler(cns.ReleaseIPAddressPath, service.releaseIPAddress) listener.AddHandler(cns.GetHostLocalIPPath, service.getHostLocalIP) listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) - listener.AddHandler(cns.GetAvailableIPAddressesPath, service.getAvailableIPAddresses) - listener.AddHandler(cns.GetReservedIPAddressesPath, service.getReservedIPAddresses) listener.AddHandler(cns.GetGhostIPAddressesPath, service.getGhostIPAddresses) - listener.AddHandler(cns.GetAllIPAddressesPath, service.getAllIPAddresses) - listener.AddHandler(cns.GetHealthReportPath, service.getHealthReport) + + // Below APIs are not handled in this iteration. + // listener.AddHandler(cns.GetAvailableIPAddressesPath, service.getAvailableIPAddresses) + // listener.AddHandler(cns.GetReservedIPAddressesPath, service.getReservedIPAddresses) + // listener.AddHandler(cns.GetAllIPAddressesPath, service.getAllIPAddresses) + // listener.AddHandler(cns.GetHealthReportPath, service.getHealthReport) log.Printf("[Azure CNS] Listening.") return nil @@ -267,18 +272,42 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. log.Response(service.Name, resp, err) } -// Retrieves the host local ip address. +// Retrieves the host local ip address. Containers can talk to host using this IP address. func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Request) { + var found bool + var errmsg string log.Printf("[Azure CNS] getHostLocalIP") - log.Request(service.Name, "getHostLocalIP", nil) + log.Request(service.Name, "getHostLocalIP", nil) + hostLocalIP := "0.0.0.0" switch r.Method { case "GET": + switch (service.state.NetworkType) { + case "Underlay": + if (service.imdsClient != nil) { + piface, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() + if err != nil { + hostLocalIP = piface.PrimaryIP + found = true; + } else { + log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) + } + } + case "Overlay": + errmsg = "[Azure-CNS] Overlay is not yet supported." + } default: + errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." } - resp := cns.Response{ReturnCode: 0} + + returnCode := 0 + if !found { + returnCode = NotFound + } + + resp := cns.Response{ReturnCode: returnCode, Message: errmsg} hostLocalIPResponse := &cns.HostLocalIPAddressResponse{ Response: resp, - IPAddress: "0.0.0.0", + IPAddress: hostLocalIP, } err := service.Listener.Encode(w, &hostLocalIPResponse) log.Response(service.Name, hostLocalIPResponse, err) diff --git a/cns/restserver/restserver_test.go b/cns/restserver/restserver_test.go index 74e05b7ce4..805832b86a 100644 --- a/cns/restserver/restserver_test.go +++ b/cns/restserver/restserver_test.go @@ -90,7 +90,7 @@ func TestCreateNetwork(t *testing.T) { err = decodeResponse(w, &resp) if err != nil || resp.ReturnCode != 0 { - t.Errorf("CreateNetwork response is invalid %+v", resp) + t.Errorf("CreateNetwork failed with response %+v", resp) } else { fmt.Printf ("CreateNetwork Responded with %+v\n", resp); } @@ -120,7 +120,7 @@ func TestDeleteNetwork(t *testing.T) { err = decodeResponse(w, &resp) if err != nil || resp.ReturnCode != 0 { - t.Errorf("DeleteNetwork response is invalid %+v", resp) + t.Errorf("DeleteNetwork failed with response %+v", resp) } else { fmt.Printf ("DeleteNetwork Responded with %+v\n", resp); } @@ -145,7 +145,7 @@ func TestSetEnvironment(t *testing.T) { err = decodeResponse(w, &resp) if err != nil || resp.ReturnCode != 0 { - t.Errorf("SetEnvironment response is invalid %+v", resp) + t.Errorf("SetEnvironment failed with response %+v", resp) } else { fmt.Printf ("SetEnvironment Responded with %+v\n", resp); } @@ -162,7 +162,7 @@ func TestReserveIPAddress(t *testing.T){ envRequestJSON := new(bytes.Buffer) json.NewEncoder(envRequestJSON).Encode(envRequest) - req, err := http.NewRequest(http.MethodGet, cns.SetEnvironmentPath, envRequestJSON) + req, err := http.NewRequest(http.MethodGet, cns.ReserveIPAddressPath, envRequestJSON) if err != nil { t.Fatal(err) } @@ -174,7 +174,7 @@ func TestReserveIPAddress(t *testing.T){ err = decodeResponse(w, &reserveIPAddressResponse) if err != nil || reserveIPAddressResponse.Response.ReturnCode != 0 { - t.Errorf("SetEnvironment response is invalid %+v", reserveIPAddressResponse) + t.Errorf("SetEnvironment failed with response %+v", reserveIPAddressResponse) } else { fmt.Printf ("SetEnvironment Responded with %+v\n", reserveIPAddressResponse); } @@ -198,7 +198,7 @@ func TestReleaseIPAddress(t *testing.T){ err = decodeResponse(w, &releaseIPAddressResponse) if err != nil || releaseIPAddressResponse.ReturnCode != 0 { - t.Errorf("SetEnvironment response is invalid %+v", releaseIPAddressResponse) + t.Errorf("SetEnvironment failed with response %+v", releaseIPAddressResponse) } else { fmt.Printf ("SetEnvironment Responded with %+v\n", releaseIPAddressResponse); } @@ -217,7 +217,7 @@ func TestGetIPAddressUtilization(t *testing.T){ err = decodeResponse(w, &iPAddressesUtilizationResponse) if err != nil || iPAddressesUtilizationResponse.Response.ReturnCode != 0 { - t.Errorf("GetIPAddressUtilization response is invalid %+v", iPAddressesUtilizationResponse) + t.Errorf("GetIPAddressUtilization failed with response %+v", iPAddressesUtilizationResponse) } else { fmt.Printf ("GetIPAddressUtilization Responded with %+v\n", iPAddressesUtilizationResponse); } @@ -236,7 +236,7 @@ func TestGetHostLocalIP(t *testing.T){ err = decodeResponse(w, &hostLocalIPAddressResponse) if err != nil || hostLocalIPAddressResponse.Response.ReturnCode != 0 { - t.Errorf("GetHostLocalIP response is invalid %+v", hostLocalIPAddressResponse) + t.Errorf("GetHostLocalIP failed with response %+v", hostLocalIPAddressResponse) } else { fmt.Printf ("GetHostLocalIP Responded with %+v\n", hostLocalIPAddressResponse); } @@ -255,7 +255,7 @@ func TestGetAvailableIPAddresses(t *testing.T){ err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetAvailableIPAddresses response is invalid %+v", getIPAddressesResponse) + t.Errorf("GetAvailableIPAddresses failed with response %+v", getIPAddressesResponse) } else { fmt.Printf ("GetAvailableIPAddresses Responded with %+v\n", getIPAddressesResponse); } @@ -274,7 +274,7 @@ func TestGetReservedIPAddresses(t *testing.T){ err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetReservedIPAddresses response is invalid %+v", getIPAddressesResponse) + t.Errorf("GetReservedIPAddresses failed with response %+v", getIPAddressesResponse) } else { fmt.Printf ("GetReservedIPAddresses Responded with %+v\n", getIPAddressesResponse); } @@ -293,7 +293,7 @@ func TestGetGhostIPAddresses(t *testing.T){ err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetGhostIPAddresses response is invalid %+v", getIPAddressesResponse) + t.Errorf("GetGhostIPAddresses failed with response %+v", getIPAddressesResponse) } else { fmt.Printf ("GetGhostIPAddresses Responded with %+v\n", getIPAddressesResponse); } @@ -312,7 +312,7 @@ func TestGetAllIPAddresses(t *testing.T){ err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetAllIPAddresses response is invalid %+v", getIPAddressesResponse) + t.Errorf("GetAllIPAddresses failed with response %+v", getIPAddressesResponse) } else { fmt.Printf ("GetAllIPAddresses Responded with %+v\n", getIPAddressesResponse); } From 1cc085e59010631d93ae341a4784c53c9b28cf99 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Thu, 8 Jun 2017 16:04:58 -0700 Subject: [PATCH 05/34] Fix network inspect and GetHostLocal IP --- cns/dockerclient/dockerclient.go | 6 ++-- cns/{client => examples}/examples.go | 0 cns/imdsclient/imdsclient.go | 15 ++++++++- cns/restserver/restserver.go | 47 +++++++++++++++------------- 4 files changed, 42 insertions(+), 26 deletions(-) rename cns/{client => examples}/examples.go (100%) diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go index 5a2eccec47..90d7ddb71f 100644 --- a/cns/dockerclient/dockerclient.go +++ b/cns/dockerclient/dockerclient.go @@ -44,10 +44,8 @@ func NewDefaultDockerClient(imdsClient *imdsclient.ImdsClient) (*DockerClient, e func (dockerClient *DockerClient) NetworkExists(networkName string) error { log.Printf("[Azure CNS] NetworkExists") - res, err := http.Post( - dockerClient.connectionURL+inspectNetworkPath+networkName, - "application/json; charset=utf-8", - nil) + res, err := http.Get( + dockerClient.connectionURL+inspectNetworkPath+networkName) if err != nil { log.Printf("[Azure CNS] Error received from http Post for docker network inspect %v %v", networkName, err.Error()) diff --git a/cns/client/examples.go b/cns/examples/examples.go similarity index 100% rename from cns/client/examples.go rename to cns/examples/examples.go diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go index fe1a0e42ae..c8c68f5c3e 100644 --- a/cns/imdsclient/imdsclient.go +++ b/cns/imdsclient/imdsclient.go @@ -78,5 +78,18 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, // GetPrimaryInterfaceInfoFromMemory retrieves subnet and gateway of primary NIC that is saved in memory. func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromMemory() (*InterfaceInfo, error) { log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromMemory") - return imdsClient.primaryInterface, nil + var iface *InterfaceInfo + var err error + if(imdsClient.primaryInterface == nil) { + log.Debugf("Azure-CNS] Primary interface in memory does not exist. Will get it from Host.") + iface, err = imdsClient.GetPrimaryInterfaceInfoFromHost() + if err != nil { + log.Printf("[Azure-CNS] Unable to retrive primary interface info.") + } else { + log.Debugf("Azure-CNS] Primary interface received from HOST: %+v.", iface) + } + } else { + iface = imdsClient.primaryInterface + } + return iface, err } \ No newline at end of file diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 7760ecb483..6d151e64ea 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -54,7 +54,7 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { if(err != nil){ return nil, err } - + return &httpRestService{ Service: service, store: service.Service.Store, @@ -167,7 +167,8 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req returnCode = UnsupportedEnvironment } } else { - log.Printf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) + returnMessage = fmt.Sprintf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) + log.Printf(returnMessage) } default: @@ -279,29 +280,33 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re log.Printf("[Azure CNS] getHostLocalIP") log.Request(service.Name, "getHostLocalIP", nil) hostLocalIP := "0.0.0.0" - switch r.Method { - case "GET": - switch (service.state.NetworkType) { - case "Underlay": - if (service.imdsClient != nil) { - piface, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() - if err != nil { - hostLocalIP = piface.PrimaryIP - found = true; - } else { - log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) + if service.state.Initialized { + switch r.Method { + case "GET": + switch (service.state.NetworkType) { + case "Underlay": + if (service.imdsClient != nil) { + piface, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() + if err == nil { + hostLocalIP = piface.PrimaryIP + found = true; + } else { + log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) + } } - } - case "Overlay": - errmsg = "[Azure-CNS] Overlay is not yet supported." - } - default: - errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." + case "Overlay": + errmsg = "[Azure-CNS] Overlay is not yet supported." + } + default: + errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." + } } - returnCode := 0 if !found { returnCode = NotFound + if(errmsg == ""){ + errmsg = "[Azure-CNS] Unable to get host local ip. Check if environment is initialized.." + } } resp := cns.Response{ReturnCode: returnCode, Message: errmsg} @@ -313,7 +318,7 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re log.Response(service.Name, hostLocalIPResponse, err) } -// Handles ip address utiliztion requests. +// Handles ip address utilization requests. func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getIPAddressUtilization") log.Request(service.Name, "getIPAddressUtilization", nil) From 3637216c81f10cdf33570a01e5583e9a1eb8623c Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Fri, 9 Jun 2017 09:41:27 -0700 Subject: [PATCH 06/34] Remove unused handlers. Update tests. --- cns/api.go | 24 ++--- cns/examples/examples.go | 66 ++------------ cns/restserver/restserver.go | 21 ++--- cns/restserver/restserver_test.go | 146 ++++++++---------------------- 4 files changed, 65 insertions(+), 192 deletions(-) diff --git a/cns/api.go b/cns/api.go index 43546b31ef..6ce07574ac 100644 --- a/cns/api.go +++ b/cns/api.go @@ -5,18 +5,15 @@ package cns // Container Network Service remote API Contract const ( - SetEnvironmentPath = "/Network/Environment" - CreateNetworkPath = "/Network/Create" - DeleteNetworkPath = "/Network/Delete" - ReserveIPAddressPath = "/Network/IP/Reserve" - ReleaseIPAddressPath = "/Network/IP/Release" - GetHostLocalIPPath = "/Network/IP/HostLocal" - GetIPAddressUtilizationPath = "/Network/IP/Utilization" - GetAvailableIPAddressesPath = "/Network/IPAddresses/Available" - GetReservedIPAddressesPath = "/Network/IPAddresses/Reserved" - GetGhostIPAddressesPath = "/Network/IPAddresses/Ghost" - GetAllIPAddressesPath = "/Network/IPAddresses/All" - GetHealthReportPath = "/Network/Health" + SetEnvironmentPath = "/network/environment" + CreateNetworkPath = "/network/create" + DeleteNetworkPath = "/network/delete" + ReserveIPAddressPath = "/network/ip/Reserve" + ReleaseIPAddressPath = "/network/ip/Release" + GetHostLocalIPPath = "/network/ip/HostLocal" + GetIPAddressUtilizationPath = "/network/ip/Utilization" + GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy" + GetHealthReportPath = "/network/health" ) // SetEnvironmentRequest describes the Request to set the environment in CNS. @@ -65,7 +62,7 @@ type IPAddressesUtilizationResponse struct { Response Response Available int Reserved int - Ghost int + Unhealthy int } // GetIPAddressesResponse describes response containing requested ip addresses. @@ -84,7 +81,6 @@ type HostLocalIPAddressResponse struct { type IPAddress struct { IPAddress string ReservationID string - IsGhost bool } // Subnet contains the ip address and the number of bits in prefix. diff --git a/cns/examples/examples.go b/cns/examples/examples.go index e1072ead63..2f0f36edb3 100644 --- a/cns/examples/examples.go +++ b/cns/examples/examples.go @@ -167,71 +167,20 @@ func getHostLocalIP() error{ return nil } -func getAvailableIPAddresses() error{ +func getUnhealthyIPAddresses() error{ res, err := - http.Get(defaultCNSServerURL+cns.GetAvailableIPAddressesPath) + http.Get(defaultCNSServerURL+cns.GetUnhealthyIPAddressesPath) if err != nil { - fmt.Printf("Error received in GetAvailable IP Addresses: %v ", err.Error()) - return err - } - var getIPAddressesResponse cns.GetIPAddressesResponse - err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) - if err != nil { - fmt.Printf("Error received in decoding response from GetAvailableIPAddresses: %v ", err.Error()) - return err - } - fmt.Printf("Response for GetAvailableIPAddresses: %+v\n", getIPAddressesResponse) - return nil -} - -func getReservedIPAddresses() error{ - res, err := - http.Get(defaultCNSServerURL+cns.GetReservedIPAddressesPath) - if err != nil { - fmt.Printf("Error received in GetReserved IP Addresses: %v ", err.Error()) - return err - } - var getIPAddressesResponse cns.GetIPAddressesResponse - err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) - if err != nil { - fmt.Printf("Error received in decoding response from getReservedIPAddresses: %v ", err.Error()) - return err - } - fmt.Printf("Response for getReservedIPAddresses: %+v\n", getIPAddressesResponse) - return nil -} - -func getGhostIPAddresses() error{ - res, err := - http.Get(defaultCNSServerURL+cns.GetGhostIPAddressesPath) - if err != nil { - fmt.Printf("Error received in GetGhost IP Addresses: %v ", err.Error()) - return err - } - var getIPAddressesResponse cns.GetIPAddressesResponse - err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) - if err != nil { - fmt.Printf("Error received in decoding response from getGhostIPAddresses: %v ", err.Error()) - return err - } - fmt.Printf("Response for getGhostIPAddresses: %+v\n", getIPAddressesResponse) - return nil -} - -func getAllIPAddresses() error{ - res, err := - http.Get(defaultCNSServerURL+cns.GetAllIPAddressesPath) - if err != nil { - fmt.Printf("Error received in GetAll IP Addresses: %v ", err.Error()) + fmt.Printf("Error received in GetUnhealthyIPAddresses IP Addresses: %v ", err.Error()) return err } var getIPAddressesResponse cns.GetIPAddressesResponse err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) if err != nil { - fmt.Printf("Error received in decoding response from getAllIPAddresses: %v ", err.Error()) + fmt.Printf("Error received in decoding response from GetUnhealthyIPAddresses: %v ", err.Error()) return err } - fmt.Printf("Response for getAllIPAddresses: %+v\n", getIPAddressesResponse) + fmt.Printf("Response for GetUnhealthyIPAddresses: %+v\n", getIPAddressesResponse) return nil } @@ -241,10 +190,7 @@ func main() { deleteNetwork() reserveIPAddress() releaseIPAddress() - getAvailableIPAddresses() - getReservedIPAddresses() - getGhostIPAddresses() - getAllIPAddresses() + getUnhealthyIPAddresses() getIPAddressUtilization() getHostLocalIP() } diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 6d151e64ea..82c54ef0db 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -81,13 +81,7 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { listener.AddHandler(cns.ReleaseIPAddressPath, service.releaseIPAddress) listener.AddHandler(cns.GetHostLocalIPPath, service.getHostLocalIP) listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) - listener.AddHandler(cns.GetGhostIPAddressesPath, service.getGhostIPAddresses) - - // Below APIs are not handled in this iteration. - // listener.AddHandler(cns.GetAvailableIPAddressesPath, service.getAvailableIPAddresses) - // listener.AddHandler(cns.GetReservedIPAddressesPath, service.getReservedIPAddresses) - // listener.AddHandler(cns.GetAllIPAddressesPath, service.getAllIPAddresses) - // listener.AddHandler(cns.GetHealthReportPath, service.getHealthReport) + listener.AddHandler(cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) log.Printf("[Azure CNS] Listening.") return nil @@ -216,7 +210,12 @@ func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Req returnCode = UnexpectedError } } else { - log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v.", req.NetworkName) + if(err == fmt.Errorf("Network not found")){ + log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v.", req.NetworkName) + } else { + returnCode = UnexpectedError + returnMessage = err.Error() + } } default: @@ -364,9 +363,9 @@ func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r } // Handles retrieval of ghost ip addresses from ipam driver. -func (service *httpRestService) getGhostIPAddresses(w http.ResponseWriter, r *http.Request) { - log.Printf("[Azure CNS] getGhostIPAddresses") - log.Request(service.Name, "getGhostIPAddresses", nil) +func (service *httpRestService) getUnhealthyIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getUnhealthyIPAddresses") + log.Request(service.Name, "getUnhealthyIPAddresses", nil) switch r.Method { case "GET": default: diff --git a/cns/restserver/restserver_test.go b/cns/restserver/restserver_test.go index 805832b86a..7626cb4400 100644 --- a/cns/restserver/restserver_test.go +++ b/cns/restserver/restserver_test.go @@ -66,29 +66,48 @@ func decodeResponse(w *httptest.ResponseRecorder, response interface{}) error { return json.NewDecoder(w.Body).Decode(&response) } -// Tests CreateNetwork functionality. -func TestCreateNetwork(t *testing.T) { - fmt.Println("Test: CreateNetwork") +func setEnv(t *testing.T) (*httptest.ResponseRecorder) { + envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} + envRequestJSON := new(bytes.Buffer) + json.NewEncoder(envRequestJSON).Encode(envRequest) - var body bytes.Buffer + req, err := http.NewRequest(http.MethodPost, cns.SetEnvironmentPath, envRequestJSON) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + return w +} +func TestSetEnvironment(t *testing.T) { + fmt.Println("Test: SetEnvironment") var resp cns.Response + w := setEnv(t) + err := decodeResponse(w, &resp) + if err != nil || resp.ReturnCode != 0 { + t.Errorf("SetEnvironment failed with response %+v", resp) + } else { + fmt.Printf ("SetEnvironment Responded with %+v\n", resp); + } +} +// Tests CreateNetwork functionality. +func TestCreateNetwork(t *testing.T) { + fmt.Println("Test: CreateNetwork") + var body bytes.Buffer + setEnv(t) info := &cns.CreateNetworkRequest{ NetworkName: "azurenet", } - json.NewEncoder(&body).Encode(info) - req, err := http.NewRequest(http.MethodPost, cns.CreateNetworkPath, &body) if err != nil { t.Fatal(err) - } - + } w := httptest.NewRecorder() mux.ServeHTTP(w, req) - + var resp cns.Response err = decodeResponse(w, &resp) - if err != nil || resp.ReturnCode != 0 { t.Errorf("CreateNetwork failed with response %+v", resp) } else { @@ -96,29 +115,23 @@ func TestCreateNetwork(t *testing.T) { } } -// Tests CreateNetwork functionality. +// Tests DeleteNetwork functionality. func TestDeleteNetwork(t *testing.T) { - fmt.Println("Test: DeleteNetwork") - + fmt.Println("Test: DeleteNetwork") var body bytes.Buffer - var resp cns.Response - + setEnv(t) info := &cns.DeleteNetworkRequest{ NetworkName: "azurenet", } - json.NewEncoder(&body).Encode(info) - req, err := http.NewRequest(http.MethodPost, cns.DeleteNetworkPath, &body) if err != nil { t.Fatal(err) - } - + } w := httptest.NewRecorder() mux.ServeHTTP(w, req) - + var resp cns.Response err = decodeResponse(w, &resp) - if err != nil || resp.ReturnCode != 0 { t.Errorf("DeleteNetwork failed with response %+v", resp) } else { @@ -126,31 +139,6 @@ func TestDeleteNetwork(t *testing.T) { } } -func TestSetEnvironment(t *testing.T) { - fmt.Println("Test: SetEnvironment") - - var resp cns.Response - envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} - envRequestJSON := new(bytes.Buffer) - json.NewEncoder(envRequestJSON).Encode(envRequest) - - req, err := http.NewRequest(http.MethodGet, cns.SetEnvironmentPath, envRequestJSON) - if err != nil { - t.Fatal(err) - } - - w := httptest.NewRecorder() - mux.ServeHTTP(w, req) - - err = decodeResponse(w, &resp) - - if err != nil || resp.ReturnCode != 0 { - t.Errorf("SetEnvironment failed with response %+v", resp) - } else { - fmt.Printf ("SetEnvironment Responded with %+v\n", resp); - } -} - func TestReserveIPAddress(t *testing.T){ fmt.Println("Test: ReserveIPAddress") @@ -225,6 +213,7 @@ func TestGetIPAddressUtilization(t *testing.T){ func TestGetHostLocalIP(t *testing.T){ fmt.Println("Test: GetHostLocalIP") + setEnv(t) req, err := http.NewRequest(http.MethodGet, cns.GetHostLocalIPPath, nil) if err != nil { t.Fatal(err) @@ -242,66 +231,9 @@ func TestGetHostLocalIP(t *testing.T){ } } -func TestGetAvailableIPAddresses(t *testing.T){ - fmt.Println("Test: GetAvailableIPAddresses") - req, err := http.NewRequest(http.MethodGet, cns.GetAvailableIPAddressesPath, nil) - if err != nil { - t.Fatal(err) - } - w := httptest.NewRecorder() - mux.ServeHTTP(w, req) - - var getIPAddressesResponse cns.GetIPAddressesResponse - err = decodeResponse(w, &getIPAddressesResponse) - - if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetAvailableIPAddresses failed with response %+v", getIPAddressesResponse) - } else { - fmt.Printf ("GetAvailableIPAddresses Responded with %+v\n", getIPAddressesResponse); - } -} - -func TestGetReservedIPAddresses(t *testing.T){ - fmt.Println("Test: GetReservedIPAddresses") - req, err := http.NewRequest(http.MethodGet, cns.GetReservedIPAddressesPath, nil) - if err != nil { - t.Fatal(err) - } - w := httptest.NewRecorder() - mux.ServeHTTP(w, req) - - var getIPAddressesResponse cns.GetIPAddressesResponse - err = decodeResponse(w, &getIPAddressesResponse) - - if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetReservedIPAddresses failed with response %+v", getIPAddressesResponse) - } else { - fmt.Printf ("GetReservedIPAddresses Responded with %+v\n", getIPAddressesResponse); - } -} - -func TestGetGhostIPAddresses(t *testing.T){ +func TestGetUnhealthyIPAddresses(t *testing.T){ fmt.Println("Test: GetGhostIPAddresses") - req, err := http.NewRequest(http.MethodGet, cns.GetGhostIPAddressesPath, nil) - if err != nil { - t.Fatal(err) - } - w := httptest.NewRecorder() - mux.ServeHTTP(w, req) - - var getIPAddressesResponse cns.GetIPAddressesResponse - err = decodeResponse(w, &getIPAddressesResponse) - - if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetGhostIPAddresses failed with response %+v", getIPAddressesResponse) - } else { - fmt.Printf ("GetGhostIPAddresses Responded with %+v\n", getIPAddressesResponse); - } -} - -func TestGetAllIPAddresses(t *testing.T){ - fmt.Println("Test: GetAllIPAddresses") - req, err := http.NewRequest(http.MethodGet, cns.GetAllIPAddressesPath, nil) + req, err := http.NewRequest(http.MethodGet, cns.GetUnhealthyIPAddressesPath, nil) if err != nil { t.Fatal(err) } @@ -312,9 +244,9 @@ func TestGetAllIPAddresses(t *testing.T){ err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetAllIPAddresses failed with response %+v", getIPAddressesResponse) + t.Errorf("GetUnhealthyIPAddresses failed with response %+v", getIPAddressesResponse) } else { - fmt.Printf ("GetAllIPAddresses Responded with %+v\n", getIPAddressesResponse); + fmt.Printf ("GetUnhealthyIPAddresses Responded with %+v\n", getIPAddressesResponse); } } From b229d46a5f86b7560cd3a5f67d2197b7e055c8a9 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Fri, 9 Jun 2017 09:43:58 -0700 Subject: [PATCH 07/34] Rename folder (main -> service) in CNS --- cns/{main => service}/main.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cns/{main => service}/main.go (100%) diff --git a/cns/main/main.go b/cns/service/main.go similarity index 100% rename from cns/main/main.go rename to cns/service/main.go From 47cb4c69854053afe830fff7cd99f1fa9bbf25c7 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Fri, 9 Jun 2017 09:56:29 -0700 Subject: [PATCH 08/34] Fix some formatting --- cns/api.go | 2 +- cns/dockerclient/dockerclient.go | 15 +++++---- cns/examples/examples.go | 2 ++ cns/imdsclient/imdsclient.go | 6 ++++ cns/restserver/restserver.go | 56 +++++++++++++++++++++++++++++--- cns/service/main.go | 1 - common/service.go | 6 +++- 7 files changed, 73 insertions(+), 15 deletions(-) diff --git a/cns/api.go b/cns/api.go index 6ce07574ac..3d2327fe20 100644 --- a/cns/api.go +++ b/cns/api.go @@ -12,7 +12,7 @@ const ( ReleaseIPAddressPath = "/network/ip/Release" GetHostLocalIPPath = "/network/ip/HostLocal" GetIPAddressUtilizationPath = "/network/ip/Utilization" - GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy" + GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy" GetHealthReportPath = "/network/health" ) diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go index 90d7ddb71f..3c2c2c9d05 100644 --- a/cns/dockerclient/dockerclient.go +++ b/cns/dockerclient/dockerclient.go @@ -79,12 +79,14 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { config := &Config{ Subnet: primaryNic.Subnet, } + configs := make([]Config, 1) configs[0] = *config ipamConfig := &IPAM{ Driver: defaultIpamPlugin, Config: configs, } + netConfig := &NetworkConfiguration{ Name: networkName, Driver: defaultNetworkPlugin, @@ -117,8 +119,8 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { if(err != nil){ ermsg = err.Error() } - return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v - %v - %v", res.StatusCode, createNetworkResponse.message, ermsg) - + return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v - %v - %v", + res.StatusCode, createNetworkResponse.message, ermsg) } return nil @@ -129,14 +131,12 @@ func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { log.Printf("[Azure CNS] DeleteNetwork") url := dockerClient.connectionURL+inspectNetworkPath+networkName - req, err := http.NewRequest( - "DELETE", - url, - nil) + req, err := http.NewRequest("DELETE", url, nil) if err != nil { log.Printf("[Azure CNS] Error received while creating http DELETE request for network delete %v %v", networkName, err.Error()) return err } + req.Header.Set("Content-Type", "application/json; charset=utf-8") client := &http.Client{} res, err := client.Do(req) @@ -151,5 +151,6 @@ func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { return fmt.Errorf("[Azure CNS] Network not found %v", networkName) } - return fmt.Errorf("[Azure CNS] Unknown return code from docker delete network %v: ret = %d", networkName, res.StatusCode) + return fmt.Errorf("[Azure CNS] Unknown return code from docker delete network %v: ret = %d", + networkName, res.StatusCode) } diff --git a/cns/examples/examples.go b/cns/examples/examples.go index 2f0f36edb3..9ad6fedb08 100644 --- a/cns/examples/examples.go +++ b/cns/examples/examples.go @@ -16,6 +16,8 @@ const ( defaultCNSServerURL = "http://localhost:10090" ) +// These are example showing how to use CNS APIs by clients + func setEnvironment() error{ envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} envRequestJSON := new(bytes.Buffer) diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go index c8c68f5c3e..4ddc71163f 100644 --- a/cns/imdsclient/imdsclient.go +++ b/cns/imdsclient/imdsclient.go @@ -15,6 +15,7 @@ import ( // GetPrimaryInterfaceInfoFromHost retrieves subnet and gateway of primary NIC from Host. func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, error) { log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromHost") + interfaceInfo := &InterfaceInfo{} resp, err := http.Get(hostQueryURL) if(err != nil){ @@ -59,6 +60,7 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, interfaceInfo.PrimaryIP = ip.Address } } + imdsClient.primaryInterface = interfaceInfo break; } @@ -67,17 +69,20 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, break; } } + var er error er = nil if (!foundPrimaryInterface) { er = fmt.Errorf("Unable to find primary NIC") } + return interfaceInfo, er } // GetPrimaryInterfaceInfoFromMemory retrieves subnet and gateway of primary NIC that is saved in memory. func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromMemory() (*InterfaceInfo, error) { log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromMemory") + var iface *InterfaceInfo var err error if(imdsClient.primaryInterface == nil) { @@ -91,5 +96,6 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromMemory() (*InterfaceInf } else { iface = imdsClient.primaryInterface } + return iface, err } \ No newline at end of file diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 82c54ef0db..ab771d57af 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -49,6 +49,7 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { if err != nil { return nil, err } + imdsClient := &imdsclient.ImdsClient{} dc, err := dockerclient.NewDefaultDockerClient(imdsClient) if(err != nil){ @@ -96,9 +97,11 @@ func (service *httpRestService) Stop() { // Handles requests to set the environment type. func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] setEnvironment") + var req cns.SetEnvironmentRequest err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) + if err != nil { return } @@ -115,18 +118,19 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re resp := &cns.Response{ReturnCode: 0} err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) } // Handles CreateNetwork requests. func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] createNetwork") + var err error returnCode := 0 returnMessage := "" if(service.state.Initialized) { - var req cns.CreateNetworkRequest err = service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) @@ -182,20 +186,24 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req } err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) } // Handles DeleteNetwork requests. func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] deleteNetwork") + var req cns.DeleteNetworkRequest returnCode := 0 returnMessage := "" err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) + if err != nil { return } + switch r.Method { case "POST": dc := service.dockerClient @@ -227,20 +235,25 @@ func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Req ReturnCode: returnCode, Message: returnMessage, } + err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) } // Handles ip reservation requests. func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] reserveIPAddress") - var req cns.ReserveIPAddressRequest + var req cns.ReserveIPAddressRequest err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { return } + switch r.Method { case "POST": default: @@ -249,19 +262,23 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. resp := cns.Response{ReturnCode: 0} reserveResp := &cns.ReserveIPAddressResponse{Response: resp, IPAddress: "0.0.0.0"} err = service.Listener.Encode(w, &reserveResp) + log.Response(service.Name, reserveResp, err) } // Handles release ip reservation requests. func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] releaseIPAddress") - var req cns.ReleaseIPAddressRequest err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) + + var req cns.ReleaseIPAddressRequest + if err != nil { return } + switch r.Method { case "POST": default: @@ -269,16 +286,19 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. resp := &cns.Response{ReturnCode: 0} err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) } // Retrieves the host local ip address. Containers can talk to host using this IP address. func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Request) { - var found bool - var errmsg string log.Printf("[Azure CNS] getHostLocalIP") log.Request(service.Name, "getHostLocalIP", nil) + + var found bool + var errmsg string hostLocalIP := "0.0.0.0" + if service.state.Initialized { switch r.Method { case "GET": @@ -293,13 +313,16 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) } } + case "Overlay": errmsg = "[Azure-CNS] Overlay is not yet supported." } + default: errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." } } + returnCode := 0 if !found { returnCode = NotFound @@ -313,7 +336,9 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re Response: resp, IPAddress: hostLocalIP, } + err := service.Listener.Encode(w, &hostLocalIPResponse) + log.Response(service.Name, hostLocalIPResponse, err) } @@ -321,16 +346,20 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getIPAddressUtilization") log.Request(service.Name, "getIPAddressUtilization", nil) + switch r.Method { case "GET": default: } + resp := cns.Response{ReturnCode: 0} utilResponse := &cns.IPAddressesUtilizationResponse{ Response: resp, Available: 0, } + err := service.Listener.Encode(w, &utilResponse) + log.Response(service.Name, utilResponse, err) } @@ -338,13 +367,16 @@ func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getAvailableIPAddresses") log.Request(service.Name, "getAvailableIPAddresses", nil) + switch r.Method { case "GET": default: } + resp := cns.Response{ReturnCode: 0} ipResp := &cns.GetIPAddressesResponse{Response: resp} err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) } @@ -352,13 +384,16 @@ func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getReservedIPAddresses") log.Request(service.Name, "getReservedIPAddresses", nil) + switch r.Method { case "GET": default: } + resp := cns.Response{ReturnCode: 0} ipResp := &cns.GetIPAddressesResponse{Response: resp} err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) } @@ -366,13 +401,16 @@ func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r func (service *httpRestService) getUnhealthyIPAddresses(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getUnhealthyIPAddresses") log.Request(service.Name, "getUnhealthyIPAddresses", nil) + switch r.Method { case "GET": default: } + resp := cns.Response{ReturnCode: 0} ipResp := &cns.GetIPAddressesResponse{Response: resp} err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) } @@ -380,13 +418,16 @@ func (service *httpRestService) getUnhealthyIPAddresses(w http.ResponseWriter, r func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getAllIPAddresses") log.Request(service.Name, "getAllIPAddresses", nil) + switch r.Method { case "GET": default: } + resp := cns.Response{ReturnCode: 0} ipResp := &cns.GetIPAddressesResponse{Response: resp} err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) } @@ -394,6 +435,7 @@ func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getHealthReport") log.Request(service.Name, "getHealthReport", nil) + switch r.Method { case "GET": default: @@ -401,12 +443,14 @@ func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.R resp := &cns.Response{ReturnCode: 0} err := service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) } // saveState writes CNS state to persistent store. func (service *httpRestService) saveState() error { log.Printf("[Azure CNS] saveState") + // Skip if a store is not provided. if service.store == nil { log.Printf("[Azure CNS] store not initialized.") @@ -421,12 +465,14 @@ func (service *httpRestService) saveState() error { } else { log.Printf("[Azure CNS] Failed to save state., err:%v\n", err) } + return err } // restoreState restores CNS state from persistent store. func (service *httpRestService) restoreState() error { log.Printf("[Azure CNS] restoreState") + // Skip if a store is not provided. if service.store == nil { log.Printf("[Azure CNS] store not initialized.") diff --git a/cns/service/main.go b/cns/service/main.go index cb8c1cc277..6867280a96 100644 --- a/cns/service/main.go +++ b/cns/service/main.go @@ -109,7 +109,6 @@ func main() { return } - // Create logging provider. log.SetName(name) log.SetLevel(logLevel) diff --git a/common/service.go b/common/service.go index ff3cf10922..ae9fc2ab41 100644 --- a/common/service.go +++ b/common/service.go @@ -38,14 +38,15 @@ type ServiceConfig struct { // NewService creates a new Service object. func NewService(name, version string, store store.KeyValueStore) (*Service, error) { - log.Debugf("[Azure CNS] Going to create a service object with name: %v. version: %v.", name, version) + svc := &Service{ Name: name, Version: version, Options: make(map[string]interface{}), Store: store, } + log.Debugf("[Azure CNS] Finished creating service object with name: %v. version: %v.", name, version) return svc, nil } @@ -59,10 +60,13 @@ func (service *Service) Initialize(config *ServiceConfig) error { } log.Debugf("[Azure CNS] Going to initialize the service: %+v with config: %+v.", service, config) + service.ErrChan = config.ErrChan service.Store = config.Store service.Version = config.Version + log.Debugf("[Azure CNS] nitialized service: %+v with config: %+v.", service, config) + return nil } From 79d43bd88502a698198b2cb06aced8dedffb5754 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Wed, 14 Jun 2017 10:39:25 -0700 Subject: [PATCH 09/34] Capability to restore routes that get lost during network create --- cns/restserver/restserver.go | 21 ++++ cns/routes/routes.go | 42 ++++++++ cns/routes/routes_linux.go | 27 ++++++ cns/routes/routes_test.go | 113 ++++++++++++++++++++++ cns/routes/routes_windows.go | 181 +++++++++++++++++++++++++++++++++++ 5 files changed, 384 insertions(+) create mode 100644 cns/routes/routes.go create mode 100644 cns/routes/routes_linux.go create mode 100644 cns/routes/routes_test.go create mode 100644 cns/routes/routes_windows.go diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index ab771d57af..3eb1bed940 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -14,6 +14,7 @@ import ( "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" + "github.com/Azure/azure-container-networking/cns/routes" ) const ( @@ -26,6 +27,7 @@ type httpRestService struct { *cns.Service dockerClient *dockerclient.DockerClient imdsClient *imdsclient.ImdsClient + routingTable *routes.RoutingTable store store.KeyValueStore state httpRestServiceState } @@ -51,6 +53,7 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { } imdsClient := &imdsclient.ImdsClient{} + routingTable := &routes.RoutingTable{} dc, err := dockerclient.NewDefaultDockerClient(imdsClient) if(err != nil){ return nil, err @@ -61,6 +64,7 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { store: service.Service.Store, dockerClient: dc, imdsClient: imdsClient, + routingTable: routingTable, }, nil } @@ -142,6 +146,7 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req switch r.Method { case "POST": dc := service.dockerClient + rt := service.routingTable err = dc.NetworkExists(req.NetworkName) // Network does not exist. @@ -151,11 +156,27 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req switch service.state.Location { case "Azure": log.Printf("[Azure CNS] Goign to create network with name %v.", req.NetworkName) + + err = rt.GetRoutingTable() + if(err != nil) { + // We should not fail the call to create network for this. + // This is because restoring routes is a fallback mechanism in case + // network driver is not behaving as expected. + // The responsibility to restore routes is with network driver. + log.Printf("[Azure CNS] Unable to get routing table from node, %+v.", err.Error()) + } + err = dc.CreateNetwork(req.NetworkName) if(err != nil) { returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) returnCode = UnexpectedError } + + err = rt.RestoreRoutingTable() + if(err != nil) { + log.Printf("[Azure CNS] Unable to restore routing table on node, %+v.", err.Error()) + } + case "StandAlone": returnMessage = fmt.Sprintf("[Azure CNS] Error. Underlay network is not supported in StandAlone environment. %v.", err.Error()) returnCode = UnsupportedEnvironment diff --git a/cns/routes/routes.go b/cns/routes/routes.go new file mode 100644 index 0000000000..7f6b072c27 --- /dev/null +++ b/cns/routes/routes.go @@ -0,0 +1,42 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package routes + +import ( + "github.com/Azure/azure-container-networking/log" +) + +// Route describes a single route in the routing table. +type Route struct { + destination string + mask string + gateway string + metric string + ifaceIndex int +} + +// RoutingTable describes the routing table on the node. +type RoutingTable struct { + Routes []Route +} + +// GetRoutingTable retireves routing table in the node. +func (rt *RoutingTable) GetRoutingTable() (error) { + routes, err := getRoutes() + if(err == nil) { + rt.Routes = routes + } + + return err +} + +// RestoreRoutingTable pushes the saved route. +func (rt *RoutingTable) RestoreRoutingTable () (error) { + if rt.Routes == nil { + log.Printf("[Azure CNS] Nothing available in routing table to push") + return nil + } + + return putRoutes(rt.Routes) +} \ No newline at end of file diff --git a/cns/routes/routes_linux.go b/cns/routes/routes_linux.go new file mode 100644 index 0000000000..13d4b3b6af --- /dev/null +++ b/cns/routes/routes_linux.go @@ -0,0 +1,27 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +// +build linux + +package routes + +import ( + "fmt" + "net" + "os/exec" + + "strings" + + "github.com/Azure/azure-container-networking/log" +) + +const ( +) + +func getRoutes() ([]Route, error) { + return nil, nil +} + +func putRoutes(routes []Route) error { + return nil +} diff --git a/cns/routes/routes_test.go b/cns/routes/routes_test.go new file mode 100644 index 0000000000..cab35cdafa --- /dev/null +++ b/cns/routes/routes_test.go @@ -0,0 +1,113 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package routes + +import ( + "os" + "os/exec" + "testing" + "github.com/Azure/azure-container-networking/log" +) + +const ( + testDest = "159.254.169.254" + testMask = "255.255.255.255" + testGateway = "0.0.0.0" + testMetric = "12" +) + +// Wraps the test run. +func TestMain(m *testing.M) { + // Run tests. + exitCode := m.Run() + + os.Exit(exitCode) +} + +func addTestRoute() error { + arg := []string{"/C", "route", "ADD", testDest, + "MASK", testMask, testGateway, "METRIC", testMetric} + log.Printf("[Azure CNS] Adding missing route: %v", arg) + + c := exec.Command("cmd", arg...) + bytes, err := c.Output() + + if err == nil { + log.Printf("[Azure CNS] Successfully executed add route: %v\n%v", + arg, string(bytes)) + } else { + log.Printf("[Azure CNS] Failed to execute add route: %v\n%v\n%v", + arg, string(bytes), err.Error()) + return err + } + return nil +} + +func deleteTestRoute() error { + args := []string{"/C", "route", "DELETE", testDest, "MASK", testMask, + testGateway, "METRIC", testMetric} + log.Printf("[Azure CNS] Deleting route: %v", args) + c := exec.Command("cmd", args...) + bytes, err := c.Output() + if err == nil { + log.Printf("[Azure CNS] Successfully executed delete route: %v\n%v", + args, string(bytes)) + } else { + log.Printf("[Azure CNS] Failed to execute delete route: %v\n%v\n%v", + args, string(bytes), err.Error()) + return err + } + return nil +} + +// TestPutRoutes tests if a missing route is properly restored or not. +func TestRestoreMissingRoute(t *testing.T){ + log.Printf("Test: PutMissingRoutes") + + err := addTestRoute() + if(err != nil) { + t.Errorf("add route failed %+v", err.Error()) + } + + rt := &RoutingTable{} + + // save routing table. + rt.GetRoutingTable() + cntr := 0 + for _,rt := range rt.Routes { + log.Printf("[]: Route[%d]: %+v", cntr, rt) + cntr++ + } + + // now delete the route so it goes missing. + err = deleteTestRoute() + if(err != nil) { + t.Errorf("delete route failed %+v", err.Error()) + } + + // now restore the deleted route. + rt.RestoreRoutingTable() + + // test if route was resotred or not. + rt.GetRoutingTable() + restored := false + for _, existingRoute := range rt.Routes { + log.Printf("Comapring %+v\n", existingRoute) + if(existingRoute.destination == testDest && + existingRoute.gateway == testGateway && + existingRoute.mask == testMask) { + restored = true + } + } + + if(!restored){ + t.Errorf("unable to restore missing route") + } else { + err = deleteTestRoute() + if(err != nil) { + t.Errorf("delete route failed %+v", err.Error()) + } + } +} + diff --git a/cns/routes/routes_windows.go b/cns/routes/routes_windows.go new file mode 100644 index 0000000000..997567b3dc --- /dev/null +++ b/cns/routes/routes_windows.go @@ -0,0 +1,181 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +// +build windows + +package routes + +import ( + "fmt" + "net" + "strings" + "os/exec" + + "github.com/Azure/azure-container-networking/log" +) + +const ( + ipv4RoutingTableStart = "IPv4 Route Table" + activeRoutesStart = "Active Routes:" +) + +func getInterfaceByAddress(address string) (int, error) { + log.Printf("[Azure CNS] getInterfaceByAddress") + + var ifaces []net.Interface + log.Printf("[Azure CNS] Going to obtain interface for address %s", address) + ifaces, err := net.Interfaces() + if err != nil { + return -1, err + } + + for i := 0; i < len(ifaces); i++ { + log.Debugf("[Azure CNS] Going to check interface %v", ifaces[i].Name) + addrs, _ := ifaces[i].Addrs() + for _, addr := range addrs { + log.Debugf("[Azure CNS] ipAddress being compared input=%v %v\n", + address, addr.String()) + ip := strings.Split(addr.String(), "/") + if(len(ip) != 2) { + return -1, fmt.Errorf("Malformed ip: %v", addr.String()) + } + if ip[0] == address { + return ifaces[i].Index, nil + } + } + } + + return -1, fmt.Errorf( + "[Azure CNS] Unable to determine interface index for address %s", + address) +} + +func getRoutes() ([]Route, error) { + log.Printf("[Azure CNS] getRoutes") + + c := exec.Command("cmd", "/C", "route", "print") + var routePrintOutput string + var routeCount int + bytes, err := c.Output() + + if err == nil { + routePrintOutput = string(bytes) + log.Debugf("[Azure CNS] Printing Routing table \n %v\n", routePrintOutput) + } else { + log.Printf("Received error in printing routing table %v", err.Error()) + return nil, err + } + + routePrint := strings.Split(routePrintOutput, ipv4RoutingTableStart) + routeTable := strings.Split(routePrint[1], activeRoutesStart) + tokens := strings.Split( + strings.Split(routeTable[1], "Metric")[1], + "=") + + table := tokens[0] + routes := strings.Split(table, "\r") + routeCount = len(routes) + log.Debugf("[Azure CNS] Recevied route count: %d", routeCount) + if(routeCount == 0){ + return nil, nil + } + + localRoutes := make([]Route, routeCount) + cntr := 0 + truncated := 0 + for _, route := range routes { + if route != "" { + tokens := strings.Fields(route) + if len(tokens) != 5 { + log.Printf("[Azure CNS] Ignoring route %s", route) + truncated++ + } else { + log.Debugf("[Azure CNS] Parsing route: %s %s %s %s %s\n", + tokens[0], tokens[1], tokens[2], tokens[3], tokens[4]) + rt := Route{ + destination: tokens[0], + mask: tokens[1], + gateway: tokens[2], + metric: tokens[4], + } + + if(rt.gateway == "On-link"){ + rt.gateway = "0.0.0.0" + } + + index, err := getInterfaceByAddress(tokens[3]) + if err == nil { + rt.ifaceIndex = index + localRoutes[cntr] = rt + cntr++ + } else { + log.Printf("[Azure CNS] Error encountered while obtaining index. %v\n", err.Error()) + truncated++ + } + } + } + } + + if(truncated == routeCount) { + localRoutes = nil + } else { + localRoutes = localRoutes[0 : routeCount-truncated-1] + } + + return localRoutes, nil +} + +func containsRoute(routes []Route, route Route) (bool, error){ + log.Printf("[Azure CNS] containsRoute") + if(routes == nil){ + return false, nil + } + for _, existingRoute := range routes { + if(existingRoute.destination == route.destination && + existingRoute.gateway == route.gateway && + existingRoute.ifaceIndex == route.ifaceIndex && + existingRoute.mask == route.mask) { + return true, nil + } + } + return false, nil +} + +func putRoutes(routes []Route) error { + log.Printf("[Azure CNS] putRoutes") + + var err error + log.Printf("[Azure CNS] Going to get current routes") + currentRoutes, err := getRoutes() + if( err != nil){ + return err + } + + for _, route := range routes { + exists, err := containsRoute(currentRoutes, route) + if(err == nil && !exists) { + args := []string{"/C", "route", "ADD", + route.destination, + "MASK", + route.mask, + route.gateway, + "METRIC", + route.metric, + "IF", + fmt.Sprintf("%d", route.ifaceIndex)} + log.Printf("[Azure CNS] Adding missing route: %v", args) + + c := exec.Command("cmd", args...) + bytes, err := c.Output() + if err == nil { + log.Printf("[Azure CNS] Successfully executed add route: %v\n%v", args, string(bytes)) + } else { + log.Printf("[Azure CNS] Failed to execute add route: %v\n%v", args, string(bytes)) + } + } else { + log.Printf("[Azure CNS] Route already exists. skipping %+v", route) + } + } + + return err +} From 5288f30cc437eb41d3ac2a8c006725d1bcef1660 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Fri, 2 Jun 2017 16:23:08 -0700 Subject: [PATCH 10/34] Create container network service (does not contain integration with IPAM and Docker) --- cns/api.go | 115 +++++++++++++ cns/client/examples.go | 225 +++++++++++++++++++++++++ cns/main/main.go | 151 +++++++++++++++++ cns/restserver/httprest.go | 266 +++++++++++++++++++++++++++++ cns/restserver/httprest_test.go | 290 ++++++++++++++++++++++++++++++++ cns/service.go | 103 ++++++++++++ common/service.go | 79 +++++++++ 7 files changed, 1229 insertions(+) create mode 100644 cns/api.go create mode 100644 cns/client/examples.go create mode 100644 cns/main/main.go create mode 100644 cns/restserver/httprest.go create mode 100644 cns/restserver/httprest_test.go create mode 100644 cns/service.go create mode 100644 common/service.go diff --git a/cns/api.go b/cns/api.go new file mode 100644 index 0000000000..13d545b982 --- /dev/null +++ b/cns/api.go @@ -0,0 +1,115 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package cns + +// Container Network Service remote API Contract +const ( + SetEnvironmentPath = "/Network/Environment" + CreateNetworkPath = "/Network/Create" + DeleteNetworkPath = "/Network/Delete" + ReserveIPAddressPath = "/Network/IP/Reserve" + ReleaseIPAddressPath = "/Network/IP/Release" + GetHostLocalIPPath = "/Network/IP/HostLocal" + GetIPAddressUtilizationPath = "/Network/IP/Utilization" + GetAvailableIPAddressesPath = "/Network/IPAddresses/Available" + GetReservedIPAddressesPath = "/Network/IPAddresses/Reserved" + GetGhostIPAddressesPath = "/Network/IPAddresses/Ghost" + GetAllIPAddressesPath = "/Network/IPAddresses/All" + GetHealthReportPath = "/Network/Health" +) + +// SetEnvironmentRequest describes the Request to set the environment in HNS. +type SetEnvironmentRequest struct { + Location string + NetworkType string +} + +// OverlayConfiguration describes configuration for all the nodes that are part of overlay. +type OverlayConfiguration struct { + NodeCount int + LocalNodeIP string + OverlaySubent Subnet + NodeConfig []NodeConfiguration +} + +// CreateNetworkRequest describes request to create the network. +type CreateNetworkRequest struct { + NetworkName string + OverlayConfiguration OverlayConfiguration +} + +// DeleteNetworkRequest describes request to delete the network. +type DeleteNetworkRequest struct { + NetworkName string +} + +// ReserveIPAddressRequest describes request to reserve an IP Address +type ReserveIPAddressRequest struct { + ReservationID string +} + +// ReserveIPAddressResponse describes response to reserve an IP address. +type ReserveIPAddressResponse struct { + Response Response + IPAddress string +} + +// ReleaseIPAddressRequest describes request to release an IP Address. +type ReleaseIPAddressRequest struct { + ReservationID string +} + +// IPAddressesUtilizationResponse describes response for ip address utilization. +type IPAddressesUtilizationResponse struct { + Response Response + Available int + Reserved int + Ghost int +} + +// GetIPAddressesResponse describes response containing requested ip addresses. +type GetIPAddressesResponse struct { + Response Response + IPAddresses []IPAddress +} + +// HostLocalIPAddressResponse describes reponse that returns the host local IP Address. +type HostLocalIPAddressResponse struct { + Response Response + IPAddress string +} + +// IPAddress Contains information about an ip address. +type IPAddress struct { + IPAddress string + ReservationID string + IsGhost bool +} + +// Subnet contains the ip address and the number of bits in prefix. +type Subnet struct { + IPAddress string + PrefixLength int +} + +// NodeConfiguration describes confguration for a node in overlay network. +type NodeConfiguration struct { + NodeIP string + NodeID string + NodeSubnet Subnet +} + +// Response describes generic response from CNS. +type Response struct { + ReturnCode int + Message string +} + +// OptionMap describes generic options that can be passed to CNS. +type OptionMap map[string]interface{} + +// Response to a failed request. +type errorResponse struct { + Err string +} \ No newline at end of file diff --git a/cns/client/examples.go b/cns/client/examples.go new file mode 100644 index 0000000000..e7d57c9807 --- /dev/null +++ b/cns/client/examples.go @@ -0,0 +1,225 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package main + +import ( + "bytes" + "fmt" + "encoding/json" + "net/http" + + "github.com/Azure/azure-container-networking/cns" +) + +const ( + defaultCNSServerURL = "http://localhost:10090" +) + +func setEnvironment() error{ + envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} + envRequestJSON := new(bytes.Buffer) + json.NewEncoder(envRequestJSON).Encode(envRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.SetEnvironmentPath, + "application/json; charset=utf-8", + envRequestJSON) + if err != nil { + fmt.Printf("Error received in Set Env: %v ", err.Error()) + return err + } + var setEnvironmentResponse cns.Response + err = json.NewDecoder(res.Body).Decode(&setEnvironmentResponse) + if err != nil{ + fmt.Printf("Error received in decoding response from SetEnvironment: %v ", err.Error()) + return err + } + fmt.Printf("Response for SetEnvironment: %+v\n", setEnvironmentResponse) + return nil +} + +func createNetwork() error{ + netRequest := cns.CreateNetworkRequest{NetworkName:"azurenet"} + netRequestJSON := new(bytes.Buffer) + json.NewEncoder(netRequestJSON).Encode(netRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.CreateNetworkPath, + "application/json; charset=utf-8", + netRequestJSON) + if err != nil { + fmt.Printf("Error received in CreateNetwork post: %v ", err.Error()) + return err + } + + var createNetworkResponse cns.Response + err = json.NewDecoder(res.Body).Decode(&createNetworkResponse) + if err != nil{ + fmt.Printf("Error received in decoding response from CreateNEtwork: %v ", err.Error()) + return err + } + fmt.Printf("Response for CreateNetwork: %+v\n", createNetworkResponse) + return nil +} + +func reserveIPAddress() error{ + reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID:"ip01"} + reserveIPRequestJSON := new(bytes.Buffer) + json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.ReserveIPAddressPath, + "application/json; charset=utf-8", + reserveIPRequestJSON) + if err != nil { + fmt.Printf("Error received in reserveIPAddress post: %v ", err.Error()) + return err + } + var reserveIPAddressResponse cns.ReserveIPAddressResponse + err = json.NewDecoder(res.Body).Decode(&reserveIPAddressResponse) + if err != nil{ + fmt.Printf("Error received in decoding response from reserveIPAddress: %v ", err.Error()) + return err + } + fmt.Printf("Response for reserveIPAddress: %+v\n", reserveIPAddressResponse) + return nil +} + +func releaseIPAddress() error{ + releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID:"ip01"} + releaseIPAddressRequestJSON := new(bytes.Buffer) + json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.ReleaseIPAddressPath, + "application/json; charset=utf-8", + releaseIPAddressRequestJSON) + if err != nil { + fmt.Printf("Error received in releaseIPAddress post: %v ", err.Error()) + return err + } + var releaseIPAddressResponse cns.Response + err = json.NewDecoder(res.Body).Decode(&releaseIPAddressResponse) + if err != nil{ + fmt.Printf("Error received in decoding response from releaseIPAddress: %v ", err.Error()) + return err + } + fmt.Printf("Response for releaseIPAddress: %+v\n", releaseIPAddressResponse) + return nil +} + +func getIPAddressUtilization() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetIPAddressUtilizationPath) + if err != nil { + fmt.Printf("Error received in getIPAddressUtilization GET: %v ", err.Error()) + return err + } + var iPAddressesUtilizationResponse cns.IPAddressesUtilizationResponse + err = json.NewDecoder(res.Body).Decode(&iPAddressesUtilizationResponse) + if err != nil { + fmt.Printf("Error received in decoding response from getIPAddressUtilization: %v ", err.Error()) + return err + } + fmt.Printf("Response for getIPAddressUtilization: %+v\n", iPAddressesUtilizationResponse) + return nil +} + +func getHostLocalIP() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetHostLocalIPPath) + if err != nil { + fmt.Printf("Error received in getHostLocalIP GET: %v ", err.Error()) + return err + } + var hostLocalIPAddressResponse cns.HostLocalIPAddressResponse + err = json.NewDecoder(res.Body).Decode(&hostLocalIPAddressResponse) + if err != nil { + fmt.Printf("Error received in decoding response from getHostLocalIP: %v ", err.Error()) + return err + } + fmt.Printf("Response for getHostLocalIP: %+v\n", hostLocalIPAddressResponse) + return nil +} + +func getAvailableIPAddresses() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetAvailableIPAddressesPath) + if err != nil { + fmt.Printf("Error received in GetAvailable IP Addresses: %v ", err.Error()) + return err + } + var getIPAddressesResponse cns.GetIPAddressesResponse + err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) + if err != nil { + fmt.Printf("Error received in decoding response from GetAvailableIPAddresses: %v ", err.Error()) + return err + } + fmt.Printf("Response for GetAvailableIPAddresses: %+v\n", getIPAddressesResponse) + return nil +} + +func getReservedIPAddresses() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetReservedIPAddressesPath) + if err != nil { + fmt.Printf("Error received in GetReserved IP Addresses: %v ", err.Error()) + return err + } + var getIPAddressesResponse cns.GetIPAddressesResponse + err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) + if err != nil { + fmt.Printf("Error received in decoding response from getReservedIPAddresses: %v ", err.Error()) + return err + } + fmt.Printf("Response for getReservedIPAddresses: %+v\n", getIPAddressesResponse) + return nil +} + +func getGhostIPAddresses() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetGhostIPAddressesPath) + if err != nil { + fmt.Printf("Error received in GetGhost IP Addresses: %v ", err.Error()) + return err + } + var getIPAddressesResponse cns.GetIPAddressesResponse + err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) + if err != nil { + fmt.Printf("Error received in decoding response from getGhostIPAddresses: %v ", err.Error()) + return err + } + fmt.Printf("Response for getGhostIPAddresses: %+v\n", getIPAddressesResponse) + return nil +} + +func getAllIPAddresses() error{ + res, err := + http.Get(defaultCNSServerURL+cns.GetAllIPAddressesPath) + if err != nil { + fmt.Printf("Error received in GetAll IP Addresses: %v ", err.Error()) + return err + } + var getIPAddressesResponse cns.GetIPAddressesResponse + err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) + if err != nil { + fmt.Printf("Error received in decoding response from getAllIPAddresses: %v ", err.Error()) + return err + } + fmt.Printf("Response for getAllIPAddresses: %+v\n", getIPAddressesResponse) + return nil +} + +func main() { + setEnvironment() + createNetwork() + reserveIPAddress() + releaseIPAddress() + getAvailableIPAddresses() + getReservedIPAddresses() + getGhostIPAddresses() + getAllIPAddresses() + getIPAddressUtilization() + getHostLocalIP() +} diff --git a/cns/main/main.go b/cns/main/main.go new file mode 100644 index 0000000000..7a0ebc79af --- /dev/null +++ b/cns/main/main.go @@ -0,0 +1,151 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package main + +import ( + "fmt" + "os" + "os/signal" + "syscall" + + "github.com/Azure/azure-container-networking/cns/restserver" + "github.com/Azure/azure-container-networking/common" + "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/platform" + "github.com/Azure/azure-container-networking/store" +) + +const ( + // Service name. + name = "azure-cns" +) + +// Version is populated by make during build. +var version string + +// Command line arguments for CNM plugin. +var args = common.ArgumentList{ + { + Name: common.OptAPIServerURL, + Shorthand: common.OptAPIServerURLAlias, + Description: "Set the API server URL", + Type: "string", + DefaultValue: "", + }, + { + Name: common.OptLogLevel, + Shorthand: common.OptLogLevelAlias, + Description: "Set the logging level", + Type: "int", + DefaultValue: common.OptLogLevelInfo, + ValueMap: map[string]interface{}{ + common.OptLogLevelInfo: log.LevelInfo, + common.OptLogLevelDebug: log.LevelDebug, + }, + }, + { + Name: common.OptLogTarget, + Shorthand: common.OptLogTargetAlias, + Description: "Set the logging target", + Type: "int", + DefaultValue: common.OptLogTargetFile, + ValueMap: map[string]interface{}{ + common.OptLogTargetSyslog: log.TargetSyslog, + common.OptLogTargetStderr: log.TargetStderr, + common.OptLogTargetFile: log.TargetLogfile, + }, + }, + { + Name: common.OptVersion, + Shorthand: common.OptVersionAlias, + Description: "Print version information", + Type: "bool", + DefaultValue: false, + }, +} + +// Prints description and version information. +func printVersion() { + fmt.Printf("Azure Container Network Service\n") + fmt.Printf("Version %v\n", version) +} + +// Main is the entry point for CNS. +func main() { + // Initialize and parse command line arguments. + common.ParseArgs(&args, printVersion) + + url := common.GetArg(common.OptAPIServerURL).(string) + logLevel := common.GetArg(common.OptLogLevel).(int) + logTarget := common.GetArg(common.OptLogTarget).(int) + vers := common.GetArg(common.OptVersion).(bool) + + if vers { + printVersion() + os.Exit(0) + } + + // Initialize CNS. + var config common.ServiceConfig + config.Version = version + config.Name = name + + // Create a channel to receive unhandled errors from CNS. + config.ErrChan = make(chan error, 1) + + // Create CNS object. + httpRestService, err := restserver.NewHTTPRestService(&config) + if err != nil { + fmt.Printf("Failed to create CNS object, err:%v.\n", err) + return + } + + // Create the key value store. + config.Store, err = store.NewJsonFileStore(platform.RuntimePath + name + ".json") + if err != nil { + fmt.Printf("Failed to create store: %v\n", err) + return + } + + // Create logging provider. + log.SetName(name) + log.SetLevel(logLevel) + err = log.SetTarget(logTarget) + if err != nil { + fmt.Printf("Failed to configure logging: %v\n", err) + return + } + + // Log platform information. + log.Printf("Running on %v", platform.GetOSInfo()) + + // Set CNS options. + httpRestService.SetOption(common.OptAPIServerURL, url) + + // Start CNS. + if httpRestService != nil { + err = httpRestService.Start(&config) + if err != nil { + fmt.Printf("Failed to start CNS, err:%v.\n", err) + return + } + } + + // Relay these incoming signals to OS signal channel. + osSignalChannel := make(chan os.Signal, 1) + signal.Notify(osSignalChannel, os.Interrupt, os.Kill, syscall.SIGTERM) + + // Wait until receiving a signal. + select { + case sig := <-osSignalChannel: + log.Printf("CNS Received OS signal <" + sig.String() + ">, shutting down.") + case err := <-config.ErrChan: + log.Printf("CNS Received unhandled error %v, shutting down.", err) + } + + // Cleanup. + if httpRestService != nil { + httpRestService.Stop() + } +} diff --git a/cns/restserver/httprest.go b/cns/restserver/httprest.go new file mode 100644 index 0000000000..7c6533172a --- /dev/null +++ b/cns/restserver/httprest.go @@ -0,0 +1,266 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package restserver + +import ( + "net/http" + + "github.com/Azure/azure-container-networking/cns" + "github.com/Azure/azure-container-networking/common" + "github.com/Azure/azure-container-networking/log" +) + +// httpRestService represents http listener for CNS - Container Networking Service. +type httpRestService struct { + *cns.Service +} + +// HTTPService describes the min API interface that every service should have. +type HTTPService interface { + common.ServiceAPI +} + +// NewHTTPRestService creates a new HTTP Service object. +func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { + service, err := cns.NewService(config.Name, config.Version) + if err != nil { + return nil, err + } + + return &httpRestService{ + Service: service, + }, nil +} + +// Start starts the CNS listener. +func (service *httpRestService) Start(config *common.ServiceConfig) error { + + err := service.Initialize(config) + if err != nil { + log.Printf("[Azure CNS] Failed to initialize base service, err:%v.", err) + return err + } + + // Add handlers. + listener := service.Listener + listener.AddHandler(cns.SetEnvironmentPath, service.setEnvironment) + listener.AddHandler(cns.CreateNetworkPath, service.createNetwork) + listener.AddHandler(cns.DeleteNetworkPath, service.deleteNetwork) + listener.AddHandler(cns.ReserveIPAddressPath, service.reserveIPAddress) + listener.AddHandler(cns.ReleaseIPAddressPath, service.releaseIPAddress) + listener.AddHandler(cns.GetHostLocalIPPath, service.getHostLocalIP) + listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) + listener.AddHandler(cns.GetAvailableIPAddressesPath, service.getAvailableIPAddresses) + listener.AddHandler(cns.GetReservedIPAddressesPath, service.getReservedIPAddresses) + listener.AddHandler(cns.GetGhostIPAddressesPath, service.getGhostIPAddresses) + listener.AddHandler(cns.GetAllIPAddressesPath, service.getAllIPAddresses) + listener.AddHandler(cns.GetHealthReportPath, service.getHealthReport) + + log.Printf("[Azure CNS] Listening.") + + return nil +} + +// Stop stops the CNS. +func (service *httpRestService) Stop() { + service.Uninitialize() + log.Printf("[Azure CNS] Service stopped.") +} + +// Handles requests to set the environment type. +func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Request) { + var req cns.SetEnvironmentRequest + err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { + return + } + + switch r.Method { + case "GET": + log.Printf("[Azure CNS] GET received for SetEnvironment.") + case "POST": + log.Printf("[Azure CNS] POST received for SetEnvironment.") + default: + } + + resp := &cns.Response{ReturnCode: 0} + err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) +} + +// Handles CreateNetwork requests. +func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Request) { + var req cns.CreateNetworkRequest + + err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { + return + } + switch r.Method { + case "POST": + default: + } + + resp := &cns.Response{ReturnCode: 0} + err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) +} + +// Handles DeleteNetwork requests. +func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Request) { + var req cns.DeleteNetworkRequest + + err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { + return + } + switch r.Method { + case "POST": + default: + } + + resp := &cns.Response{ReturnCode: 0} + err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) +} + +// Handles ip reservation requests. +func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http.Request) { + var req cns.ReserveIPAddressRequest + + err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { + return + } + switch r.Method { + case "POST": + default: + } + + resp := cns.Response{ReturnCode: 0} + reserveResp := &cns.ReserveIPAddressResponse{Response: resp, IPAddress: "0.0.0.0"} + err = service.Listener.Encode(w, &reserveResp) + log.Response(service.Name, reserveResp, err) +} + +// Handles release ip reservation requests. +func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http.Request) { + var req cns.ReleaseIPAddressRequest + + err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { + return + } + switch r.Method { + case "POST": + default: + } + + resp := &cns.Response{ReturnCode: 0} + err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) +} + +// Retrieves the host local ip address. +func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getHostLocalIP", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + hostLocalIPResponse := &cns.HostLocalIPAddressResponse{ + Response: resp, + IPAddress: "0.0.0.0", + } + err := service.Listener.Encode(w, &hostLocalIPResponse) + log.Response(service.Name, hostLocalIPResponse, err) +} + +// Handles ip address utiliztion requests. +func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getIPAddressUtilization", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + utilResponse := &cns.IPAddressesUtilizationResponse{ + Response: resp, + Available: 0, + } + err := service.Listener.Encode(w, &utilResponse) + log.Response(service.Name, utilResponse, err) +} + +// Handles retrieval of ip addresses that are available to be reserved from ipam driver. +func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getAvailableIPAddresses", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + ipResp := &cns.GetIPAddressesResponse{Response: resp} + err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) +} + +// Handles retrieval of reserved ip addresses from ipam driver. +func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getReservedIPAddresses", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + ipResp := &cns.GetIPAddressesResponse{Response: resp} + err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) +} + +// Handles retrieval of ghost ip addresses from ipam driver. +func (service *httpRestService) getGhostIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getGhostIPAddresses", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + ipResp := &cns.GetIPAddressesResponse{Response: resp} + err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) +} + +// Handles retrieval of all ip addresses from ipam driver. +func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getAllIPAddresses", nil) + switch r.Method { + case "GET": + default: + } + resp := cns.Response{ReturnCode: 0} + ipResp := &cns.GetIPAddressesResponse{Response: resp} + err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) +} + +// Handles health report requests. +func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.Request) { + log.Request(service.Name, "getHealthReport", nil) + + switch r.Method { + case "GET": + default: + } + + resp := &cns.Response{ReturnCode: 0} + err := service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) +} diff --git a/cns/restserver/httprest_test.go b/cns/restserver/httprest_test.go new file mode 100644 index 0000000000..4a4486bc30 --- /dev/null +++ b/cns/restserver/httprest_test.go @@ -0,0 +1,290 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package restserver + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/Azure/azure-container-networking/common" + "github.com/Azure/azure-container-networking/cns" +) + +var service HTTPService +var mux *http.ServeMux + +// Wraps the test run with service setup and teardown. +func TestMain(m *testing.M) { + var config common.ServiceConfig + var err error + + // Create the service. + service, err = NewHTTPRestService(&config) + if err != nil { + fmt.Printf("Failed to create CNS object %v\n", err) + os.Exit(1) + } + + // Configure test mode. + service.(*httpRestService).Name = "cns-test-server" + + // Start the service. + err = service.Start(&config) + if err != nil { + fmt.Printf("Failed to start CNS %v\n", err) + os.Exit(2) + } + + // Get the internal http mux as test hook. + mux = service.(*httpRestService).Listener.GetMux() + + // Run tests. + exitCode := m.Run() + + // Cleanup. + service.Stop() + + os.Exit(exitCode) +} + +// Decodes service'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 %d", w.Code) + } + + if w.Body == nil { + return fmt.Errorf("Response body is empty") + } + + return json.NewDecoder(w.Body).Decode(&response) +} + +// Tests CreateNetwork functionality. +func TestCreateNetwork(t *testing.T) { + fmt.Println("Test: CreateNetwork") + + var body bytes.Buffer + var resp cns.Response + + info := &cns.CreateNetworkRequest{ + NetworkName: "azurenet", + } + + json.NewEncoder(&body).Encode(info) + + req, err := http.NewRequest(http.MethodGet, cns.CreateNetworkPath, &body) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + err = decodeResponse(w, &resp) + + if err != nil || resp.ReturnCode != 0 { + t.Errorf("CreateNetwork response is invalid %+v", resp) + } else { + fmt.Printf ("CreateNetwork Responded with %+v\n", resp); + } +} + +func TestSetEnvironment(t *testing.T) { + fmt.Println("Test: SetEnvironment") + + var resp cns.Response + envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} + envRequestJSON := new(bytes.Buffer) + json.NewEncoder(envRequestJSON).Encode(envRequest) + + req, err := http.NewRequest(http.MethodGet, cns.SetEnvironmentPath, envRequestJSON) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + err = decodeResponse(w, &resp) + + if err != nil || resp.ReturnCode != 0 { + t.Errorf("SetEnvironment response is invalid %+v", resp) + } else { + fmt.Printf ("SetEnvironment Responded with %+v\n", resp); + } +} + +func TestReserveIPAddress(t *testing.T){ + fmt.Println("Test: ReserveIPAddress") + + reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID:"ip01"} + reserveIPRequestJSON := new(bytes.Buffer) + json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) + + envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} + envRequestJSON := new(bytes.Buffer) + json.NewEncoder(envRequestJSON).Encode(envRequest) + + req, err := http.NewRequest(http.MethodGet, cns.SetEnvironmentPath, envRequestJSON) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var reserveIPAddressResponse cns.ReserveIPAddressResponse + err = decodeResponse(w, &reserveIPAddressResponse) + + if err != nil || reserveIPAddressResponse.Response.ReturnCode != 0 { + t.Errorf("SetEnvironment response is invalid %+v", reserveIPAddressResponse) + } else { + fmt.Printf ("SetEnvironment Responded with %+v\n", reserveIPAddressResponse); + } +} + +func TestReleaseIPAddress(t *testing.T){ + fmt.Println("Test: ReleaseIPAddress") + releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID:"ip01"} + releaseIPAddressRequestJSON := new(bytes.Buffer) + json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) + + req, err := http.NewRequest(http.MethodGet, cns.ReleaseIPAddressPath, releaseIPAddressRequestJSON) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var releaseIPAddressResponse cns.Response + err = decodeResponse(w, &releaseIPAddressResponse) + + if err != nil || releaseIPAddressResponse.ReturnCode != 0 { + t.Errorf("SetEnvironment response is invalid %+v", releaseIPAddressResponse) + } else { + fmt.Printf ("SetEnvironment Responded with %+v\n", releaseIPAddressResponse); + } +} +func TestGetIPAddressUtilization(t *testing.T){ + fmt.Println("Test: GetIPAddressUtilization") + + req, err := http.NewRequest(http.MethodGet, cns.GetIPAddressUtilizationPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var iPAddressesUtilizationResponse cns.IPAddressesUtilizationResponse + err = decodeResponse(w, &iPAddressesUtilizationResponse) + + if err != nil || iPAddressesUtilizationResponse.Response.ReturnCode != 0 { + t.Errorf("GetIPAddressUtilization response is invalid %+v", iPAddressesUtilizationResponse) + } else { + fmt.Printf ("GetIPAddressUtilization Responded with %+v\n", iPAddressesUtilizationResponse); + } +} + +func TestGetHostLocalIP(t *testing.T){ + fmt.Println("Test: GetHostLocalIP") + req, err := http.NewRequest(http.MethodGet, cns.GetHostLocalIPPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var hostLocalIPAddressResponse cns.HostLocalIPAddressResponse + err = decodeResponse(w, &hostLocalIPAddressResponse) + + if err != nil || hostLocalIPAddressResponse.Response.ReturnCode != 0 { + t.Errorf("GetHostLocalIP response is invalid %+v", hostLocalIPAddressResponse) + } else { + fmt.Printf ("GetHostLocalIP Responded with %+v\n", hostLocalIPAddressResponse); + } +} + +func TestGetAvailableIPAddresses(t *testing.T){ + fmt.Println("Test: GetAvailableIPAddresses") + req, err := http.NewRequest(http.MethodGet, cns.GetAvailableIPAddressesPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var getIPAddressesResponse cns.GetIPAddressesResponse + err = decodeResponse(w, &getIPAddressesResponse) + + if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { + t.Errorf("GetAvailableIPAddresses response is invalid %+v", getIPAddressesResponse) + } else { + fmt.Printf ("GetAvailableIPAddresses Responded with %+v\n", getIPAddressesResponse); + } +} + +func TestGetReservedIPAddresses(t *testing.T){ + fmt.Println("Test: GetReservedIPAddresses") + req, err := http.NewRequest(http.MethodGet, cns.GetReservedIPAddressesPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var getIPAddressesResponse cns.GetIPAddressesResponse + err = decodeResponse(w, &getIPAddressesResponse) + + if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { + t.Errorf("GetReservedIPAddresses response is invalid %+v", getIPAddressesResponse) + } else { + fmt.Printf ("GetReservedIPAddresses Responded with %+v\n", getIPAddressesResponse); + } +} + +func TestGetGhostIPAddresses(t *testing.T){ + fmt.Println("Test: GetGhostIPAddresses") + req, err := http.NewRequest(http.MethodGet, cns.GetGhostIPAddressesPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var getIPAddressesResponse cns.GetIPAddressesResponse + err = decodeResponse(w, &getIPAddressesResponse) + + if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { + t.Errorf("GetGhostIPAddresses response is invalid %+v", getIPAddressesResponse) + } else { + fmt.Printf ("GetGhostIPAddresses Responded with %+v\n", getIPAddressesResponse); + } +} + +func TestGetAllIPAddresses(t *testing.T){ + fmt.Println("Test: GetAllIPAddresses") + req, err := http.NewRequest(http.MethodGet, cns.GetAllIPAddressesPath, nil) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + var getIPAddressesResponse cns.GetIPAddressesResponse + err = decodeResponse(w, &getIPAddressesResponse) + + if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { + t.Errorf("GetAllIPAddresses response is invalid %+v", getIPAddressesResponse) + } else { + fmt.Printf ("GetAllIPAddresses Responded with %+v\n", getIPAddressesResponse); + } +} + diff --git a/cns/service.go b/cns/service.go new file mode 100644 index 0000000000..cd22fcc163 --- /dev/null +++ b/cns/service.go @@ -0,0 +1,103 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package cns + +import ( + "net/http" + "net/url" + + "github.com/Azure/azure-container-networking/common" + "github.com/Azure/azure-container-networking/log" +) + +const ( + // Default CNS server URL. + defaultAPIServerURL = "tcp://localhost:10090" + genericData = "com.microsoft.azure.network.generic" +) + +// Service defines Container Networking Service. +type Service struct { + *common.Service + EndpointType string + Listener *common.Listener +} + +// NewService creates a new Service object. +func NewService(name, version string) (*Service, error) { + service, err := common.NewService(name, version) + + if err != nil { + return nil, err + } + + return &Service{ + Service: service, + }, nil +} + +// GetAPIServerURL returns the API server URL. +func (service *Service) getAPIServerURL() string { + urls, _ := service.GetOption(common.OptAPIServerURL).(string) + if urls == "" { + urls = defaultAPIServerURL + } + + return urls +} + +// Initialize initializes the service and starts the listener. +func (service *Service) Initialize(config *common.ServiceConfig) error { + log.Debugf("[Azure CNS] Going to initialize a service with config: %+v", config) + + // Initialize the base service. + service.Service.Initialize(config) + + // Initialize the listener. + if config.Listener == nil { + // Fetch and parse the API server URL. + u, err := url.Parse(service.getAPIServerURL()) + if err != nil { + return err + } + + // Create the listener. + listener, err := common.NewListener(u) + if err != nil { + return err + } + + // Start the listener. + err = listener.Start(config.ErrChan) + if err != nil { + return err + } + + config.Listener = listener + } + + service.Listener = config.Listener + + log.Debugf("[Azure CNS] Successfully initialized a service with config: %+v", config) + return nil +} + +// Uninitialize cleans up the plugin. +func (service *Service) Uninitialize() { + service.Listener.Stop() + service.Service.Uninitialize() +} + +// ParseOptions returns generic options from a libnetwork request. +func (service *Service) ParseOptions(options OptionMap) OptionMap { + opt, _ := options[genericData].(OptionMap) + return opt +} + +// SendErrorResponse sends and logs an error response. +func (service *Service) SendErrorResponse(w http.ResponseWriter, errMsg error) { + resp := errorResponse{errMsg.Error()} + err := service.Listener.Encode(w, &resp) + log.Response(service.Name, &resp, err) +} diff --git a/common/service.go b/common/service.go new file mode 100644 index 0000000000..179617993c --- /dev/null +++ b/common/service.go @@ -0,0 +1,79 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package common + +import ( + "errors" + "github.com/Azure/azure-container-networking/store" + "github.com/Azure/azure-container-networking/log" +) + +// Service implements behavior common to all services. +type Service struct { + Name string + Version string + Options map[string]interface{} + ErrChan chan error + Store store.KeyValueStore +} + +// ServiceAPI defines base interface. +type ServiceAPI interface { + Start(*ServiceConfig) error + Stop() + GetOption(string) interface{} + SetOption(string, interface{}) +} + +// ServiceConfig specifies common configuration. +type ServiceConfig struct { + Name string + Version string + Listener *Listener + ErrChan chan error + Store store.KeyValueStore +} + +// NewService creates a new Service object. +func NewService(name, version string) (*Service, error) { + + log.Debugf("[Azure CNS] Going to create a service object with name: %v. version: %v.", name, version) + svc := &Service{ + Name: name, + Version: version, + Options: make(map[string]interface{}), + } + log.Debugf("[Azure CNS] Finished creating service object with name: %v. version: %v.", name, version) + return svc, nil +} + +// Initialize initializes the service. +func (service *Service) Initialize(config *ServiceConfig) error { + if(config == nil){ + err := "[Azure CNS Errror] Initialize called with nil ServiceConfig." + log.Printf(err) + return errors.New(err) + } + + log.Debugf("[Azure CNS] Going to initialize the service: %+v with config: %+v.", service, config) + service.ErrChan = config.ErrChan + service.Store = config.Store + service.Version = config.Version + log.Debugf("[Azure CNS] nitialized service: %+v with config: %+v.", service, config) + return nil +} + +// Uninitialize cleans up the service. +func (service *Service) Uninitialize() { +} + +// GetOption gets the option value for the given key. +func (service *Service) GetOption(key string) interface{} { + return service.Options[key] +} + +// SetOption sets the option value for the given key. +func (service *Service) SetOption(key string, value interface{}) { + service.Options[key] = value +} From 15ddd1f1c2f3b7c9b0656551a59fbcc4062c3dc8 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Wed, 7 Jun 2017 15:26:28 -0700 Subject: [PATCH 11/34] Integrate CNS with Docker --- cns/client/examples.go | 25 +++ cns/dockerclient/api.go | 36 ++++ cns/dockerclient/dockerclient.go | 146 +++++++++++++++++ cns/dockerclient/dockerclient_linux.go | 10 ++ cns/dockerclient/dockerclient_windows.go | 10 ++ cns/imdsclient/api.go | 47 ++++++ cns/imdsclient/imdsclient.go | 67 ++++++++ cns/main/main.go | 18 +- cns/restserver/api.go | 18 ++ cns/restserver/{httprest.go => restserver.go} | 155 ++++++++++++++++-- .../{httprest_test.go => restserver_test.go} | 32 +++- cns/service.go | 5 +- common/service.go | 30 ++-- 13 files changed, 559 insertions(+), 40 deletions(-) create mode 100644 cns/dockerclient/api.go create mode 100644 cns/dockerclient/dockerclient.go create mode 100644 cns/dockerclient/dockerclient_linux.go create mode 100644 cns/dockerclient/dockerclient_windows.go create mode 100644 cns/imdsclient/api.go create mode 100644 cns/imdsclient/imdsclient.go create mode 100644 cns/restserver/api.go rename cns/restserver/{httprest.go => restserver.go} (61%) rename cns/restserver/{httprest_test.go => restserver_test.go} (91%) diff --git a/cns/client/examples.go b/cns/client/examples.go index e7d57c9807..e1072ead63 100644 --- a/cns/client/examples.go +++ b/cns/client/examples.go @@ -63,6 +63,30 @@ func createNetwork() error{ return nil } +func deleteNetwork() error{ + netRequest := cns.DeleteNetworkRequest{NetworkName:"azurenet"} + netRequestJSON := new(bytes.Buffer) + json.NewEncoder(netRequestJSON).Encode(netRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.DeleteNetworkPath, + "application/json; charset=utf-8", + netRequestJSON) + if err != nil { + fmt.Printf("Error received in DeleteNetwork post: %v ", err.Error()) + return err + } + + var deleteNetworkResponse cns.Response + err = json.NewDecoder(res.Body).Decode(&deleteNetworkResponse) + if err != nil{ + fmt.Printf("Error received in decoding response from DeleteNetwork: %v ", err.Error()) + return err + } + fmt.Printf("Response for DeleteNetwork: %+v\n", deleteNetworkResponse) + return nil +} + func reserveIPAddress() error{ reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID:"ip01"} reserveIPRequestJSON := new(bytes.Buffer) @@ -214,6 +238,7 @@ func getAllIPAddresses() error{ func main() { setEnvironment() createNetwork() + deleteNetwork() reserveIPAddress() releaseIPAddress() getAvailableIPAddresses() diff --git a/cns/dockerclient/api.go b/cns/dockerclient/api.go new file mode 100644 index 0000000000..06a1be9303 --- /dev/null +++ b/cns/dockerclient/api.go @@ -0,0 +1,36 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package dockerclient + +import ( +) + +const ( + createNetworkPath = "/Networks/Create" + inspectNetworkPath = "/Networks/" +) + +// Config describes subnet/gateway for ipam +type Config struct { + Subnet string + Gateway string +} + +// IPAM describes ipam details +type IPAM struct { + Driver string + Config *Config + Options map[string] string +} + +// NetworkConfiguration describes configuration for docker entwork create +type NetworkConfiguration struct +{ + Name string + Driver string + IPAM *IPAM + Internal bool + Options map[string]string + Labels map[string]string +} \ No newline at end of file diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go new file mode 100644 index 0000000000..48072cb45f --- /dev/null +++ b/cns/dockerclient/dockerclient.go @@ -0,0 +1,146 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package dockerclient + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + + "github.com/Azure/azure-container-networking/cns/imdsclient" + "github.com/Azure/azure-container-networking/log" +) + +const ( + defaultDockerConnectionURL = "http://127.0.0.1:2375" + defaultIpamPlugin = "azure-vnet-ipam" +) + +// DockerClient specifies a client to connect to docker +type DockerClient struct { + connectionURL string + imdsClient *imdsclient.ImdsClient +} + +// NewDockerClient create a new docker client +func NewDockerClient(url string) (*DockerClient, error) { + return &DockerClient{ + connectionURL: url, + imdsClient: &imdsclient.ImdsClient{}, + }, nil +} + +// NewDefaultDockerClient create a new docker client +func NewDefaultDockerClient() (*DockerClient, error) { + return &DockerClient{ + connectionURL: defaultDockerConnectionURL, + imdsClient: &imdsclient.ImdsClient{}, + }, nil +} + +// NetworkExists tries to retrieve a network from docker (if it exists) +func (dockerClient *DockerClient) NetworkExists(networkName string) error { + log.Printf("[Azure CNS] NetworkExists") + + res, err := http.Post( + dockerClient.connectionURL+inspectNetworkPath+networkName, + "application/json; charset=utf-8", + nil) + + if err != nil { + log.Printf("[Azure CNS] Error received from http Post for docker network inspect %v %v", networkName, err.Error()) + return err + } + + // network exists + if res.StatusCode == 200 { + return nil + } + + // network not found + if res.StatusCode == 404 { + return fmt.Errorf("Network not found") + } + + return fmt.Errorf("Unknown return code from docker inspect %d", res.StatusCode) +} + +// CreateNetwork creates a network using docker network create +func (dockerClient *DockerClient) CreateNetwork(networkName string) error { + log.Printf("[Azure CNS] CreateNetwork") + + primaryNic, err := dockerClient.imdsClient.GetPrimaryInterfaceInfoFromHost() + if err != nil { + return err + } + + config := &Config{ + Subnet: primaryNic.Subnet, + Gateway: primaryNic.Gateway, + } + ipamConfig := &IPAM{ + Driver: defaultIpamPlugin, + Config: config, + } + netConfig := &NetworkConfiguration{ + Name: networkName, + Driver: defaultNetworkPlugin, + IPAM: ipamConfig, + Internal: true, + } + + log.Printf("[Azure CNS] Going to create network with config: %+v", netConfig) + + netConfigJSON := new(bytes.Buffer) + err = json.NewEncoder(netConfigJSON).Encode(netConfig) + if err != nil { + return err + } + + res, err := http.Post( + dockerClient.connectionURL+createNetworkPath, + "application/json; charset=utf-8", + netConfigJSON) + + if err != nil { + log.Printf("[Azure CNS] Error received from http Post for docker network create %v", networkName) + return err + } + if res.StatusCode != 200 { + return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v", res.StatusCode) + } + + return nil +} + +// DeleteNetwork creates a network using docker network create +func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { + log.Printf("[Azure CNS] DeleteNetwork") + + url := dockerClient.connectionURL+inspectNetworkPath+networkName + req, err := http.NewRequest( + "DELETE", + url, + nil) + if err != nil { + log.Printf("[Azure CNS] Error received while creating http DELETE request for network delete %v %v", networkName, err.Error()) + return err + } + req.Header.Set("Content-Type", "application/json; charset=utf-8") + client := &http.Client{} + res, err := client.Do(req) + + // network successfully deleted + if res.StatusCode == 204 { + return nil + } + + // network not found + if res.StatusCode == 404 { + return fmt.Errorf("[Azure CNS] Network not found %v", networkName) + } + + return fmt.Errorf("[Azure CNS] Unknown return code from docker delete network %v: ret = %d", networkName, res.StatusCode) +} diff --git a/cns/dockerclient/dockerclient_linux.go b/cns/dockerclient/dockerclient_linux.go new file mode 100644 index 0000000000..18eadf5eee --- /dev/null +++ b/cns/dockerclient/dockerclient_linux.go @@ -0,0 +1,10 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +// +build linux + +package dockerclient + +const ( + defaultNetworkPlugin = "azure-vnet" +) diff --git a/cns/dockerclient/dockerclient_windows.go b/cns/dockerclient/dockerclient_windows.go new file mode 100644 index 0000000000..94ac90796d --- /dev/null +++ b/cns/dockerclient/dockerclient_windows.go @@ -0,0 +1,10 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +// +build windows + +package dockerclient + +const ( + defaultNetworkPlugin = "l2tunnel" +) diff --git a/cns/imdsclient/api.go b/cns/imdsclient/api.go new file mode 100644 index 0000000000..2b7d9b8c5a --- /dev/null +++ b/cns/imdsclient/api.go @@ -0,0 +1,47 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package imdsclient + +import ( + "encoding/xml" +) + +const ( + hostQueryURL = "http://169.254.169.254/machine/plugins?comp=nmagent&type=getinterfaceinfov1" +) + +// ImdsClient cna be used to connect to VM Host agent in Azure +type ImdsClient struct { + primaryInterface *InterfaceInfo +} + +// InterfaceInfo specifies the information about an interface as retunred by Host Agent +type InterfaceInfo struct { + Subnet string + Gateway string + IsPrimary bool + PrimaryIP string + SecondaryIPs[] string +} + +// Azure host agent XML document format. +type xmlDocument struct { + XMLName xml.Name `xml:"Interfaces"` + Interface []struct { + XMLName xml.Name `xml:"Interface"` + MacAddress string `xml:"MacAddress,attr"` + IsPrimary bool `xml:"IsPrimary,attr"` + + IPSubnet []struct { + XMLName xml.Name `xml:"IPSubnet"` + Prefix string `xml:"Prefix,attr"` + + IPAddress []struct { + XMLName xml.Name `xml:"IPAddress"` + Address string `xml:"Address,attr"` + IsPrimary bool `xml:"IsPrimary,attr"` + } + } + } +} \ No newline at end of file diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go new file mode 100644 index 0000000000..07213dc055 --- /dev/null +++ b/cns/imdsclient/imdsclient.go @@ -0,0 +1,67 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package imdsclient + +import ( + "fmt" + "strings" + "encoding/xml" + "net/http" + + "github.com/Azure/azure-container-networking/log" +) + +// GetPrimaryInterfaceInfoFromHost retrieves subnet and gateway of primary NIC from Host +func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, error) { + log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromHost") + interfaceInfo := &InterfaceInfo{} + resp, err := http.Get(hostQueryURL) + if(err != nil){ + return nil, err + } + + log.Printf("[Azure CNS] Response received from NMAgent: %v", resp.Body) + + var doc xmlDocument + decoder := xml.NewDecoder(resp.Body) + err = decoder.Decode(&doc) + if err != nil { + return nil, err + } + + foundInterface := false + + // For each interface... + for _, i := range doc.Interface { + // Find primary Interface + if i.IsPrimary { + // Get the first subnet + for _, s := range i.IPSubnet { + interfaceInfo.Subnet = s.Prefix + malformedSubnetError := fmt.Errorf("Malformed subnet received from host %s", s.Prefix) + + st := strings.Split(s.Prefix, "/") + if(len(st) != 2){ + return nil, malformedSubnetError + } + + ip := strings.Split(st[0], ".") + if(len(ip) != 4){ + return nil, malformedSubnetError + } + + interfaceInfo.Gateway = fmt.Sprintf("%s.%s.%s.1", ip[0], ip[1], ip[2]) + foundInterface = true + break; + } + break; + } + } + var er error + er = nil + if (!foundInterface) { + er = fmt.Errorf("Unable to find primary NIC") + } + return interfaceInfo, er +} \ No newline at end of file diff --git a/cns/main/main.go b/cns/main/main.go index 7a0ebc79af..cb8c1cc277 100644 --- a/cns/main/main.go +++ b/cns/main/main.go @@ -92,22 +92,24 @@ func main() { config.Name = name // Create a channel to receive unhandled errors from CNS. - config.ErrChan = make(chan error, 1) + config.ErrChan = make(chan error, 1) - // Create CNS object. - httpRestService, err := restserver.NewHTTPRestService(&config) + // Create the key value store. + var err error + config.Store, err = store.NewJsonFileStore(platform.RuntimePath + name + ".json") if err != nil { - fmt.Printf("Failed to create CNS object, err:%v.\n", err) + fmt.Printf("Failed to create store: %v\n", err) return } - // Create the key value store. - config.Store, err = store.NewJsonFileStore(platform.RuntimePath + name + ".json") + // Create CNS object. + httpRestService, err := restserver.NewHTTPRestService(&config) if err != nil { - fmt.Printf("Failed to create store: %v\n", err) + fmt.Printf("Failed to create CNS object, err:%v.\n", err) return } + // Create logging provider. log.SetName(name) log.SetLevel(logLevel) @@ -119,7 +121,7 @@ func main() { // Log platform information. log.Printf("Running on %v", platform.GetOSInfo()) - + // Set CNS options. httpRestService.SetOption(common.OptAPIServerURL, url) diff --git a/cns/restserver/api.go b/cns/restserver/api.go new file mode 100644 index 0000000000..47869100c7 --- /dev/null +++ b/cns/restserver/api.go @@ -0,0 +1,18 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package restserver + +// Container Network Service remote API Contract +const ( + Success = 0 + UnsupportedNetworkType = 1 + InvalidParameter = 2 + UnsupportedEnvironment = 3 + UnreachableHost = 4 + ReservationNotFound = 5 + MalformedSubnet = 8 + UnreachableDockerDaemon = 9 + UnspecifiedNetworkName = 10 + UnexpectedError = 99 +) diff --git a/cns/restserver/httprest.go b/cns/restserver/restserver.go similarity index 61% rename from cns/restserver/httprest.go rename to cns/restserver/restserver.go index 7c6533172a..e651aa2685 100644 --- a/cns/restserver/httprest.go +++ b/cns/restserver/restserver.go @@ -4,16 +4,35 @@ package restserver import ( + "time" "net/http" "github.com/Azure/azure-container-networking/cns" + "github.com/Azure/azure-container-networking/cns/dockerclient" "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/store" + "fmt" +) + +const ( + // Key against which CNS state is persisted. + storeKey = "ContainerNetworkService" ) // httpRestService represents http listener for CNS - Container Networking Service. type httpRestService struct { *cns.Service + dockerClient *dockerclient.DockerClient + store store.KeyValueStore + state httpRestServiceState +} + +// httpRestServiceState contrains the state we would like to persist +type httpRestServiceState struct { + Location string + NetworkType string + TimeStamp time.Time } // HTTPService describes the min API interface that every service should have. @@ -23,13 +42,20 @@ type HTTPService interface { // NewHTTPRestService creates a new HTTP Service object. func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { - service, err := cns.NewService(config.Name, config.Version) + service, err := cns.NewService(config.Name, config.Version, config.Store) if err != nil { return nil, err } + dc, err := dockerclient.NewDefaultDockerClient() + if(err != nil){ + return nil, err + } + return &httpRestService{ Service: service, + store: service.Service.Store, + dockerClient: dc, }, nil } @@ -38,7 +64,7 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { err := service.Initialize(config) if err != nil { - log.Printf("[Azure CNS] Failed to initialize base service, err:%v.", err) + log.Printf("[Azure CNS] Failed to initialize base service, err:%v.", err) return err } @@ -57,19 +83,19 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { listener.AddHandler(cns.GetAllIPAddressesPath, service.getAllIPAddresses) listener.AddHandler(cns.GetHealthReportPath, service.getHealthReport) - log.Printf("[Azure CNS] Listening.") - + log.Printf("[Azure CNS] Listening.") return nil } // Stop stops the CNS. func (service *httpRestService) Stop() { service.Uninitialize() - log.Printf("[Azure CNS] Service stopped.") + log.Printf("[Azure CNS] Service stopped.") } // Handles requests to set the environment type. func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] setEnvironment") var req cns.SetEnvironmentRequest err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) @@ -78,10 +104,11 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re } switch r.Method { - case "GET": - log.Printf("[Azure CNS] GET received for SetEnvironment.") case "POST": - log.Printf("[Azure CNS] POST received for SetEnvironment.") + log.Printf("[Azure CNS] POST received for SetEnvironment.") + service.state.Location = req.Location + service.state.NetworkType = req.NetworkType + service.saveState() default: } @@ -92,8 +119,10 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re // Handles CreateNetwork requests. func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] createNetwork") var req cns.CreateNetworkRequest - + returnCode := 0 + returnMessage := "" err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) if err != nil { @@ -101,18 +130,40 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req } switch r.Method { case "POST": + dc := service.dockerClient + err := dc.NetworkExists(req.NetworkName) + + // Network does not exist + if(err != nil) { + log.Printf("[Azure CNS] Goign to create network with name %v", req.NetworkName) + err := dc.CreateNetwork(req.NetworkName) + if(err != nil) { + returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) + returnCode = UnexpectedError + } + } else { + log.Printf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) + } + default: + returnMessage = "[Azure CNS] Error. CreateNetwork did not receive a POST." + returnCode = InvalidParameter } - resp := &cns.Response{ReturnCode: 0} + resp := &cns.Response{ + ReturnCode: returnCode, + Message: returnMessage, + } err = service.Listener.Encode(w, &resp) log.Response(service.Name, resp, err) } // Handles DeleteNetwork requests. func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] deleteNetwork") var req cns.DeleteNetworkRequest - + returnCode := 0 + returnMessage := "" err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) if err != nil { @@ -120,16 +171,37 @@ func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Req } switch r.Method { case "POST": + dc := service.dockerClient + err := dc.NetworkExists(req.NetworkName) + + // Network does exist + if(err == nil) { + log.Printf("[Azure CNS] Goign to delete network with name %v", req.NetworkName) + err := dc.DeleteNetwork(req.NetworkName) + if(err != nil) { + returnMessage = fmt.Sprintf("[Azure CNS] Error. DeleteNetwork failed %v.", err.Error()) + returnCode = UnexpectedError + } + } else { + log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v", req.NetworkName) + } + default: + returnMessage = "[Azure CNS] Error. DeleteNetwork did not receive a POST." + returnCode = InvalidParameter } - resp := &cns.Response{ReturnCode: 0} + resp := &cns.Response{ + ReturnCode: returnCode, + Message: returnMessage, + } err = service.Listener.Encode(w, &resp) log.Response(service.Name, resp, err) } // Handles ip reservation requests. func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] reserveIPAddress") var req cns.ReserveIPAddressRequest err := service.Listener.Decode(w, r, &req) @@ -150,6 +222,7 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. // Handles release ip reservation requests. func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] releaseIPAddress") var req cns.ReleaseIPAddressRequest err := service.Listener.Decode(w, r, &req) @@ -169,6 +242,7 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. // Retrieves the host local ip address. func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getHostLocalIP") log.Request(service.Name, "getHostLocalIP", nil) switch r.Method { case "GET": @@ -185,6 +259,7 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re // Handles ip address utiliztion requests. func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getIPAddressUtilization") log.Request(service.Name, "getIPAddressUtilization", nil) switch r.Method { case "GET": @@ -201,6 +276,7 @@ func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r // Handles retrieval of ip addresses that are available to be reserved from ipam driver. func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getAvailableIPAddresses") log.Request(service.Name, "getAvailableIPAddresses", nil) switch r.Method { case "GET": @@ -214,6 +290,7 @@ func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r // Handles retrieval of reserved ip addresses from ipam driver. func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getReservedIPAddresses") log.Request(service.Name, "getReservedIPAddresses", nil) switch r.Method { case "GET": @@ -227,6 +304,7 @@ func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r // Handles retrieval of ghost ip addresses from ipam driver. func (service *httpRestService) getGhostIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getGhostIPAddresses") log.Request(service.Name, "getGhostIPAddresses", nil) switch r.Method { case "GET": @@ -238,8 +316,9 @@ func (service *httpRestService) getGhostIPAddresses(w http.ResponseWriter, r *ht log.Response(service.Name, ipResp, err) } -// Handles retrieval of all ip addresses from ipam driver. +// getAllIPAddresses retrieves all ip addresses from ipam driver. func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getAllIPAddresses") log.Request(service.Name, "getAllIPAddresses", nil) switch r.Method { case "GET": @@ -253,8 +332,8 @@ func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http // Handles health report requests. func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.Request) { - log.Request(service.Name, "getHealthReport", nil) - + log.Printf("[Azure CNS] getHealthReport") + log.Request(service.Name, "getHealthReport", nil) switch r.Method { case "GET": default: @@ -264,3 +343,49 @@ func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.R err := service.Listener.Encode(w, &resp) log.Response(service.Name, resp, err) } + +// saveState writes CNS state to persistent store. +func (service *httpRestService) saveState() error { + log.Printf("[Azure CNS] saveState") + // Skip if a store is not provided. + if service.store == nil { + log.Printf("[Azure CNS] store not initialized.") + return nil + } + + // Update time stamp. + service.state.TimeStamp = time.Now() + err := service.store.Write(storeKey, &service.state) + if err == nil { + log.Printf("[Azure CNS] State saved successfully.\n") + } else { + log.Printf("[Azure CNS] Failed to save state., err:%v\n", err) + } + return err +} + +// restoreState restores CNS state from persistent store. +func (service *httpRestService) restoreState() error { + log.Printf("[Azure CNS] restoreState") + // Skip if a store is not provided. + if service.store == nil { + log.Printf("[Azure CNS] store not initialized.") + return nil + } + + // Read any persisted state. + err := service.store.Read(storeKey, &service.state) + if err != nil { + if err == store.ErrKeyNotFound { + // Nothing to restore. + log.Printf("[Azure CNS] No state to restore.\n") + return nil + } + + log.Printf("[Azure CNS] Failed to restore state, err:%v\n", err) + return err + } + + log.Printf("[Azure CNS] Restored state, %+v\n", service.state) + return nil +} diff --git a/cns/restserver/httprest_test.go b/cns/restserver/restserver_test.go similarity index 91% rename from cns/restserver/httprest_test.go rename to cns/restserver/restserver_test.go index 4a4486bc30..74e05b7ce4 100644 --- a/cns/restserver/httprest_test.go +++ b/cns/restserver/restserver_test.go @@ -79,7 +79,7 @@ func TestCreateNetwork(t *testing.T) { json.NewEncoder(&body).Encode(info) - req, err := http.NewRequest(http.MethodGet, cns.CreateNetworkPath, &body) + req, err := http.NewRequest(http.MethodPost, cns.CreateNetworkPath, &body) if err != nil { t.Fatal(err) } @@ -96,6 +96,36 @@ func TestCreateNetwork(t *testing.T) { } } +// Tests CreateNetwork functionality. +func TestDeleteNetwork(t *testing.T) { + fmt.Println("Test: DeleteNetwork") + + var body bytes.Buffer + var resp cns.Response + + info := &cns.DeleteNetworkRequest{ + NetworkName: "azurenet", + } + + json.NewEncoder(&body).Encode(info) + + req, err := http.NewRequest(http.MethodPost, cns.DeleteNetworkPath, &body) + if err != nil { + t.Fatal(err) + } + + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + + err = decodeResponse(w, &resp) + + if err != nil || resp.ReturnCode != 0 { + t.Errorf("DeleteNetwork response is invalid %+v", resp) + } else { + fmt.Printf ("DeleteNetwork Responded with %+v\n", resp); + } +} + func TestSetEnvironment(t *testing.T) { fmt.Println("Test: SetEnvironment") diff --git a/cns/service.go b/cns/service.go index cd22fcc163..91639b09f0 100644 --- a/cns/service.go +++ b/cns/service.go @@ -9,6 +9,7 @@ import ( "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/store" ) const ( @@ -25,8 +26,8 @@ type Service struct { } // NewService creates a new Service object. -func NewService(name, version string) (*Service, error) { - service, err := common.NewService(name, version) +func NewService(name, version string, store store.KeyValueStore) (*Service, error) { + service, err := common.NewService(name, version, store) if err != nil { return nil, err diff --git a/common/service.go b/common/service.go index 179617993c..ff3cf10922 100644 --- a/common/service.go +++ b/common/service.go @@ -5,8 +5,9 @@ package common import ( "errors" - "github.com/Azure/azure-container-networking/store" + "github.com/Azure/azure-container-networking/log" + "github.com/Azure/azure-container-networking/store" ) // Service implements behavior common to all services. @@ -28,39 +29,40 @@ type ServiceAPI interface { // ServiceConfig specifies common configuration. type ServiceConfig struct { - Name string - Version string + Name string + Version string Listener *Listener ErrChan chan error Store store.KeyValueStore } // NewService creates a new Service object. -func NewService(name, version string) (*Service, error) { - - log.Debugf("[Azure CNS] Going to create a service object with name: %v. version: %v.", name, version) +func NewService(name, version string, store store.KeyValueStore) (*Service, error) { + + log.Debugf("[Azure CNS] Going to create a service object with name: %v. version: %v.", name, version) svc := &Service{ - Name: name, - Version: version, - Options: make(map[string]interface{}), - } + Name: name, + Version: version, + Options: make(map[string]interface{}), + Store: store, + } log.Debugf("[Azure CNS] Finished creating service object with name: %v. version: %v.", name, version) return svc, nil } // Initialize initializes the service. func (service *Service) Initialize(config *ServiceConfig) error { - if(config == nil){ + if config == nil { err := "[Azure CNS Errror] Initialize called with nil ServiceConfig." log.Printf(err) return errors.New(err) } - - log.Debugf("[Azure CNS] Going to initialize the service: %+v with config: %+v.", service, config) + + log.Debugf("[Azure CNS] Going to initialize the service: %+v with config: %+v.", service, config) service.ErrChan = config.ErrChan service.Store = config.Store service.Version = config.Version - log.Debugf("[Azure CNS] nitialized service: %+v with config: %+v.", service, config) + log.Debugf("[Azure CNS] nitialized service: %+v with config: %+v.", service, config) return nil } From a890186d5a41bae8bb209cd3cb9de612ef29deee Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Thu, 8 Jun 2017 10:45:31 -0700 Subject: [PATCH 12/34] Update docker API paths and IPAM config --- cns/client/examples.go | 4 +- cns/dockerclient/api.go | 26 ++++----- cns/dockerclient/dockerclient.go | 39 +++++++++----- cns/imdsclient/api.go | 4 +- cns/imdsclient/imdsclient.go | 8 +-- cns/restserver/api.go | 2 +- cns/restserver/restserver.go | 91 +++++++++++++++++++++----------- 7 files changed, 106 insertions(+), 68 deletions(-) diff --git a/cns/client/examples.go b/cns/client/examples.go index e1072ead63..be3f7ee561 100644 --- a/cns/client/examples.go +++ b/cns/client/examples.go @@ -238,7 +238,7 @@ func getAllIPAddresses() error{ func main() { setEnvironment() createNetwork() - deleteNetwork() + /*deleteNetwork() reserveIPAddress() releaseIPAddress() getAvailableIPAddresses() @@ -246,5 +246,5 @@ func main() { getGhostIPAddresses() getAllIPAddresses() getIPAddressUtilization() - getHostLocalIP() + getHostLocalIP()*/ } diff --git a/cns/dockerclient/api.go b/cns/dockerclient/api.go index 06a1be9303..4cb8acb7e7 100644 --- a/cns/dockerclient/api.go +++ b/cns/dockerclient/api.go @@ -7,30 +7,30 @@ import ( ) const ( - createNetworkPath = "/Networks/Create" - inspectNetworkPath = "/Networks/" + createNetworkPath = "/networks/create" + inspectNetworkPath = "/networks/" ) -// Config describes subnet/gateway for ipam +// Config describes subnet/gateway for ipam. type Config struct { Subnet string - Gateway string } // IPAM describes ipam details type IPAM struct { Driver string - Config *Config - Options map[string] string + Config []Config } -// NetworkConfiguration describes configuration for docker entwork create -type NetworkConfiguration struct -{ +// NetworkConfiguration describes configuration for docker network create. +type NetworkConfiguration struct { Name string Driver string - IPAM *IPAM + IPAM IPAM Internal bool - Options map[string]string - Labels map[string]string -} \ No newline at end of file +} + +// DockerErrorResponse defines the error response retunred by docker. +type DockerErrorResponse struct { + message string +} diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go index 48072cb45f..5aed627118 100644 --- a/cns/dockerclient/dockerclient.go +++ b/cns/dockerclient/dockerclient.go @@ -15,16 +15,16 @@ import ( const ( defaultDockerConnectionURL = "http://127.0.0.1:2375" - defaultIpamPlugin = "azure-vnet-ipam" + defaultIpamPlugin = "azure-vnet" ) -// DockerClient specifies a client to connect to docker +// DockerClient specifies a client to connect to docker. type DockerClient struct { connectionURL string imdsClient *imdsclient.ImdsClient } -// NewDockerClient create a new docker client +// NewDockerClient create a new docker client. func NewDockerClient(url string) (*DockerClient, error) { return &DockerClient{ connectionURL: url, @@ -32,7 +32,7 @@ func NewDockerClient(url string) (*DockerClient, error) { }, nil } -// NewDefaultDockerClient create a new docker client +// NewDefaultDockerClient create a new docker client. func NewDefaultDockerClient() (*DockerClient, error) { return &DockerClient{ connectionURL: defaultDockerConnectionURL, @@ -40,7 +40,7 @@ func NewDefaultDockerClient() (*DockerClient, error) { }, nil } -// NetworkExists tries to retrieve a network from docker (if it exists) +// NetworkExists tries to retrieve a network from docker (if it exists). func (dockerClient *DockerClient) NetworkExists(networkName string) error { log.Printf("[Azure CNS] NetworkExists") @@ -56,18 +56,20 @@ func (dockerClient *DockerClient) NetworkExists(networkName string) error { // network exists if res.StatusCode == 200 { + log.Debugf("[Azure CNS] Network with name %v already exists. Docker return code: %v", networkName, res.StatusCode) return nil } // network not found if res.StatusCode == 404 { + log.Debugf("[Azure CNS] Network with name %v does not exist. Docker return code: %v", networkName, res.StatusCode) return fmt.Errorf("Network not found") } return fmt.Errorf("Unknown return code from docker inspect %d", res.StatusCode) } -// CreateNetwork creates a network using docker network create +// CreateNetwork creates a network using docker network create. func (dockerClient *DockerClient) CreateNetwork(networkName string) error { log.Printf("[Azure CNS] CreateNetwork") @@ -78,16 +80,17 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { config := &Config{ Subnet: primaryNic.Subnet, - Gateway: primaryNic.Gateway, } + configs := make([]Config, 1) + configs[0] = *config ipamConfig := &IPAM{ Driver: defaultIpamPlugin, - Config: config, + Config: configs, } netConfig := &NetworkConfiguration{ Name: networkName, Driver: defaultNetworkPlugin, - IPAM: ipamConfig, + IPAM: *ipamConfig, Internal: true, } @@ -108,14 +111,22 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { log.Printf("[Azure CNS] Error received from http Post for docker network create %v", networkName) return err } - if res.StatusCode != 200 { - return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v", res.StatusCode) + if res.StatusCode != 201 { + var createNetworkResponse DockerErrorResponse + err = json.NewDecoder(res.Body).Decode(&createNetworkResponse) + var ermsg string + ermsg = "" + if(err != nil){ + ermsg = err.Error() + } + return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v - %v - %v", res.StatusCode, createNetworkResponse.message, ermsg) + } return nil } -// DeleteNetwork creates a network using docker network create +// DeleteNetwork creates a network using docker network create. func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { log.Printf("[Azure CNS] DeleteNetwork") @@ -132,12 +143,12 @@ func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { client := &http.Client{} res, err := client.Do(req) - // network successfully deleted + // network successfully deleted. if res.StatusCode == 204 { return nil } - // network not found + // network not found. if res.StatusCode == 404 { return fmt.Errorf("[Azure CNS] Network not found %v", networkName) } diff --git a/cns/imdsclient/api.go b/cns/imdsclient/api.go index 2b7d9b8c5a..dec8d918fa 100644 --- a/cns/imdsclient/api.go +++ b/cns/imdsclient/api.go @@ -11,12 +11,12 @@ const ( hostQueryURL = "http://169.254.169.254/machine/plugins?comp=nmagent&type=getinterfaceinfov1" ) -// ImdsClient cna be used to connect to VM Host agent in Azure +// ImdsClient cna be used to connect to VM Host agent in Azure. type ImdsClient struct { primaryInterface *InterfaceInfo } -// InterfaceInfo specifies the information about an interface as retunred by Host Agent +// InterfaceInfo specifies the information about an interface as returned by Host Agent. type InterfaceInfo struct { Subnet string Gateway string diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go index 07213dc055..8351f82cf1 100644 --- a/cns/imdsclient/imdsclient.go +++ b/cns/imdsclient/imdsclient.go @@ -12,7 +12,7 @@ import ( "github.com/Azure/azure-container-networking/log" ) -// GetPrimaryInterfaceInfoFromHost retrieves subnet and gateway of primary NIC from Host +// GetPrimaryInterfaceInfoFromHost retrieves subnet and gateway of primary NIC from Host. func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, error) { log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromHost") interfaceInfo := &InterfaceInfo{} @@ -32,11 +32,11 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, foundInterface := false - // For each interface... + // For each interface. for _, i := range doc.Interface { - // Find primary Interface + // Find primary Interface. if i.IsPrimary { - // Get the first subnet + // Get the first subnet. for _, s := range i.IPSubnet { interfaceInfo.Subnet = s.Prefix malformedSubnetError := fmt.Errorf("Malformed subnet received from host %s", s.Prefix) diff --git a/cns/restserver/api.go b/cns/restserver/api.go index 47869100c7..8ef2e122d4 100644 --- a/cns/restserver/api.go +++ b/cns/restserver/api.go @@ -3,7 +3,7 @@ package restserver -// Container Network Service remote API Contract +// Container Network Service remote API Contract. const ( Success = 0 UnsupportedNetworkType = 1 diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index e651aa2685..458ee3de67 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -4,6 +4,7 @@ package restserver import ( + "fmt" "time" "net/http" @@ -12,7 +13,6 @@ import ( "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" - "fmt" ) const ( @@ -28,10 +28,11 @@ type httpRestService struct { state httpRestServiceState } -// httpRestServiceState contrains the state we would like to persist +// httpRestServiceState contains the state we would like to persist. type httpRestServiceState struct { - Location string - NetworkType string + Location string + NetworkType string + Initialized bool TimeStamp time.Time } @@ -107,7 +108,8 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re case "POST": log.Printf("[Azure CNS] POST received for SetEnvironment.") service.state.Location = req.Location - service.state.NetworkType = req.NetworkType + service.state.NetworkType = req.NetworkType + service.state.Initialized = true service.saveState() default: } @@ -120,40 +122,65 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re // Handles CreateNetwork requests. func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] createNetwork") - var req cns.CreateNetworkRequest + var err error returnCode := 0 returnMessage := "" - err := service.Listener.Decode(w, r, &req) - log.Request(service.Name, &req, err) - if err != nil { - return - } - switch r.Method { - case "POST": - dc := service.dockerClient - err := dc.NetworkExists(req.NetworkName) - - // Network does not exist - if(err != nil) { - log.Printf("[Azure CNS] Goign to create network with name %v", req.NetworkName) - err := dc.CreateNetwork(req.NetworkName) - if(err != nil) { - returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) - returnCode = UnexpectedError - } - } else { - log.Printf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) - } - - default: - returnMessage = "[Azure CNS] Error. CreateNetwork did not receive a POST." + + if(service.state.Initialized) { + + var req cns.CreateNetworkRequest + err = service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. Unable to decode input request.") returnCode = InvalidParameter + } else { + switch r.Method { + case "POST": + dc := service.dockerClient + err = dc.NetworkExists(req.NetworkName) + + // Network does not exist. + if(err != nil) { + switch service.state.NetworkType { + case "Underlay": + switch service.state.Location { + case "Azure": + log.Printf("[Azure CNS] Goign to create network with name %v.", req.NetworkName) + err = dc.CreateNetwork(req.NetworkName) + if(err != nil) { + returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) + returnCode = UnexpectedError + } + case "StandAlone": + returnMessage = fmt.Sprintf("[Azure CNS] Error. Underlay network is not supported in StandAlone environment. %v.", err.Error()) + returnCode = UnsupportedEnvironment + } + case "Overlay": + returnMessage = fmt.Sprintf("[Azure CNS] Error. Overlay support not yet available. %v.", err.Error()) + returnCode = UnsupportedEnvironment + } + } else { + log.Printf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) + } + + default: + returnMessage = "[Azure CNS] Error. CreateNetwork did not receive a POST." + returnCode = InvalidParameter + } + } + + } else { + returnMessage = fmt.Sprintf("[Azure CNS] Error. CNS is not yet initialized with environment.") + returnCode = UnsupportedEnvironment } resp := &cns.Response{ ReturnCode: returnCode, Message: returnMessage, } + err = service.Listener.Encode(w, &resp) log.Response(service.Name, resp, err) } @@ -176,14 +203,14 @@ func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Req // Network does exist if(err == nil) { - log.Printf("[Azure CNS] Goign to delete network with name %v", req.NetworkName) + log.Printf("[Azure CNS] Goign to delete network with name %v.", req.NetworkName) err := dc.DeleteNetwork(req.NetworkName) if(err != nil) { returnMessage = fmt.Sprintf("[Azure CNS] Error. DeleteNetwork failed %v.", err.Error()) returnCode = UnexpectedError } } else { - log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v", req.NetworkName) + log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v.", req.NetworkName) } default: From 5d3bf48b14ef9fb2346c27c45787705909bfd639 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Thu, 8 Jun 2017 14:47:02 -0700 Subject: [PATCH 13/34] Implement GetHostLocalIp --- cns/api.go | 2 +- cns/client/examples.go | 4 +-- cns/dockerclient/dockerclient.go | 6 ++-- cns/imdsclient/imdsclient.go | 23 ++++++++++++--- cns/restserver/api.go | 1 + cns/restserver/restserver.go | 49 ++++++++++++++++++++++++------- cns/restserver/restserver_test.go | 24 +++++++-------- 7 files changed, 77 insertions(+), 32 deletions(-) diff --git a/cns/api.go b/cns/api.go index 13d545b982..43546b31ef 100644 --- a/cns/api.go +++ b/cns/api.go @@ -19,7 +19,7 @@ const ( GetHealthReportPath = "/Network/Health" ) -// SetEnvironmentRequest describes the Request to set the environment in HNS. +// SetEnvironmentRequest describes the Request to set the environment in CNS. type SetEnvironmentRequest struct { Location string NetworkType string diff --git a/cns/client/examples.go b/cns/client/examples.go index be3f7ee561..e1072ead63 100644 --- a/cns/client/examples.go +++ b/cns/client/examples.go @@ -238,7 +238,7 @@ func getAllIPAddresses() error{ func main() { setEnvironment() createNetwork() - /*deleteNetwork() + deleteNetwork() reserveIPAddress() releaseIPAddress() getAvailableIPAddresses() @@ -246,5 +246,5 @@ func main() { getGhostIPAddresses() getAllIPAddresses() getIPAddressUtilization() - getHostLocalIP()*/ + getHostLocalIP() } diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go index 5aed627118..5a2eccec47 100644 --- a/cns/dockerclient/dockerclient.go +++ b/cns/dockerclient/dockerclient.go @@ -33,10 +33,10 @@ func NewDockerClient(url string) (*DockerClient, error) { } // NewDefaultDockerClient create a new docker client. -func NewDefaultDockerClient() (*DockerClient, error) { +func NewDefaultDockerClient(imdsClient *imdsclient.ImdsClient) (*DockerClient, error) { return &DockerClient{ connectionURL: defaultDockerConnectionURL, - imdsClient: &imdsclient.ImdsClient{}, + imdsClient: imdsClient, }, nil } @@ -77,7 +77,7 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { if err != nil { return err } - + config := &Config{ Subnet: primaryNic.Subnet, } diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go index 8351f82cf1..fe1a0e42ae 100644 --- a/cns/imdsclient/imdsclient.go +++ b/cns/imdsclient/imdsclient.go @@ -30,12 +30,14 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, return nil, err } - foundInterface := false + foundPrimaryInterface := false // For each interface. for _, i := range doc.Interface { // Find primary Interface. if i.IsPrimary { + interfaceInfo.IsPrimary = true + // Get the first subnet. for _, s := range i.IPSubnet { interfaceInfo.Subnet = s.Prefix @@ -52,16 +54,29 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, } interfaceInfo.Gateway = fmt.Sprintf("%s.%s.%s.1", ip[0], ip[1], ip[2]) - foundInterface = true + for _,ip := range s.IPAddress { + if ip.IsPrimary == true { + interfaceInfo.PrimaryIP = ip.Address + } + } + imdsClient.primaryInterface = interfaceInfo break; } + + foundPrimaryInterface = true break; } } var er error er = nil - if (!foundInterface) { + if (!foundPrimaryInterface) { er = fmt.Errorf("Unable to find primary NIC") - } + } return interfaceInfo, er +} + +// GetPrimaryInterfaceInfoFromMemory retrieves subnet and gateway of primary NIC that is saved in memory. +func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromMemory() (*InterfaceInfo, error) { + log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromMemory") + return imdsClient.primaryInterface, nil } \ No newline at end of file diff --git a/cns/restserver/api.go b/cns/restserver/api.go index 8ef2e122d4..af669a7985 100644 --- a/cns/restserver/api.go +++ b/cns/restserver/api.go @@ -14,5 +14,6 @@ const ( MalformedSubnet = 8 UnreachableDockerDaemon = 9 UnspecifiedNetworkName = 10 + NotFound = 14 UnexpectedError = 99 ) diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 458ee3de67..7760ecb483 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -10,6 +10,7 @@ import ( "github.com/Azure/azure-container-networking/cns" "github.com/Azure/azure-container-networking/cns/dockerclient" + "github.com/Azure/azure-container-networking/cns/imdsclient" "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" @@ -24,6 +25,7 @@ const ( type httpRestService struct { *cns.Service dockerClient *dockerclient.DockerClient + imdsClient *imdsclient.ImdsClient store store.KeyValueStore state httpRestServiceState } @@ -47,8 +49,8 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { if err != nil { return nil, err } - - dc, err := dockerclient.NewDefaultDockerClient() + imdsClient := &imdsclient.ImdsClient{} + dc, err := dockerclient.NewDefaultDockerClient(imdsClient) if(err != nil){ return nil, err } @@ -57,6 +59,7 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { Service: service, store: service.Service.Store, dockerClient: dc, + imdsClient: imdsClient, }, nil } @@ -78,11 +81,13 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { listener.AddHandler(cns.ReleaseIPAddressPath, service.releaseIPAddress) listener.AddHandler(cns.GetHostLocalIPPath, service.getHostLocalIP) listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) - listener.AddHandler(cns.GetAvailableIPAddressesPath, service.getAvailableIPAddresses) - listener.AddHandler(cns.GetReservedIPAddressesPath, service.getReservedIPAddresses) listener.AddHandler(cns.GetGhostIPAddressesPath, service.getGhostIPAddresses) - listener.AddHandler(cns.GetAllIPAddressesPath, service.getAllIPAddresses) - listener.AddHandler(cns.GetHealthReportPath, service.getHealthReport) + + // Below APIs are not handled in this iteration. + // listener.AddHandler(cns.GetAvailableIPAddressesPath, service.getAvailableIPAddresses) + // listener.AddHandler(cns.GetReservedIPAddressesPath, service.getReservedIPAddresses) + // listener.AddHandler(cns.GetAllIPAddressesPath, service.getAllIPAddresses) + // listener.AddHandler(cns.GetHealthReportPath, service.getHealthReport) log.Printf("[Azure CNS] Listening.") return nil @@ -267,18 +272,42 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. log.Response(service.Name, resp, err) } -// Retrieves the host local ip address. +// Retrieves the host local ip address. Containers can talk to host using this IP address. func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Request) { + var found bool + var errmsg string log.Printf("[Azure CNS] getHostLocalIP") - log.Request(service.Name, "getHostLocalIP", nil) + log.Request(service.Name, "getHostLocalIP", nil) + hostLocalIP := "0.0.0.0" switch r.Method { case "GET": + switch (service.state.NetworkType) { + case "Underlay": + if (service.imdsClient != nil) { + piface, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() + if err != nil { + hostLocalIP = piface.PrimaryIP + found = true; + } else { + log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) + } + } + case "Overlay": + errmsg = "[Azure-CNS] Overlay is not yet supported." + } default: + errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." } - resp := cns.Response{ReturnCode: 0} + + returnCode := 0 + if !found { + returnCode = NotFound + } + + resp := cns.Response{ReturnCode: returnCode, Message: errmsg} hostLocalIPResponse := &cns.HostLocalIPAddressResponse{ Response: resp, - IPAddress: "0.0.0.0", + IPAddress: hostLocalIP, } err := service.Listener.Encode(w, &hostLocalIPResponse) log.Response(service.Name, hostLocalIPResponse, err) diff --git a/cns/restserver/restserver_test.go b/cns/restserver/restserver_test.go index 74e05b7ce4..805832b86a 100644 --- a/cns/restserver/restserver_test.go +++ b/cns/restserver/restserver_test.go @@ -90,7 +90,7 @@ func TestCreateNetwork(t *testing.T) { err = decodeResponse(w, &resp) if err != nil || resp.ReturnCode != 0 { - t.Errorf("CreateNetwork response is invalid %+v", resp) + t.Errorf("CreateNetwork failed with response %+v", resp) } else { fmt.Printf ("CreateNetwork Responded with %+v\n", resp); } @@ -120,7 +120,7 @@ func TestDeleteNetwork(t *testing.T) { err = decodeResponse(w, &resp) if err != nil || resp.ReturnCode != 0 { - t.Errorf("DeleteNetwork response is invalid %+v", resp) + t.Errorf("DeleteNetwork failed with response %+v", resp) } else { fmt.Printf ("DeleteNetwork Responded with %+v\n", resp); } @@ -145,7 +145,7 @@ func TestSetEnvironment(t *testing.T) { err = decodeResponse(w, &resp) if err != nil || resp.ReturnCode != 0 { - t.Errorf("SetEnvironment response is invalid %+v", resp) + t.Errorf("SetEnvironment failed with response %+v", resp) } else { fmt.Printf ("SetEnvironment Responded with %+v\n", resp); } @@ -162,7 +162,7 @@ func TestReserveIPAddress(t *testing.T){ envRequestJSON := new(bytes.Buffer) json.NewEncoder(envRequestJSON).Encode(envRequest) - req, err := http.NewRequest(http.MethodGet, cns.SetEnvironmentPath, envRequestJSON) + req, err := http.NewRequest(http.MethodGet, cns.ReserveIPAddressPath, envRequestJSON) if err != nil { t.Fatal(err) } @@ -174,7 +174,7 @@ func TestReserveIPAddress(t *testing.T){ err = decodeResponse(w, &reserveIPAddressResponse) if err != nil || reserveIPAddressResponse.Response.ReturnCode != 0 { - t.Errorf("SetEnvironment response is invalid %+v", reserveIPAddressResponse) + t.Errorf("SetEnvironment failed with response %+v", reserveIPAddressResponse) } else { fmt.Printf ("SetEnvironment Responded with %+v\n", reserveIPAddressResponse); } @@ -198,7 +198,7 @@ func TestReleaseIPAddress(t *testing.T){ err = decodeResponse(w, &releaseIPAddressResponse) if err != nil || releaseIPAddressResponse.ReturnCode != 0 { - t.Errorf("SetEnvironment response is invalid %+v", releaseIPAddressResponse) + t.Errorf("SetEnvironment failed with response %+v", releaseIPAddressResponse) } else { fmt.Printf ("SetEnvironment Responded with %+v\n", releaseIPAddressResponse); } @@ -217,7 +217,7 @@ func TestGetIPAddressUtilization(t *testing.T){ err = decodeResponse(w, &iPAddressesUtilizationResponse) if err != nil || iPAddressesUtilizationResponse.Response.ReturnCode != 0 { - t.Errorf("GetIPAddressUtilization response is invalid %+v", iPAddressesUtilizationResponse) + t.Errorf("GetIPAddressUtilization failed with response %+v", iPAddressesUtilizationResponse) } else { fmt.Printf ("GetIPAddressUtilization Responded with %+v\n", iPAddressesUtilizationResponse); } @@ -236,7 +236,7 @@ func TestGetHostLocalIP(t *testing.T){ err = decodeResponse(w, &hostLocalIPAddressResponse) if err != nil || hostLocalIPAddressResponse.Response.ReturnCode != 0 { - t.Errorf("GetHostLocalIP response is invalid %+v", hostLocalIPAddressResponse) + t.Errorf("GetHostLocalIP failed with response %+v", hostLocalIPAddressResponse) } else { fmt.Printf ("GetHostLocalIP Responded with %+v\n", hostLocalIPAddressResponse); } @@ -255,7 +255,7 @@ func TestGetAvailableIPAddresses(t *testing.T){ err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetAvailableIPAddresses response is invalid %+v", getIPAddressesResponse) + t.Errorf("GetAvailableIPAddresses failed with response %+v", getIPAddressesResponse) } else { fmt.Printf ("GetAvailableIPAddresses Responded with %+v\n", getIPAddressesResponse); } @@ -274,7 +274,7 @@ func TestGetReservedIPAddresses(t *testing.T){ err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetReservedIPAddresses response is invalid %+v", getIPAddressesResponse) + t.Errorf("GetReservedIPAddresses failed with response %+v", getIPAddressesResponse) } else { fmt.Printf ("GetReservedIPAddresses Responded with %+v\n", getIPAddressesResponse); } @@ -293,7 +293,7 @@ func TestGetGhostIPAddresses(t *testing.T){ err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetGhostIPAddresses response is invalid %+v", getIPAddressesResponse) + t.Errorf("GetGhostIPAddresses failed with response %+v", getIPAddressesResponse) } else { fmt.Printf ("GetGhostIPAddresses Responded with %+v\n", getIPAddressesResponse); } @@ -312,7 +312,7 @@ func TestGetAllIPAddresses(t *testing.T){ err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetAllIPAddresses response is invalid %+v", getIPAddressesResponse) + t.Errorf("GetAllIPAddresses failed with response %+v", getIPAddressesResponse) } else { fmt.Printf ("GetAllIPAddresses Responded with %+v\n", getIPAddressesResponse); } From bd68184d49f5592a21edc285c00557900c7d289f Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Thu, 8 Jun 2017 16:04:58 -0700 Subject: [PATCH 14/34] Fix network inspect and GetHostLocal IP --- cns/dockerclient/dockerclient.go | 6 ++-- cns/{client => examples}/examples.go | 0 cns/imdsclient/imdsclient.go | 15 ++++++++- cns/restserver/restserver.go | 47 +++++++++++++++------------- 4 files changed, 42 insertions(+), 26 deletions(-) rename cns/{client => examples}/examples.go (100%) diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go index 5a2eccec47..90d7ddb71f 100644 --- a/cns/dockerclient/dockerclient.go +++ b/cns/dockerclient/dockerclient.go @@ -44,10 +44,8 @@ func NewDefaultDockerClient(imdsClient *imdsclient.ImdsClient) (*DockerClient, e func (dockerClient *DockerClient) NetworkExists(networkName string) error { log.Printf("[Azure CNS] NetworkExists") - res, err := http.Post( - dockerClient.connectionURL+inspectNetworkPath+networkName, - "application/json; charset=utf-8", - nil) + res, err := http.Get( + dockerClient.connectionURL+inspectNetworkPath+networkName) if err != nil { log.Printf("[Azure CNS] Error received from http Post for docker network inspect %v %v", networkName, err.Error()) diff --git a/cns/client/examples.go b/cns/examples/examples.go similarity index 100% rename from cns/client/examples.go rename to cns/examples/examples.go diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go index fe1a0e42ae..c8c68f5c3e 100644 --- a/cns/imdsclient/imdsclient.go +++ b/cns/imdsclient/imdsclient.go @@ -78,5 +78,18 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, // GetPrimaryInterfaceInfoFromMemory retrieves subnet and gateway of primary NIC that is saved in memory. func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromMemory() (*InterfaceInfo, error) { log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromMemory") - return imdsClient.primaryInterface, nil + var iface *InterfaceInfo + var err error + if(imdsClient.primaryInterface == nil) { + log.Debugf("Azure-CNS] Primary interface in memory does not exist. Will get it from Host.") + iface, err = imdsClient.GetPrimaryInterfaceInfoFromHost() + if err != nil { + log.Printf("[Azure-CNS] Unable to retrive primary interface info.") + } else { + log.Debugf("Azure-CNS] Primary interface received from HOST: %+v.", iface) + } + } else { + iface = imdsClient.primaryInterface + } + return iface, err } \ No newline at end of file diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 7760ecb483..6d151e64ea 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -54,7 +54,7 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { if(err != nil){ return nil, err } - + return &httpRestService{ Service: service, store: service.Service.Store, @@ -167,7 +167,8 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req returnCode = UnsupportedEnvironment } } else { - log.Printf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) + returnMessage = fmt.Sprintf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) + log.Printf(returnMessage) } default: @@ -279,29 +280,33 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re log.Printf("[Azure CNS] getHostLocalIP") log.Request(service.Name, "getHostLocalIP", nil) hostLocalIP := "0.0.0.0" - switch r.Method { - case "GET": - switch (service.state.NetworkType) { - case "Underlay": - if (service.imdsClient != nil) { - piface, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() - if err != nil { - hostLocalIP = piface.PrimaryIP - found = true; - } else { - log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) + if service.state.Initialized { + switch r.Method { + case "GET": + switch (service.state.NetworkType) { + case "Underlay": + if (service.imdsClient != nil) { + piface, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() + if err == nil { + hostLocalIP = piface.PrimaryIP + found = true; + } else { + log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) + } } - } - case "Overlay": - errmsg = "[Azure-CNS] Overlay is not yet supported." - } - default: - errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." + case "Overlay": + errmsg = "[Azure-CNS] Overlay is not yet supported." + } + default: + errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." + } } - returnCode := 0 if !found { returnCode = NotFound + if(errmsg == ""){ + errmsg = "[Azure-CNS] Unable to get host local ip. Check if environment is initialized.." + } } resp := cns.Response{ReturnCode: returnCode, Message: errmsg} @@ -313,7 +318,7 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re log.Response(service.Name, hostLocalIPResponse, err) } -// Handles ip address utiliztion requests. +// Handles ip address utilization requests. func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getIPAddressUtilization") log.Request(service.Name, "getIPAddressUtilization", nil) From 5ba896a4f98d540fe14f58cbe982f973ac7102aa Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Fri, 9 Jun 2017 09:41:27 -0700 Subject: [PATCH 15/34] Remove unused handlers. Update tests. --- cns/api.go | 24 ++--- cns/examples/examples.go | 66 ++------------ cns/restserver/restserver.go | 21 ++--- cns/restserver/restserver_test.go | 146 ++++++++---------------------- 4 files changed, 65 insertions(+), 192 deletions(-) diff --git a/cns/api.go b/cns/api.go index 43546b31ef..6ce07574ac 100644 --- a/cns/api.go +++ b/cns/api.go @@ -5,18 +5,15 @@ package cns // Container Network Service remote API Contract const ( - SetEnvironmentPath = "/Network/Environment" - CreateNetworkPath = "/Network/Create" - DeleteNetworkPath = "/Network/Delete" - ReserveIPAddressPath = "/Network/IP/Reserve" - ReleaseIPAddressPath = "/Network/IP/Release" - GetHostLocalIPPath = "/Network/IP/HostLocal" - GetIPAddressUtilizationPath = "/Network/IP/Utilization" - GetAvailableIPAddressesPath = "/Network/IPAddresses/Available" - GetReservedIPAddressesPath = "/Network/IPAddresses/Reserved" - GetGhostIPAddressesPath = "/Network/IPAddresses/Ghost" - GetAllIPAddressesPath = "/Network/IPAddresses/All" - GetHealthReportPath = "/Network/Health" + SetEnvironmentPath = "/network/environment" + CreateNetworkPath = "/network/create" + DeleteNetworkPath = "/network/delete" + ReserveIPAddressPath = "/network/ip/Reserve" + ReleaseIPAddressPath = "/network/ip/Release" + GetHostLocalIPPath = "/network/ip/HostLocal" + GetIPAddressUtilizationPath = "/network/ip/Utilization" + GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy" + GetHealthReportPath = "/network/health" ) // SetEnvironmentRequest describes the Request to set the environment in CNS. @@ -65,7 +62,7 @@ type IPAddressesUtilizationResponse struct { Response Response Available int Reserved int - Ghost int + Unhealthy int } // GetIPAddressesResponse describes response containing requested ip addresses. @@ -84,7 +81,6 @@ type HostLocalIPAddressResponse struct { type IPAddress struct { IPAddress string ReservationID string - IsGhost bool } // Subnet contains the ip address and the number of bits in prefix. diff --git a/cns/examples/examples.go b/cns/examples/examples.go index e1072ead63..2f0f36edb3 100644 --- a/cns/examples/examples.go +++ b/cns/examples/examples.go @@ -167,71 +167,20 @@ func getHostLocalIP() error{ return nil } -func getAvailableIPAddresses() error{ +func getUnhealthyIPAddresses() error{ res, err := - http.Get(defaultCNSServerURL+cns.GetAvailableIPAddressesPath) + http.Get(defaultCNSServerURL+cns.GetUnhealthyIPAddressesPath) if err != nil { - fmt.Printf("Error received in GetAvailable IP Addresses: %v ", err.Error()) - return err - } - var getIPAddressesResponse cns.GetIPAddressesResponse - err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) - if err != nil { - fmt.Printf("Error received in decoding response from GetAvailableIPAddresses: %v ", err.Error()) - return err - } - fmt.Printf("Response for GetAvailableIPAddresses: %+v\n", getIPAddressesResponse) - return nil -} - -func getReservedIPAddresses() error{ - res, err := - http.Get(defaultCNSServerURL+cns.GetReservedIPAddressesPath) - if err != nil { - fmt.Printf("Error received in GetReserved IP Addresses: %v ", err.Error()) - return err - } - var getIPAddressesResponse cns.GetIPAddressesResponse - err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) - if err != nil { - fmt.Printf("Error received in decoding response from getReservedIPAddresses: %v ", err.Error()) - return err - } - fmt.Printf("Response for getReservedIPAddresses: %+v\n", getIPAddressesResponse) - return nil -} - -func getGhostIPAddresses() error{ - res, err := - http.Get(defaultCNSServerURL+cns.GetGhostIPAddressesPath) - if err != nil { - fmt.Printf("Error received in GetGhost IP Addresses: %v ", err.Error()) - return err - } - var getIPAddressesResponse cns.GetIPAddressesResponse - err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) - if err != nil { - fmt.Printf("Error received in decoding response from getGhostIPAddresses: %v ", err.Error()) - return err - } - fmt.Printf("Response for getGhostIPAddresses: %+v\n", getIPAddressesResponse) - return nil -} - -func getAllIPAddresses() error{ - res, err := - http.Get(defaultCNSServerURL+cns.GetAllIPAddressesPath) - if err != nil { - fmt.Printf("Error received in GetAll IP Addresses: %v ", err.Error()) + fmt.Printf("Error received in GetUnhealthyIPAddresses IP Addresses: %v ", err.Error()) return err } var getIPAddressesResponse cns.GetIPAddressesResponse err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) if err != nil { - fmt.Printf("Error received in decoding response from getAllIPAddresses: %v ", err.Error()) + fmt.Printf("Error received in decoding response from GetUnhealthyIPAddresses: %v ", err.Error()) return err } - fmt.Printf("Response for getAllIPAddresses: %+v\n", getIPAddressesResponse) + fmt.Printf("Response for GetUnhealthyIPAddresses: %+v\n", getIPAddressesResponse) return nil } @@ -241,10 +190,7 @@ func main() { deleteNetwork() reserveIPAddress() releaseIPAddress() - getAvailableIPAddresses() - getReservedIPAddresses() - getGhostIPAddresses() - getAllIPAddresses() + getUnhealthyIPAddresses() getIPAddressUtilization() getHostLocalIP() } diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 6d151e64ea..82c54ef0db 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -81,13 +81,7 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { listener.AddHandler(cns.ReleaseIPAddressPath, service.releaseIPAddress) listener.AddHandler(cns.GetHostLocalIPPath, service.getHostLocalIP) listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) - listener.AddHandler(cns.GetGhostIPAddressesPath, service.getGhostIPAddresses) - - // Below APIs are not handled in this iteration. - // listener.AddHandler(cns.GetAvailableIPAddressesPath, service.getAvailableIPAddresses) - // listener.AddHandler(cns.GetReservedIPAddressesPath, service.getReservedIPAddresses) - // listener.AddHandler(cns.GetAllIPAddressesPath, service.getAllIPAddresses) - // listener.AddHandler(cns.GetHealthReportPath, service.getHealthReport) + listener.AddHandler(cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) log.Printf("[Azure CNS] Listening.") return nil @@ -216,7 +210,12 @@ func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Req returnCode = UnexpectedError } } else { - log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v.", req.NetworkName) + if(err == fmt.Errorf("Network not found")){ + log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v.", req.NetworkName) + } else { + returnCode = UnexpectedError + returnMessage = err.Error() + } } default: @@ -364,9 +363,9 @@ func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r } // Handles retrieval of ghost ip addresses from ipam driver. -func (service *httpRestService) getGhostIPAddresses(w http.ResponseWriter, r *http.Request) { - log.Printf("[Azure CNS] getGhostIPAddresses") - log.Request(service.Name, "getGhostIPAddresses", nil) +func (service *httpRestService) getUnhealthyIPAddresses(w http.ResponseWriter, r *http.Request) { + log.Printf("[Azure CNS] getUnhealthyIPAddresses") + log.Request(service.Name, "getUnhealthyIPAddresses", nil) switch r.Method { case "GET": default: diff --git a/cns/restserver/restserver_test.go b/cns/restserver/restserver_test.go index 805832b86a..7626cb4400 100644 --- a/cns/restserver/restserver_test.go +++ b/cns/restserver/restserver_test.go @@ -66,29 +66,48 @@ func decodeResponse(w *httptest.ResponseRecorder, response interface{}) error { return json.NewDecoder(w.Body).Decode(&response) } -// Tests CreateNetwork functionality. -func TestCreateNetwork(t *testing.T) { - fmt.Println("Test: CreateNetwork") +func setEnv(t *testing.T) (*httptest.ResponseRecorder) { + envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} + envRequestJSON := new(bytes.Buffer) + json.NewEncoder(envRequestJSON).Encode(envRequest) - var body bytes.Buffer + req, err := http.NewRequest(http.MethodPost, cns.SetEnvironmentPath, envRequestJSON) + if err != nil { + t.Fatal(err) + } + w := httptest.NewRecorder() + mux.ServeHTTP(w, req) + return w +} +func TestSetEnvironment(t *testing.T) { + fmt.Println("Test: SetEnvironment") var resp cns.Response + w := setEnv(t) + err := decodeResponse(w, &resp) + if err != nil || resp.ReturnCode != 0 { + t.Errorf("SetEnvironment failed with response %+v", resp) + } else { + fmt.Printf ("SetEnvironment Responded with %+v\n", resp); + } +} +// Tests CreateNetwork functionality. +func TestCreateNetwork(t *testing.T) { + fmt.Println("Test: CreateNetwork") + var body bytes.Buffer + setEnv(t) info := &cns.CreateNetworkRequest{ NetworkName: "azurenet", } - json.NewEncoder(&body).Encode(info) - req, err := http.NewRequest(http.MethodPost, cns.CreateNetworkPath, &body) if err != nil { t.Fatal(err) - } - + } w := httptest.NewRecorder() mux.ServeHTTP(w, req) - + var resp cns.Response err = decodeResponse(w, &resp) - if err != nil || resp.ReturnCode != 0 { t.Errorf("CreateNetwork failed with response %+v", resp) } else { @@ -96,29 +115,23 @@ func TestCreateNetwork(t *testing.T) { } } -// Tests CreateNetwork functionality. +// Tests DeleteNetwork functionality. func TestDeleteNetwork(t *testing.T) { - fmt.Println("Test: DeleteNetwork") - + fmt.Println("Test: DeleteNetwork") var body bytes.Buffer - var resp cns.Response - + setEnv(t) info := &cns.DeleteNetworkRequest{ NetworkName: "azurenet", } - json.NewEncoder(&body).Encode(info) - req, err := http.NewRequest(http.MethodPost, cns.DeleteNetworkPath, &body) if err != nil { t.Fatal(err) - } - + } w := httptest.NewRecorder() mux.ServeHTTP(w, req) - + var resp cns.Response err = decodeResponse(w, &resp) - if err != nil || resp.ReturnCode != 0 { t.Errorf("DeleteNetwork failed with response %+v", resp) } else { @@ -126,31 +139,6 @@ func TestDeleteNetwork(t *testing.T) { } } -func TestSetEnvironment(t *testing.T) { - fmt.Println("Test: SetEnvironment") - - var resp cns.Response - envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} - envRequestJSON := new(bytes.Buffer) - json.NewEncoder(envRequestJSON).Encode(envRequest) - - req, err := http.NewRequest(http.MethodGet, cns.SetEnvironmentPath, envRequestJSON) - if err != nil { - t.Fatal(err) - } - - w := httptest.NewRecorder() - mux.ServeHTTP(w, req) - - err = decodeResponse(w, &resp) - - if err != nil || resp.ReturnCode != 0 { - t.Errorf("SetEnvironment failed with response %+v", resp) - } else { - fmt.Printf ("SetEnvironment Responded with %+v\n", resp); - } -} - func TestReserveIPAddress(t *testing.T){ fmt.Println("Test: ReserveIPAddress") @@ -225,6 +213,7 @@ func TestGetIPAddressUtilization(t *testing.T){ func TestGetHostLocalIP(t *testing.T){ fmt.Println("Test: GetHostLocalIP") + setEnv(t) req, err := http.NewRequest(http.MethodGet, cns.GetHostLocalIPPath, nil) if err != nil { t.Fatal(err) @@ -242,66 +231,9 @@ func TestGetHostLocalIP(t *testing.T){ } } -func TestGetAvailableIPAddresses(t *testing.T){ - fmt.Println("Test: GetAvailableIPAddresses") - req, err := http.NewRequest(http.MethodGet, cns.GetAvailableIPAddressesPath, nil) - if err != nil { - t.Fatal(err) - } - w := httptest.NewRecorder() - mux.ServeHTTP(w, req) - - var getIPAddressesResponse cns.GetIPAddressesResponse - err = decodeResponse(w, &getIPAddressesResponse) - - if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetAvailableIPAddresses failed with response %+v", getIPAddressesResponse) - } else { - fmt.Printf ("GetAvailableIPAddresses Responded with %+v\n", getIPAddressesResponse); - } -} - -func TestGetReservedIPAddresses(t *testing.T){ - fmt.Println("Test: GetReservedIPAddresses") - req, err := http.NewRequest(http.MethodGet, cns.GetReservedIPAddressesPath, nil) - if err != nil { - t.Fatal(err) - } - w := httptest.NewRecorder() - mux.ServeHTTP(w, req) - - var getIPAddressesResponse cns.GetIPAddressesResponse - err = decodeResponse(w, &getIPAddressesResponse) - - if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetReservedIPAddresses failed with response %+v", getIPAddressesResponse) - } else { - fmt.Printf ("GetReservedIPAddresses Responded with %+v\n", getIPAddressesResponse); - } -} - -func TestGetGhostIPAddresses(t *testing.T){ +func TestGetUnhealthyIPAddresses(t *testing.T){ fmt.Println("Test: GetGhostIPAddresses") - req, err := http.NewRequest(http.MethodGet, cns.GetGhostIPAddressesPath, nil) - if err != nil { - t.Fatal(err) - } - w := httptest.NewRecorder() - mux.ServeHTTP(w, req) - - var getIPAddressesResponse cns.GetIPAddressesResponse - err = decodeResponse(w, &getIPAddressesResponse) - - if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetGhostIPAddresses failed with response %+v", getIPAddressesResponse) - } else { - fmt.Printf ("GetGhostIPAddresses Responded with %+v\n", getIPAddressesResponse); - } -} - -func TestGetAllIPAddresses(t *testing.T){ - fmt.Println("Test: GetAllIPAddresses") - req, err := http.NewRequest(http.MethodGet, cns.GetAllIPAddressesPath, nil) + req, err := http.NewRequest(http.MethodGet, cns.GetUnhealthyIPAddressesPath, nil) if err != nil { t.Fatal(err) } @@ -312,9 +244,9 @@ func TestGetAllIPAddresses(t *testing.T){ err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { - t.Errorf("GetAllIPAddresses failed with response %+v", getIPAddressesResponse) + t.Errorf("GetUnhealthyIPAddresses failed with response %+v", getIPAddressesResponse) } else { - fmt.Printf ("GetAllIPAddresses Responded with %+v\n", getIPAddressesResponse); + fmt.Printf ("GetUnhealthyIPAddresses Responded with %+v\n", getIPAddressesResponse); } } From ad3d49e75c443f36edb586c5145b7cc524d76957 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Fri, 9 Jun 2017 09:43:58 -0700 Subject: [PATCH 16/34] Rename folder (main -> service) in CNS --- cns/{main => service}/main.go | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename cns/{main => service}/main.go (100%) diff --git a/cns/main/main.go b/cns/service/main.go similarity index 100% rename from cns/main/main.go rename to cns/service/main.go From 8f5e0095fa66c8889a51cba07712bd537d0b980f Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Fri, 9 Jun 2017 09:56:29 -0700 Subject: [PATCH 17/34] Fix some formatting --- cns/api.go | 2 +- cns/dockerclient/dockerclient.go | 15 +++++---- cns/examples/examples.go | 2 ++ cns/imdsclient/imdsclient.go | 6 ++++ cns/restserver/restserver.go | 56 +++++++++++++++++++++++++++++--- cns/service/main.go | 1 - common/service.go | 6 +++- 7 files changed, 73 insertions(+), 15 deletions(-) diff --git a/cns/api.go b/cns/api.go index 6ce07574ac..3d2327fe20 100644 --- a/cns/api.go +++ b/cns/api.go @@ -12,7 +12,7 @@ const ( ReleaseIPAddressPath = "/network/ip/Release" GetHostLocalIPPath = "/network/ip/HostLocal" GetIPAddressUtilizationPath = "/network/ip/Utilization" - GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy" + GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy" GetHealthReportPath = "/network/health" ) diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go index 90d7ddb71f..3c2c2c9d05 100644 --- a/cns/dockerclient/dockerclient.go +++ b/cns/dockerclient/dockerclient.go @@ -79,12 +79,14 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { config := &Config{ Subnet: primaryNic.Subnet, } + configs := make([]Config, 1) configs[0] = *config ipamConfig := &IPAM{ Driver: defaultIpamPlugin, Config: configs, } + netConfig := &NetworkConfiguration{ Name: networkName, Driver: defaultNetworkPlugin, @@ -117,8 +119,8 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { if(err != nil){ ermsg = err.Error() } - return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v - %v - %v", res.StatusCode, createNetworkResponse.message, ermsg) - + return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v - %v - %v", + res.StatusCode, createNetworkResponse.message, ermsg) } return nil @@ -129,14 +131,12 @@ func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { log.Printf("[Azure CNS] DeleteNetwork") url := dockerClient.connectionURL+inspectNetworkPath+networkName - req, err := http.NewRequest( - "DELETE", - url, - nil) + req, err := http.NewRequest("DELETE", url, nil) if err != nil { log.Printf("[Azure CNS] Error received while creating http DELETE request for network delete %v %v", networkName, err.Error()) return err } + req.Header.Set("Content-Type", "application/json; charset=utf-8") client := &http.Client{} res, err := client.Do(req) @@ -151,5 +151,6 @@ func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { return fmt.Errorf("[Azure CNS] Network not found %v", networkName) } - return fmt.Errorf("[Azure CNS] Unknown return code from docker delete network %v: ret = %d", networkName, res.StatusCode) + return fmt.Errorf("[Azure CNS] Unknown return code from docker delete network %v: ret = %d", + networkName, res.StatusCode) } diff --git a/cns/examples/examples.go b/cns/examples/examples.go index 2f0f36edb3..9ad6fedb08 100644 --- a/cns/examples/examples.go +++ b/cns/examples/examples.go @@ -16,6 +16,8 @@ const ( defaultCNSServerURL = "http://localhost:10090" ) +// These are example showing how to use CNS APIs by clients + func setEnvironment() error{ envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} envRequestJSON := new(bytes.Buffer) diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go index c8c68f5c3e..4ddc71163f 100644 --- a/cns/imdsclient/imdsclient.go +++ b/cns/imdsclient/imdsclient.go @@ -15,6 +15,7 @@ import ( // GetPrimaryInterfaceInfoFromHost retrieves subnet and gateway of primary NIC from Host. func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, error) { log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromHost") + interfaceInfo := &InterfaceInfo{} resp, err := http.Get(hostQueryURL) if(err != nil){ @@ -59,6 +60,7 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, interfaceInfo.PrimaryIP = ip.Address } } + imdsClient.primaryInterface = interfaceInfo break; } @@ -67,17 +69,20 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, break; } } + var er error er = nil if (!foundPrimaryInterface) { er = fmt.Errorf("Unable to find primary NIC") } + return interfaceInfo, er } // GetPrimaryInterfaceInfoFromMemory retrieves subnet and gateway of primary NIC that is saved in memory. func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromMemory() (*InterfaceInfo, error) { log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromMemory") + var iface *InterfaceInfo var err error if(imdsClient.primaryInterface == nil) { @@ -91,5 +96,6 @@ func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromMemory() (*InterfaceInf } else { iface = imdsClient.primaryInterface } + return iface, err } \ No newline at end of file diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 82c54ef0db..ab771d57af 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -49,6 +49,7 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { if err != nil { return nil, err } + imdsClient := &imdsclient.ImdsClient{} dc, err := dockerclient.NewDefaultDockerClient(imdsClient) if(err != nil){ @@ -96,9 +97,11 @@ func (service *httpRestService) Stop() { // Handles requests to set the environment type. func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] setEnvironment") + var req cns.SetEnvironmentRequest err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) + if err != nil { return } @@ -115,18 +118,19 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re resp := &cns.Response{ReturnCode: 0} err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) } // Handles CreateNetwork requests. func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] createNetwork") + var err error returnCode := 0 returnMessage := "" if(service.state.Initialized) { - var req cns.CreateNetworkRequest err = service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) @@ -182,20 +186,24 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req } err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) } // Handles DeleteNetwork requests. func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] deleteNetwork") + var req cns.DeleteNetworkRequest returnCode := 0 returnMessage := "" err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) + if err != nil { return } + switch r.Method { case "POST": dc := service.dockerClient @@ -227,20 +235,25 @@ func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Req ReturnCode: returnCode, Message: returnMessage, } + err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) } // Handles ip reservation requests. func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] reserveIPAddress") - var req cns.ReserveIPAddressRequest + var req cns.ReserveIPAddressRequest err := service.Listener.Decode(w, r, &req) + log.Request(service.Name, &req, err) + if err != nil { return } + switch r.Method { case "POST": default: @@ -249,19 +262,23 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. resp := cns.Response{ReturnCode: 0} reserveResp := &cns.ReserveIPAddressResponse{Response: resp, IPAddress: "0.0.0.0"} err = service.Listener.Encode(w, &reserveResp) + log.Response(service.Name, reserveResp, err) } // Handles release ip reservation requests. func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] releaseIPAddress") - var req cns.ReleaseIPAddressRequest err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) + + var req cns.ReleaseIPAddressRequest + if err != nil { return } + switch r.Method { case "POST": default: @@ -269,16 +286,19 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. resp := &cns.Response{ReturnCode: 0} err = service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) } // Retrieves the host local ip address. Containers can talk to host using this IP address. func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Request) { - var found bool - var errmsg string log.Printf("[Azure CNS] getHostLocalIP") log.Request(service.Name, "getHostLocalIP", nil) + + var found bool + var errmsg string hostLocalIP := "0.0.0.0" + if service.state.Initialized { switch r.Method { case "GET": @@ -293,13 +313,16 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) } } + case "Overlay": errmsg = "[Azure-CNS] Overlay is not yet supported." } + default: errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." } } + returnCode := 0 if !found { returnCode = NotFound @@ -313,7 +336,9 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re Response: resp, IPAddress: hostLocalIP, } + err := service.Listener.Encode(w, &hostLocalIPResponse) + log.Response(service.Name, hostLocalIPResponse, err) } @@ -321,16 +346,20 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getIPAddressUtilization") log.Request(service.Name, "getIPAddressUtilization", nil) + switch r.Method { case "GET": default: } + resp := cns.Response{ReturnCode: 0} utilResponse := &cns.IPAddressesUtilizationResponse{ Response: resp, Available: 0, } + err := service.Listener.Encode(w, &utilResponse) + log.Response(service.Name, utilResponse, err) } @@ -338,13 +367,16 @@ func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getAvailableIPAddresses") log.Request(service.Name, "getAvailableIPAddresses", nil) + switch r.Method { case "GET": default: } + resp := cns.Response{ReturnCode: 0} ipResp := &cns.GetIPAddressesResponse{Response: resp} err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) } @@ -352,13 +384,16 @@ func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getReservedIPAddresses") log.Request(service.Name, "getReservedIPAddresses", nil) + switch r.Method { case "GET": default: } + resp := cns.Response{ReturnCode: 0} ipResp := &cns.GetIPAddressesResponse{Response: resp} err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) } @@ -366,13 +401,16 @@ func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r func (service *httpRestService) getUnhealthyIPAddresses(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getUnhealthyIPAddresses") log.Request(service.Name, "getUnhealthyIPAddresses", nil) + switch r.Method { case "GET": default: } + resp := cns.Response{ReturnCode: 0} ipResp := &cns.GetIPAddressesResponse{Response: resp} err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) } @@ -380,13 +418,16 @@ func (service *httpRestService) getUnhealthyIPAddresses(w http.ResponseWriter, r func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getAllIPAddresses") log.Request(service.Name, "getAllIPAddresses", nil) + switch r.Method { case "GET": default: } + resp := cns.Response{ReturnCode: 0} ipResp := &cns.GetIPAddressesResponse{Response: resp} err := service.Listener.Encode(w, &ipResp) + log.Response(service.Name, ipResp, err) } @@ -394,6 +435,7 @@ func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getHealthReport") log.Request(service.Name, "getHealthReport", nil) + switch r.Method { case "GET": default: @@ -401,12 +443,14 @@ func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.R resp := &cns.Response{ReturnCode: 0} err := service.Listener.Encode(w, &resp) + log.Response(service.Name, resp, err) } // saveState writes CNS state to persistent store. func (service *httpRestService) saveState() error { log.Printf("[Azure CNS] saveState") + // Skip if a store is not provided. if service.store == nil { log.Printf("[Azure CNS] store not initialized.") @@ -421,12 +465,14 @@ func (service *httpRestService) saveState() error { } else { log.Printf("[Azure CNS] Failed to save state., err:%v\n", err) } + return err } // restoreState restores CNS state from persistent store. func (service *httpRestService) restoreState() error { log.Printf("[Azure CNS] restoreState") + // Skip if a store is not provided. if service.store == nil { log.Printf("[Azure CNS] store not initialized.") diff --git a/cns/service/main.go b/cns/service/main.go index cb8c1cc277..6867280a96 100644 --- a/cns/service/main.go +++ b/cns/service/main.go @@ -109,7 +109,6 @@ func main() { return } - // Create logging provider. log.SetName(name) log.SetLevel(logLevel) diff --git a/common/service.go b/common/service.go index ff3cf10922..ae9fc2ab41 100644 --- a/common/service.go +++ b/common/service.go @@ -38,14 +38,15 @@ type ServiceConfig struct { // NewService creates a new Service object. func NewService(name, version string, store store.KeyValueStore) (*Service, error) { - log.Debugf("[Azure CNS] Going to create a service object with name: %v. version: %v.", name, version) + svc := &Service{ Name: name, Version: version, Options: make(map[string]interface{}), Store: store, } + log.Debugf("[Azure CNS] Finished creating service object with name: %v. version: %v.", name, version) return svc, nil } @@ -59,10 +60,13 @@ func (service *Service) Initialize(config *ServiceConfig) error { } log.Debugf("[Azure CNS] Going to initialize the service: %+v with config: %+v.", service, config) + service.ErrChan = config.ErrChan service.Store = config.Store service.Version = config.Version + log.Debugf("[Azure CNS] nitialized service: %+v with config: %+v.", service, config) + return nil } From 26425a008e15614cf177c9519dd35b484f338195 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Wed, 14 Jun 2017 10:39:25 -0700 Subject: [PATCH 18/34] Capability to restore routes that get lost during network create --- cns/restserver/restserver.go | 21 ++++ cns/routes/routes.go | 42 ++++++++ cns/routes/routes_linux.go | 27 ++++++ cns/routes/routes_test.go | 113 ++++++++++++++++++++++ cns/routes/routes_windows.go | 181 +++++++++++++++++++++++++++++++++++ 5 files changed, 384 insertions(+) create mode 100644 cns/routes/routes.go create mode 100644 cns/routes/routes_linux.go create mode 100644 cns/routes/routes_test.go create mode 100644 cns/routes/routes_windows.go diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index ab771d57af..3eb1bed940 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -14,6 +14,7 @@ import ( "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" + "github.com/Azure/azure-container-networking/cns/routes" ) const ( @@ -26,6 +27,7 @@ type httpRestService struct { *cns.Service dockerClient *dockerclient.DockerClient imdsClient *imdsclient.ImdsClient + routingTable *routes.RoutingTable store store.KeyValueStore state httpRestServiceState } @@ -51,6 +53,7 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { } imdsClient := &imdsclient.ImdsClient{} + routingTable := &routes.RoutingTable{} dc, err := dockerclient.NewDefaultDockerClient(imdsClient) if(err != nil){ return nil, err @@ -61,6 +64,7 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { store: service.Service.Store, dockerClient: dc, imdsClient: imdsClient, + routingTable: routingTable, }, nil } @@ -142,6 +146,7 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req switch r.Method { case "POST": dc := service.dockerClient + rt := service.routingTable err = dc.NetworkExists(req.NetworkName) // Network does not exist. @@ -151,11 +156,27 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req switch service.state.Location { case "Azure": log.Printf("[Azure CNS] Goign to create network with name %v.", req.NetworkName) + + err = rt.GetRoutingTable() + if(err != nil) { + // We should not fail the call to create network for this. + // This is because restoring routes is a fallback mechanism in case + // network driver is not behaving as expected. + // The responsibility to restore routes is with network driver. + log.Printf("[Azure CNS] Unable to get routing table from node, %+v.", err.Error()) + } + err = dc.CreateNetwork(req.NetworkName) if(err != nil) { returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) returnCode = UnexpectedError } + + err = rt.RestoreRoutingTable() + if(err != nil) { + log.Printf("[Azure CNS] Unable to restore routing table on node, %+v.", err.Error()) + } + case "StandAlone": returnMessage = fmt.Sprintf("[Azure CNS] Error. Underlay network is not supported in StandAlone environment. %v.", err.Error()) returnCode = UnsupportedEnvironment diff --git a/cns/routes/routes.go b/cns/routes/routes.go new file mode 100644 index 0000000000..7f6b072c27 --- /dev/null +++ b/cns/routes/routes.go @@ -0,0 +1,42 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package routes + +import ( + "github.com/Azure/azure-container-networking/log" +) + +// Route describes a single route in the routing table. +type Route struct { + destination string + mask string + gateway string + metric string + ifaceIndex int +} + +// RoutingTable describes the routing table on the node. +type RoutingTable struct { + Routes []Route +} + +// GetRoutingTable retireves routing table in the node. +func (rt *RoutingTable) GetRoutingTable() (error) { + routes, err := getRoutes() + if(err == nil) { + rt.Routes = routes + } + + return err +} + +// RestoreRoutingTable pushes the saved route. +func (rt *RoutingTable) RestoreRoutingTable () (error) { + if rt.Routes == nil { + log.Printf("[Azure CNS] Nothing available in routing table to push") + return nil + } + + return putRoutes(rt.Routes) +} \ No newline at end of file diff --git a/cns/routes/routes_linux.go b/cns/routes/routes_linux.go new file mode 100644 index 0000000000..13d4b3b6af --- /dev/null +++ b/cns/routes/routes_linux.go @@ -0,0 +1,27 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +// +build linux + +package routes + +import ( + "fmt" + "net" + "os/exec" + + "strings" + + "github.com/Azure/azure-container-networking/log" +) + +const ( +) + +func getRoutes() ([]Route, error) { + return nil, nil +} + +func putRoutes(routes []Route) error { + return nil +} diff --git a/cns/routes/routes_test.go b/cns/routes/routes_test.go new file mode 100644 index 0000000000..cab35cdafa --- /dev/null +++ b/cns/routes/routes_test.go @@ -0,0 +1,113 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package routes + +import ( + "os" + "os/exec" + "testing" + "github.com/Azure/azure-container-networking/log" +) + +const ( + testDest = "159.254.169.254" + testMask = "255.255.255.255" + testGateway = "0.0.0.0" + testMetric = "12" +) + +// Wraps the test run. +func TestMain(m *testing.M) { + // Run tests. + exitCode := m.Run() + + os.Exit(exitCode) +} + +func addTestRoute() error { + arg := []string{"/C", "route", "ADD", testDest, + "MASK", testMask, testGateway, "METRIC", testMetric} + log.Printf("[Azure CNS] Adding missing route: %v", arg) + + c := exec.Command("cmd", arg...) + bytes, err := c.Output() + + if err == nil { + log.Printf("[Azure CNS] Successfully executed add route: %v\n%v", + arg, string(bytes)) + } else { + log.Printf("[Azure CNS] Failed to execute add route: %v\n%v\n%v", + arg, string(bytes), err.Error()) + return err + } + return nil +} + +func deleteTestRoute() error { + args := []string{"/C", "route", "DELETE", testDest, "MASK", testMask, + testGateway, "METRIC", testMetric} + log.Printf("[Azure CNS] Deleting route: %v", args) + c := exec.Command("cmd", args...) + bytes, err := c.Output() + if err == nil { + log.Printf("[Azure CNS] Successfully executed delete route: %v\n%v", + args, string(bytes)) + } else { + log.Printf("[Azure CNS] Failed to execute delete route: %v\n%v\n%v", + args, string(bytes), err.Error()) + return err + } + return nil +} + +// TestPutRoutes tests if a missing route is properly restored or not. +func TestRestoreMissingRoute(t *testing.T){ + log.Printf("Test: PutMissingRoutes") + + err := addTestRoute() + if(err != nil) { + t.Errorf("add route failed %+v", err.Error()) + } + + rt := &RoutingTable{} + + // save routing table. + rt.GetRoutingTable() + cntr := 0 + for _,rt := range rt.Routes { + log.Printf("[]: Route[%d]: %+v", cntr, rt) + cntr++ + } + + // now delete the route so it goes missing. + err = deleteTestRoute() + if(err != nil) { + t.Errorf("delete route failed %+v", err.Error()) + } + + // now restore the deleted route. + rt.RestoreRoutingTable() + + // test if route was resotred or not. + rt.GetRoutingTable() + restored := false + for _, existingRoute := range rt.Routes { + log.Printf("Comapring %+v\n", existingRoute) + if(existingRoute.destination == testDest && + existingRoute.gateway == testGateway && + existingRoute.mask == testMask) { + restored = true + } + } + + if(!restored){ + t.Errorf("unable to restore missing route") + } else { + err = deleteTestRoute() + if(err != nil) { + t.Errorf("delete route failed %+v", err.Error()) + } + } +} + diff --git a/cns/routes/routes_windows.go b/cns/routes/routes_windows.go new file mode 100644 index 0000000000..997567b3dc --- /dev/null +++ b/cns/routes/routes_windows.go @@ -0,0 +1,181 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +// +build windows + +package routes + +import ( + "fmt" + "net" + "strings" + "os/exec" + + "github.com/Azure/azure-container-networking/log" +) + +const ( + ipv4RoutingTableStart = "IPv4 Route Table" + activeRoutesStart = "Active Routes:" +) + +func getInterfaceByAddress(address string) (int, error) { + log.Printf("[Azure CNS] getInterfaceByAddress") + + var ifaces []net.Interface + log.Printf("[Azure CNS] Going to obtain interface for address %s", address) + ifaces, err := net.Interfaces() + if err != nil { + return -1, err + } + + for i := 0; i < len(ifaces); i++ { + log.Debugf("[Azure CNS] Going to check interface %v", ifaces[i].Name) + addrs, _ := ifaces[i].Addrs() + for _, addr := range addrs { + log.Debugf("[Azure CNS] ipAddress being compared input=%v %v\n", + address, addr.String()) + ip := strings.Split(addr.String(), "/") + if(len(ip) != 2) { + return -1, fmt.Errorf("Malformed ip: %v", addr.String()) + } + if ip[0] == address { + return ifaces[i].Index, nil + } + } + } + + return -1, fmt.Errorf( + "[Azure CNS] Unable to determine interface index for address %s", + address) +} + +func getRoutes() ([]Route, error) { + log.Printf("[Azure CNS] getRoutes") + + c := exec.Command("cmd", "/C", "route", "print") + var routePrintOutput string + var routeCount int + bytes, err := c.Output() + + if err == nil { + routePrintOutput = string(bytes) + log.Debugf("[Azure CNS] Printing Routing table \n %v\n", routePrintOutput) + } else { + log.Printf("Received error in printing routing table %v", err.Error()) + return nil, err + } + + routePrint := strings.Split(routePrintOutput, ipv4RoutingTableStart) + routeTable := strings.Split(routePrint[1], activeRoutesStart) + tokens := strings.Split( + strings.Split(routeTable[1], "Metric")[1], + "=") + + table := tokens[0] + routes := strings.Split(table, "\r") + routeCount = len(routes) + log.Debugf("[Azure CNS] Recevied route count: %d", routeCount) + if(routeCount == 0){ + return nil, nil + } + + localRoutes := make([]Route, routeCount) + cntr := 0 + truncated := 0 + for _, route := range routes { + if route != "" { + tokens := strings.Fields(route) + if len(tokens) != 5 { + log.Printf("[Azure CNS] Ignoring route %s", route) + truncated++ + } else { + log.Debugf("[Azure CNS] Parsing route: %s %s %s %s %s\n", + tokens[0], tokens[1], tokens[2], tokens[3], tokens[4]) + rt := Route{ + destination: tokens[0], + mask: tokens[1], + gateway: tokens[2], + metric: tokens[4], + } + + if(rt.gateway == "On-link"){ + rt.gateway = "0.0.0.0" + } + + index, err := getInterfaceByAddress(tokens[3]) + if err == nil { + rt.ifaceIndex = index + localRoutes[cntr] = rt + cntr++ + } else { + log.Printf("[Azure CNS] Error encountered while obtaining index. %v\n", err.Error()) + truncated++ + } + } + } + } + + if(truncated == routeCount) { + localRoutes = nil + } else { + localRoutes = localRoutes[0 : routeCount-truncated-1] + } + + return localRoutes, nil +} + +func containsRoute(routes []Route, route Route) (bool, error){ + log.Printf("[Azure CNS] containsRoute") + if(routes == nil){ + return false, nil + } + for _, existingRoute := range routes { + if(existingRoute.destination == route.destination && + existingRoute.gateway == route.gateway && + existingRoute.ifaceIndex == route.ifaceIndex && + existingRoute.mask == route.mask) { + return true, nil + } + } + return false, nil +} + +func putRoutes(routes []Route) error { + log.Printf("[Azure CNS] putRoutes") + + var err error + log.Printf("[Azure CNS] Going to get current routes") + currentRoutes, err := getRoutes() + if( err != nil){ + return err + } + + for _, route := range routes { + exists, err := containsRoute(currentRoutes, route) + if(err == nil && !exists) { + args := []string{"/C", "route", "ADD", + route.destination, + "MASK", + route.mask, + route.gateway, + "METRIC", + route.metric, + "IF", + fmt.Sprintf("%d", route.ifaceIndex)} + log.Printf("[Azure CNS] Adding missing route: %v", args) + + c := exec.Command("cmd", args...) + bytes, err := c.Output() + if err == nil { + log.Printf("[Azure CNS] Successfully executed add route: %v\n%v", args, string(bytes)) + } else { + log.Printf("[Azure CNS] Failed to execute add route: %v\n%v", args, string(bytes)) + } + } else { + log.Printf("[Azure CNS] Route already exists. skipping %+v", route) + } + } + + return err +} From 31f622b90ac64500be07b4ed36f9f71ab096632f Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Wed, 14 Jun 2017 12:05:20 -0700 Subject: [PATCH 19/34] Add versioning for REST APIs --- cns/api.go | 1 + cns/restserver/restserver.go | 15 +++++++++++++-- cns/restserver/restserver_test.go | 2 +- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/cns/api.go b/cns/api.go index 3d2327fe20..dcfd530d87 100644 --- a/cns/api.go +++ b/cns/api.go @@ -14,6 +14,7 @@ const ( GetIPAddressUtilizationPath = "/network/ip/Utilization" GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy" GetHealthReportPath = "/network/health" + V1Prefix = "/v0.1" ) // SetEnvironmentRequest describes the Request to set the environment in CNS. diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 3eb1bed940..89e700ce7b 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -79,6 +79,8 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { // Add handlers. listener := service.Listener + + // default handlers listener.AddHandler(cns.SetEnvironmentPath, service.setEnvironment) listener.AddHandler(cns.CreateNetworkPath, service.createNetwork) listener.AddHandler(cns.DeleteNetworkPath, service.deleteNetwork) @@ -88,6 +90,16 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) listener.AddHandler(cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) + // handlers for v0.1 + listener.AddHandler(cns.V1Prefix + cns.SetEnvironmentPath, service.setEnvironment) + listener.AddHandler(cns.V1Prefix + cns.CreateNetworkPath, service.createNetwork) + listener.AddHandler(cns.V1Prefix + cns.DeleteNetworkPath, service.deleteNetwork) + listener.AddHandler(cns.V1Prefix + cns.ReserveIPAddressPath, service.reserveIPAddress) + listener.AddHandler(cns.V1Prefix + cns.ReleaseIPAddressPath, service.releaseIPAddress) + listener.AddHandler(cns.V1Prefix + cns.GetHostLocalIPPath, service.getHostLocalIP) + listener.AddHandler(cns.V1Prefix + cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) + listener.AddHandler(cns.V1Prefix + cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) + log.Printf("[Azure CNS] Listening.") return nil } @@ -291,11 +303,10 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] releaseIPAddress") + var req cns.ReleaseIPAddressRequest err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) - var req cns.ReleaseIPAddressRequest - if err != nil { return } diff --git a/cns/restserver/restserver_test.go b/cns/restserver/restserver_test.go index 7626cb4400..b46d35d2fd 100644 --- a/cns/restserver/restserver_test.go +++ b/cns/restserver/restserver_test.go @@ -71,7 +71,7 @@ func setEnv(t *testing.T) (*httptest.ResponseRecorder) { envRequestJSON := new(bytes.Buffer) json.NewEncoder(envRequestJSON).Encode(envRequest) - req, err := http.NewRequest(http.MethodPost, cns.SetEnvironmentPath, envRequestJSON) + req, err := http.NewRequest(http.MethodPost, cns.V1Prefix + cns.SetEnvironmentPath, envRequestJSON) if err != nil { t.Fatal(err) } From d9de81a3259072a2581a173228cf47156d03d70a Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Wed, 14 Jun 2017 12:39:13 -0700 Subject: [PATCH 20/34] Added ipamclient changes in cns --- cns/dockerclient/api.go | 17 +- cns/ipamclient/api.go | 70 +++++ cns/ipamclient/ipamclient.go | 234 +++++++++++++++++ cns/ipamclient/ipamclient_linux.go | 10 + cns/ipamclient/ipamclient_test.go | 229 ++++++++++++++++ cns/ipamclient/ipamclient_windows.go | 10 + cns/restserver/api.go | 23 +- cns/restserver/restserver.go | 377 ++++++++++++++++++--------- 8 files changed, 828 insertions(+), 142 deletions(-) create mode 100644 cns/ipamclient/api.go create mode 100644 cns/ipamclient/ipamclient.go create mode 100644 cns/ipamclient/ipamclient_linux.go create mode 100644 cns/ipamclient/ipamclient_test.go create mode 100644 cns/ipamclient/ipamclient_windows.go diff --git a/cns/dockerclient/api.go b/cns/dockerclient/api.go index 4cb8acb7e7..e949f4b019 100644 --- a/cns/dockerclient/api.go +++ b/cns/dockerclient/api.go @@ -3,12 +3,9 @@ package dockerclient -import ( -) - const ( - createNetworkPath = "/networks/create" - inspectNetworkPath = "/networks/" + createNetworkPath = "/networks/create" + inspectNetworkPath = "/networks/" ) // Config describes subnet/gateway for ipam. @@ -19,15 +16,15 @@ type Config struct { // IPAM describes ipam details type IPAM struct { Driver string - Config []Config + Config []Config } // NetworkConfiguration describes configuration for docker network create. type NetworkConfiguration struct { - Name string - Driver string - IPAM IPAM - Internal bool + Name string + Driver string + IPAM IPAM + Internal bool } // DockerErrorResponse defines the error response retunred by docker. diff --git a/cns/ipamclient/api.go b/cns/ipamclient/api.go new file mode 100644 index 0000000000..9ad6f64dcc --- /dev/null +++ b/cns/ipamclient/api.go @@ -0,0 +1,70 @@ +package ipamclient + +const ( + getAddressSpacesPath = "/IpamDriver.GetDefaultAddressSpaces" + requestPoolPath = "/IpamDriver.RequestPool" + reserveAddrPath = "/IpamDriver.RequestAddress" + releaseAddrPath = "/IpamDriver.ReleaseAddress" + getPoolInfoPath = "/IpamDriver.GetPoolInfo" +) + +// Config describes subnet/gateway for ipam. +type Config struct { + Subnet string +} + +type getAddressSpacesResponse struct { + LocalDefaultAddressSpace string + GlobalDefaultAddressSpace string +} + +// Request sent by libnetwork when acquiring a reference to an address pool. +type requestPoolRequest struct { + AddressSpace string + Pool string + SubPool string + Options map[string]string + V6 bool +} + +// Response sent by plugin when an address pool is successfully referenced. +type requestPoolResponse struct { + PoolID string + Pool string + Data map[string]string +} + +// NetworkConfiguration describes configuration for docker network create. +type reserveAddrRequest struct { + PoolID string + Address string + Options map[string]string +} + +// DockerErrorResponse defines the error response retunred by docker. +type reserveAddrResponse struct { + Address string +} + +// NetworkConfiguration describes configuration for docker network create. +type releaseAddrRequest struct { + PoolID string + Address string + Options map[string]string +} + +type releaseAddrResponse struct { + Err string +} + +// Request sent when querying address pool information. +type getPoolInfoRequest struct { + PoolID string +} + +// Response sent by plugin when returning address pool information. +type getPoolInfoResponse struct { + Capacity int + Available int + UnhealthyAddresses []string +} diff --git a/cns/ipamclient/ipamclient.go b/cns/ipamclient/ipamclient.go new file mode 100644 index 0000000000..995808baf0 --- /dev/null +++ b/cns/ipamclient/ipamclient.go @@ -0,0 +1,234 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +package ipamclient + +import ( + "bytes" + "encoding/json" + "net/http" + + "github.com/Azure/azure-container-networking/ipam" + "github.com/Azure/azure-container-networking/log" +) + +// IpamClient specifies a client to connect to Ipam Plugin. +type IpamClient struct { + connectionURL string +} + +// NewIpamClient create a new ipam client. +func NewIpamClient(url string) (*IpamClient, error) { + if url == "" { + url = defaultIpamPluginURL + } + return &IpamClient{ + connectionURL: url, + }, nil +} + +// GetAddressSpace request to get address space ID. +func (ic *IpamClient) GetAddressSpace() (string, error) { + log.Printf("[Azure CNS] GetAddressSpace Request") + + url := ic.connectionURL + getAddressSpacesPath + + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + log.Printf("[Azure CNS] Error received while creating http GET request for AddressSpace %v", err.Error()) + return "", err + } + + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + client := &http.Client{} + + res, err := client.Do(req) + if res == nil { + return "", err + } + + if res.StatusCode == 200 { + var resp getAddressSpacesResponse + err := json.NewDecoder(res.Body).Decode(&resp) + if err != nil { + log.Printf("[Azure CNS] Error received while parsing GetAddressSpace response resp:%v err:%v", res.Body, err.Error()) + return "", err + } + return resp.LocalDefaultAddressSpace, nil + } + log.Printf("[Azure CNS] GetAddressSpace invalid http status code: %v err:%v", res.StatusCode, err.Error()) + return "", err +} + +// GetPoolID Request to get poolID. +func (ic *IpamClient) GetPoolID(asID, subnet string) (string, error) { + var body bytes.Buffer + log.Printf("[Azure CNS] GetPoolID Request") + + url := ic.connectionURL + requestPoolPath + + payload := &requestPoolRequest{ + AddressSpace: asID, + Pool: subnet, + } + + json.NewEncoder(&body).Encode(payload) + + req, err := http.NewRequest(http.MethodGet, url, &body) + if err != nil { + log.Printf("[Azure CNS] Error received while creating http GET request for GetPoolID asID: %v poolid: %v err:%v", asID, subnet, err.Error()) + return "", err + } + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + client := &http.Client{} + res, err := client.Do(req) + + if res == nil { + return "", err + } + + if res.StatusCode == 200 { + var resp requestPoolResponse + err := json.NewDecoder(res.Body).Decode(&resp) + if err != nil { + log.Printf("[Azure CNS] Error received while parsing GetPoolID response resp:%v err:%v", res.Body, err.Error()) + return "", err + } + return resp.PoolID, nil + } + log.Printf("[Azure CNS] GetPoolID invalid http status code: %v err:%v", res.StatusCode, err.Error()) + return "", err + +} + +// ReserveIPAddress request an Ip address for the reservation id. +func (ic *IpamClient) ReserveIPAddress(poolID string, reservationID string) (string, error) { + var body bytes.Buffer + log.Printf("[Azure CNS] ReserveIpAddress") + + url := ic.connectionURL + reserveAddrPath + + payload := &reserveAddrRequest{ + PoolID: poolID, + Address: "", + Options: make(map[string]string), + } + payload.Options[ipam.OptAddressID] = reservationID + json.NewEncoder(&body).Encode(payload) + + req, err := http.NewRequest(http.MethodGet, url, &body) + if err != nil { + log.Printf("[Azure CNS] Error received while creating http GET request for reserve IP resid: %v poolid: %v err:%v", reservationID, poolID, err.Error()) + return "", err + } + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + client := &http.Client{} + res, err := client.Do(req) + + if res == nil { + return "", err + } + + if res.StatusCode == 200 { + var reserveResp reserveAddrResponse + err := json.NewDecoder(res.Body).Decode(&reserveResp) + if err != nil { + log.Printf("[Azure CNS] Error received while parsing reserve response resp:%v err:%v", res.Body, err.Error()) + return "", err + } + } + log.Printf("[Azure CNS] ReserveIp invalid http status code: %v err:%v", res.StatusCode, err.Error()) + return "", err +} + +// ReleaseIPAddress release an Ip address for the reservation id. +func (ic *IpamClient) ReleaseIPAddress(poolID string, reservationID string) error { + var body bytes.Buffer + log.Printf("[Azure CNS] ReleaseIpAddress") + + url := ic.connectionURL + releaseAddrPath + + payload := &releaseAddrRequest{ + PoolID: poolID, + Address: "", + Options: make(map[string]string), + } + + payload.Options[ipam.OptAddressID] = reservationID + + json.NewEncoder(&body).Encode(payload) + + req, err := http.NewRequest(http.MethodGet, url, &body) + if err != nil { + log.Printf("[Azure CNS] Error received while creating http GET request for ReleaseIP resid: %v poolid: %v err:%v", reservationID, poolID, err.Error()) + return err + } + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + client := &http.Client{} + res, err := client.Do(req) + + if res == nil { + return err + } + + if res.StatusCode == 200 { + var releaseResp releaseAddrResponse + err := json.NewDecoder(res.Body).Decode(&releaseResp) + if err != nil { + log.Printf("[Azure CNS] Error received while parsing release response :%v err:%v", res.Body, err.Error()) + return err + } else if releaseResp.Err != "" { + log.Printf("[Azure CNS] Error received while parsing release response :%v err:%v", res.Body, err.Error()) + return err + } + return nil + } + log.Printf("[Azure CNS] ReleaseIP invalid http status code: %v err:%v", res.StatusCode, err.Error()) + return err + +} + +// GetIPAddressUtilization - returns number of available, reserved and unhealthy addresses list +func (ic *IpamClient) GetIPAddressUtilization(poolID string) (int, int, []string, error) { + var body bytes.Buffer + log.Printf("[Azure CNS] GetIPAddressUtilization") + + url := ic.connectionURL + getPoolInfoPath + + payload := &getPoolInfoRequest{ + PoolID: poolID, + } + + json.NewEncoder(&body).Encode(payload) + + req, err := http.NewRequest(http.MethodGet, url, &body) + if err != nil { + log.Printf("[Azure CNS] Error received while creating http GET request for GetIPUtilization poolid: %v err:%v", poolID, err.Error()) + return 0, 0, nil, err + } + req.Header.Set("Content-Type", "application/json; charset=utf-8") + + client := &http.Client{} + res, err := client.Do(req) + + if res == nil { + return 0, 0, nil, err + } + + if res.StatusCode == 200 { + var poolInfoResp getPoolInfoResponse + err := json.NewDecoder(res.Body).Decode(&poolInfoResp) + if err != nil { + log.Printf("[Azure CNS] Error received while parsing release response :%v err:%v", res.Body, err.Error()) + return 0, 0, nil, err + } + return poolInfoResp.Capacity, poolInfoResp.Available, poolInfoResp.UnhealthyAddresses, nil + } + log.Printf("[Azure CNS] ReleaseIP invalid http status code: %v err:%v", res.StatusCode, err.Error()) + return 0, 0, nil, err + +} diff --git a/cns/ipamclient/ipamclient_linux.go b/cns/ipamclient/ipamclient_linux.go new file mode 100644 index 0000000000..37a3b0a189 --- /dev/null +++ b/cns/ipamclient/ipamclient_linux.go @@ -0,0 +1,10 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +// +build linux + +package dockerclient + +const ( + ipamPluginURL = "" +) diff --git a/cns/ipamclient/ipamclient_test.go b/cns/ipamclient/ipamclient_test.go new file mode 100644 index 0000000000..ca57150ce3 --- /dev/null +++ b/cns/ipamclient/ipamclient_test.go @@ -0,0 +1,229 @@ +package ipamclient + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "net/http/httptest" + "net/url" + "os" + "testing" + + "github.com/Azure/azure-container-networking/common" +) + +var mux *http.ServeMux +var ipamQueryUrl = "localhost:42424" +var ic *IpamClient + +// Wraps the test run with service setup and teardown. +func TestMain(m *testing.M) { + + // Create a fake IPAM plugin to handle requests from CNS plugin. + u, _ := url.Parse("tcp://" + ipamQueryUrl) + ipamAgent, err := common.NewListener(u) + if err != nil { + fmt.Printf("Failed to create agent, err:%v.\n", err) + return + } + ipamAgent.AddHandler(getAddressSpacesPath, handleIpamAsIDQuery) + ipamAgent.AddHandler(requestPoolPath, handlePoolIDQuery) + ipamAgent.AddHandler(reserveAddrPath, handleReserveIPQuery) + ipamAgent.AddHandler(releaseAddrPath, handleReleaseIPQuery) + ipamAgent.AddHandler(getPoolInfoPath, handleIPUtilizationQuery) + + err = ipamAgent.Start(make(chan error, 1)) + if err != nil { + fmt.Printf("Failed to start agent, err:%v.\n", err) + return + } + ic, err = NewIpamClient("http://" + ipamQueryUrl) + if err != nil { + fmt.Printf("Ipam client creation failed %+v", err) + } + + // Run tests. + exitCode := m.Run() + + ipamAgent.Stop() + + os.Exit(exitCode) + +} + +// Handles queries from GetAddressSpace. +func handleIpamAsIDQuery(w http.ResponseWriter, r *http.Request) { + var addressSpaceResp = "{\"LocalDefaultAddressSpace\": \"local\", \"GlobalDefaultAddressSpace\": \"global\"}" + w.Write([]byte(addressSpaceResp)) +} + +// Handles queries from GetPoolID +func handlePoolIDQuery(w http.ResponseWriter, r *http.Request) { + var requestPoolResp = "{\"PoolID\":\"10.0.0.0/16\", \"Pool\": \"\"}" + w.Write([]byte(requestPoolResp)) +} + +// Handles queries from ReserveIPAddress. +func handleReserveIPQuery(w http.ResponseWriter, r *http.Request) { + var reserveIPResp = "{\"Address\":\"10.0.0.2/16\"}" + w.Write([]byte(reserveIPResp)) +} + +// Handles queries from ReleaseIPAddress. +func handleReleaseIPQuery(w http.ResponseWriter, r *http.Request) { + w.Write([]byte("{}")) +} + +// Handles queries from GetIPAddressUtiltization. +func handleIPUtilizationQuery(w http.ResponseWriter, r *http.Request) { + var ipUtilizationResp = "{\"Capacity\":10, \"Available\":7, \"UnhealthyAddresses\":[\"10.0.0.5\",\"10.0.0.6\",\"10.0.0.7\"]}" + w.Write([]byte(ipUtilizationResp)) +} + +// 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) + } + + if w.Body == nil { + return fmt.Errorf("Response body is empty") + } + + return json.NewDecoder(w.Body).Decode(&response) +} + +// Tests IpamClient GetAddressSpace function to get AddressSpaceID +func TestAddressSpaces(t *testing.T) { + asID, err := ic.GetAddressSpace() + if err != nil { + t.Errorf("GetAddressSpace failed with %v\n", err) + return + } + + if asID != "local" { + t.Errorf("GetAddressSpace failed with invalid as id %s", asID) + } +} + +// Tests IpamClient GetPoolID function to get PoolID +func TestGetPoolID(t *testing.T) { + subnet := "10.0.0.0/16" + + asID, err := ic.GetAddressSpace() + if err != nil { + t.Errorf("GetAddressSpace failed with %v\n", err) + return + } + + poolID, err := ic.GetPoolID(asID, subnet) + if err != nil { + t.Errorf("GetPoolID failed with %v\n", err) + return + } + + if poolID != "10.0.0.0/16" { + t.Errorf("GetPoolId failed with invalid pool id %s", poolID) + } +} + +// Tests IpamClient ReserveIPAddress function to request IP for ID +func TestReserveIP(t *testing.T) { + subnet := "10.0.0.0/16" + + asID, err := ic.GetAddressSpace() + if err != nil { + t.Errorf("GetAddressSpace failed with %v\n", err) + return + } + + poolID, err := ic.GetPoolID(asID, subnet) + if err != nil { + t.Errorf("GetPoolID failed with %v\n", err) + return + } + + addr1, err := ic.ReserveIPAddress(poolID, "id1") + if err != nil { + t.Errorf("GetReserveIP failed with %v\n", err) + return + } + if addr1 != "10.0.0.2/16" { + t.Errorf("GetReserveIP returned ivnvalid IP %s\n", addr1) + return + } + addr2, err := ic.ReserveIPAddress(poolID, "id1") + if err != nil { + t.Errorf("GetReserveIP failed with %v\n", err) + return + } + if addr1 != addr2 { + t.Errorf("GetReserveIP with id returned ivnvalid IP1 %s IP2 %s\n", addr1, addr2) + return + } + +} + +// Tests IpamClient ReleaseIPAddress function to release IP associated with ID +func TestReleaseIP(t *testing.T) { + subnet := "10.0.0.0/16" + + asID, err := ic.GetAddressSpace() + if err != nil { + t.Errorf("GetAddressSpace failed with %v\n", err) + return + } + + poolID, err := ic.GetPoolID(asID, subnet) + if err != nil { + t.Errorf("GetPoolID failed with %v\n", err) + return + } + + addr1, err := ic.ReserveIPAddress(poolID, "id1") + if err != nil { + t.Errorf("GetReserveIP failed with %v\n", err) + return + } + if addr1 != "10.0.0.2/16" { + t.Errorf("GetReserveIP returned ivnvalid IP %s\n", addr1) + return + } + + err = ic.ReleaseIPAddress(poolID, "id1") + if err != nil { + t.Errorf("Release reservation failed with %v\n", err) + return + } +} + +// Tests IpamClient GetIPAddressUtilization function to retrieve IP Utilization info +func TestIPAddressUtilization(t *testing.T) { + subnet := "10.0.0.0/16" + + asID, err := ic.GetAddressSpace() + if err != nil { + t.Errorf("GetAddressSpace failed with %v\n", err) + return + } + + poolID, err := ic.GetPoolID(asID, subnet) + if err != nil { + t.Errorf("GetPoolID failed with %v\n", err) + return + } + + capacity, available, unhealthyAddrs, err := ic.GetIPAddressUtilization(poolID) + if err != nil { + t.Errorf("GetIPUtilization failed with %v\n", err) + return + } + + if capacity != 10 && available != 7 && len(unhealthyAddrs) == 3 { + t.Errorf("GetIPUtilization returned invalid either capacity %v / available %v count/ unhealthyaddrs %v \n", capacity, available, unhealthyAddrs) + return + } + + log.Printf("Capacity %v Available %v Unhealthy %v", capacity, available, unhealthyAddrs) +} diff --git a/cns/ipamclient/ipamclient_windows.go b/cns/ipamclient/ipamclient_windows.go new file mode 100644 index 0000000000..e39962aec0 --- /dev/null +++ b/cns/ipamclient/ipamclient_windows.go @@ -0,0 +1,10 @@ +// Copyright 2017 Microsoft. All rights reserved. +// MIT License + +// +build windows + +package ipamclient + +const ( + defaultIpamPluginURL = "http://localhost:48080" +) diff --git a/cns/restserver/api.go b/cns/restserver/api.go index af669a7985..eb7e7e2b51 100644 --- a/cns/restserver/api.go +++ b/cns/restserver/api.go @@ -4,16 +4,17 @@ package restserver // Container Network Service remote API Contract. -const ( - Success = 0 - UnsupportedNetworkType = 1 - InvalidParameter = 2 - UnsupportedEnvironment = 3 - UnreachableHost = 4 - ReservationNotFound = 5 - MalformedSubnet = 8 +const ( + Success = 0 + UnsupportedNetworkType = 1 + InvalidParameter = 2 + UnsupportedEnvironment = 3 + UnreachableHost = 4 + ReservationNotFound = 5 + InvalidReservationID = 6 + MalformedSubnet = 8 UnreachableDockerDaemon = 9 - UnspecifiedNetworkName = 10 - NotFound = 14 - UnexpectedError = 99 + UnspecifiedNetworkName = 10 + NotFound = 14 + UnexpectedError = 99 ) diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index ab771d57af..9fa9b70ada 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -5,12 +5,13 @@ package restserver import ( "fmt" - "time" "net/http" + "time" "github.com/Azure/azure-container-networking/cns" "github.com/Azure/azure-container-networking/cns/dockerclient" "github.com/Azure/azure-container-networking/cns/imdsclient" + "github.com/Azure/azure-container-networking/cns/ipamclient" "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" @@ -26,16 +27,19 @@ type httpRestService struct { *cns.Service dockerClient *dockerclient.DockerClient imdsClient *imdsclient.ImdsClient - store store.KeyValueStore - state httpRestServiceState + ipamClient *ipamclient.IpamClient + store store.KeyValueStore + state httpRestServiceState + asID string + poolID string } // httpRestServiceState contains the state we would like to persist. type httpRestServiceState struct { - Location string - NetworkType string - Initialized bool - TimeStamp time.Time + Location string + NetworkType string + Initialized bool + TimeStamp time.Time } // HTTPService describes the min API interface that every service should have. @@ -52,15 +56,21 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { imdsClient := &imdsclient.ImdsClient{} dc, err := dockerclient.NewDefaultDockerClient(imdsClient) - if(err != nil){ + if err != nil { + return nil, err + } + + ic, err := ipamclient.NewIpamClient("") + if err != nil { return nil, err } - + return &httpRestService{ - Service: service, - store: service.Service.Store, + Service: service, + store: service.Service.Store, dockerClient: dc, - imdsClient: imdsClient, + imdsClient: imdsClient, + ipamClient: ic, }, nil } @@ -110,7 +120,7 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re case "POST": log.Printf("[Azure CNS] POST received for SetEnvironment.") service.state.Location = req.Location - service.state.NetworkType = req.NetworkType + service.state.NetworkType = req.NetworkType service.state.Initialized = true service.saveState() default: @@ -130,50 +140,50 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req returnCode := 0 returnMessage := "" - if(service.state.Initialized) { - var req cns.CreateNetworkRequest + if service.state.Initialized { + var req cns.CreateNetworkRequest err = service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) - + if err != nil { returnMessage = fmt.Sprintf("[Azure CNS] Error. Unable to decode input request.") returnCode = InvalidParameter } else { switch r.Method { - case "POST": - dc := service.dockerClient - err = dc.NetworkExists(req.NetworkName) - - // Network does not exist. - if(err != nil) { - switch service.state.NetworkType { - case "Underlay": - switch service.state.Location { - case "Azure": - log.Printf("[Azure CNS] Goign to create network with name %v.", req.NetworkName) - err = dc.CreateNetwork(req.NetworkName) - if(err != nil) { - returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) - returnCode = UnexpectedError - } - case "StandAlone": - returnMessage = fmt.Sprintf("[Azure CNS] Error. Underlay network is not supported in StandAlone environment. %v.", err.Error()) - returnCode = UnsupportedEnvironment - } - case "Overlay": - returnMessage = fmt.Sprintf("[Azure CNS] Error. Overlay support not yet available. %v.", err.Error()) - returnCode = UnsupportedEnvironment + case "POST": + dc := service.dockerClient + err = dc.NetworkExists(req.NetworkName) + + // Network does not exist. + if err != nil { + switch service.state.NetworkType { + case "Underlay": + switch service.state.Location { + case "Azure": + log.Printf("[Azure CNS] Goign to create network with name %v.", req.NetworkName) + err = dc.CreateNetwork(req.NetworkName) + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) + returnCode = UnexpectedError + } + case "StandAlone": + returnMessage = fmt.Sprintf("[Azure CNS] Error. Underlay network is not supported in StandAlone environment. %v.", err.Error()) + returnCode = UnsupportedEnvironment } - } else { - returnMessage = fmt.Sprintf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) - log.Printf(returnMessage) + case "Overlay": + returnMessage = fmt.Sprintf("[Azure CNS] Error. Overlay support not yet available. %v.", err.Error()) + returnCode = UnsupportedEnvironment } - - default: - returnMessage = "[Azure CNS] Error. CreateNetwork did not receive a POST." - returnCode = InvalidParameter + } else { + returnMessage = fmt.Sprintf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) + log.Printf(returnMessage) + } + + default: + returnMessage = "[Azure CNS] Error. CreateNetwork did not receive a POST." + returnCode = InvalidParameter } - } + } } else { returnMessage = fmt.Sprintf("[Azure CNS] Error. CNS is not yet initialized with environment.") @@ -181,8 +191,8 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req } resp := &cns.Response{ - ReturnCode: returnCode, - Message: returnMessage, + ReturnCode: returnCode, + Message: returnMessage, } err = service.Listener.Encode(w, &resp) @@ -205,35 +215,35 @@ func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Req } switch r.Method { - case "POST": - dc := service.dockerClient - err := dc.NetworkExists(req.NetworkName) - - // Network does exist - if(err == nil) { - log.Printf("[Azure CNS] Goign to delete network with name %v.", req.NetworkName) - err := dc.DeleteNetwork(req.NetworkName) - if(err != nil) { - returnMessage = fmt.Sprintf("[Azure CNS] Error. DeleteNetwork failed %v.", err.Error()) - returnCode = UnexpectedError - } + case "POST": + dc := service.dockerClient + err := dc.NetworkExists(req.NetworkName) + + // Network does exist + if err == nil { + log.Printf("[Azure CNS] Goign to delete network with name %v.", req.NetworkName) + err := dc.DeleteNetwork(req.NetworkName) + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. DeleteNetwork failed %v.", err.Error()) + returnCode = UnexpectedError + } + } else { + if err == fmt.Errorf("Network not found") { + log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v.", req.NetworkName) } else { - if(err == fmt.Errorf("Network not found")){ - log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v.", req.NetworkName) - } else { - returnCode = UnexpectedError - returnMessage = err.Error() - } + returnCode = UnexpectedError + returnMessage = err.Error() } - - default: - returnMessage = "[Azure CNS] Error. DeleteNetwork did not receive a POST." - returnCode = InvalidParameter + } + + default: + returnMessage = "[Azure CNS] Error. DeleteNetwork did not receive a POST." + returnCode = InvalidParameter } resp := &cns.Response{ - ReturnCode: returnCode, - Message: returnMessage, + ReturnCode: returnCode, + Message: returnMessage, } err = service.Listener.Encode(w, &resp) @@ -246,6 +256,9 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. log.Printf("[Azure CNS] reserveIPAddress") var req cns.ReserveIPAddressRequest + returnMessage := "" + returnCode := 0 + addr := "" err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) @@ -254,13 +267,53 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. return } + if req.ReservationID == "" { + returnCode = InvalidReservationID + returnMessage = fmt.Sprintf("[Azure CNS] Error. ReservationId is empty") + } + switch r.Method { - case "POST": - default: + case "POST": + ic := service.ipamClient + + ifInfo, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. GetPrimaryIfaceInfo failed %v", err.Error()) + returnCode = UnexpectedError + break + } + + asID, err := ic.GetAddressSpace() + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. GetAddressSpace failed %v", err.Error()) + returnCode = UnexpectedError + break + } + + poolID, err := ic.GetPoolID(asID, ifInfo.Subnet) + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. GetPoolID failed %v", err.Error()) + returnCode = UnexpectedError + break + } + + addr, err = ic.ReserveIPAddress(poolID, req.ReservationID) + if err != nil { + returnMessage = fmt.Sprintf("ReserveIpAddress failed with %+v", err.Error()) + returnCode = UnexpectedError + } + + default: + returnMessage = "[Azure CNS] Error. ReserveIP did not receive a POST." + returnCode = InvalidParameter + } - resp := cns.Response{ReturnCode: 0} - reserveResp := &cns.ReserveIPAddressResponse{Response: resp, IPAddress: "0.0.0.0"} + resp := cns.Response{ + ReturnCode: returnCode, + Message: returnMessage, + } + reserveResp := &cns.ReserveIPAddressResponse{Response: resp, IPAddress: addr} err = service.Listener.Encode(w, &reserveResp) log.Response(service.Name, reserveResp, err) @@ -269,22 +322,61 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. // Handles release ip reservation requests. func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] releaseIPAddress") + var req cns.ReleaseIPAddressRequest + returnMessage := "" + returnCode := 0 err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) - var req cns.ReleaseIPAddressRequest - if err != nil { return } + if req.ReservationID == "" { + returnCode = InvalidReservationID + returnMessage = fmt.Sprintf("[Azure CNS] Error. ReservationId is empty") + } + switch r.Method { - case "POST": - default: + case "POST": + ic := service.ipamClient + + ifInfo, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. GetPrimaryIfaceInfo failed %v", err.Error()) + returnCode = UnexpectedError + break + } + + asID, err := ic.GetAddressSpace() + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. GetAddressSpace failed %v", err.Error()) + returnCode = UnexpectedError + break + } + + poolID, err := ic.GetPoolID(asID, ifInfo.Subnet) + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. GetPoolID failed %v", err.Error()) + returnCode = UnexpectedError + break + } + + err = ic.ReleaseIPAddress(poolID, req.ReservationID) + if err != nil { + returnMessage = fmt.Sprintf("ReserveIpAddress failed with %+v", err.Error()) + returnCode = UnexpectedError + } + + default: + } + + resp := cns.Response{ + ReturnCode: returnCode, + Message: returnMessage, } - resp := &cns.Response{ReturnCode: 0} err = service.Listener.Encode(w, &resp) log.Response(service.Name, resp, err) @@ -293,40 +385,40 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. // Retrieves the host local ip address. Containers can talk to host using this IP address. func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getHostLocalIP") - log.Request(service.Name, "getHostLocalIP", nil) - + log.Request(service.Name, "getHostLocalIP", nil) + var found bool var errmsg string - hostLocalIP := "0.0.0.0" + hostLocalIP := "0.0.0.0" if service.state.Initialized { switch r.Method { - case "GET": - switch (service.state.NetworkType) { - case "Underlay": - if (service.imdsClient != nil) { - piface, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() - if err == nil { - hostLocalIP = piface.PrimaryIP - found = true; - } else { - log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) - } - } - - case "Overlay": - errmsg = "[Azure-CNS] Overlay is not yet supported." + case "GET": + switch service.state.NetworkType { + case "Underlay": + if service.imdsClient != nil { + piface, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() + if err == nil { + hostLocalIP = piface.PrimaryIP + found = true + } else { + log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) + } } - default: - errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." + case "Overlay": + errmsg = "[Azure-CNS] Overlay is not yet supported." + } + + default: + errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." } } returnCode := 0 if !found { returnCode = NotFound - if(errmsg == ""){ + if errmsg == "" { errmsg = "[Azure-CNS] Unable to get host local ip. Check if environment is initialized.." } } @@ -345,17 +437,60 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re // Handles ip address utilization requests. func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getIPAddressUtilization") - log.Request(service.Name, "getIPAddressUtilization", nil) + log.Request(service.Name, "getIPAddressUtilization", nil) + + returnMessage := "" + returnCode := 0 + capacity := 0 + available := 0 + var unhealthyAddrs []string switch r.Method { - case "GET": - default: + case "GET": + ic := service.ipamClient + + ifInfo, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. GetPrimaryIfaceInfo failed %v", err.Error()) + returnCode = UnexpectedError + break + } + + asID, err := ic.GetAddressSpace() + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. GetAddressSpace failed %v", err.Error()) + returnCode = UnexpectedError + break + } + + poolID, err := ic.GetPoolID(asID, ifInfo.Subnet) + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. GetPoolID failed %v", err.Error()) + returnCode = UnexpectedError + break + } + + capacity, available, unhealthyAddrs, err = ic.GetIPAddressUtilization(poolID) + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. GetIPUtilization failed %v", err.Error()) + returnCode = UnexpectedError + break + } + log.Printf("Capacity %v Available %v UnhealthyAddrs %v", capacity, available, unhealthyAddrs) + + default: + } + + resp := cns.Response{ + ReturnCode: returnCode, + Message: returnMessage, } - resp := cns.Response{ReturnCode: 0} utilResponse := &cns.IPAddressesUtilizationResponse{ Response: resp, - Available: 0, + Available: available, + Reserved: capacity - available, + Unhealthy: len(unhealthyAddrs), } err := service.Listener.Encode(w, &utilResponse) @@ -369,8 +504,8 @@ func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r log.Request(service.Name, "getAvailableIPAddresses", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := cns.Response{ReturnCode: 0} @@ -386,8 +521,8 @@ func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r log.Request(service.Name, "getReservedIPAddresses", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := cns.Response{ReturnCode: 0} @@ -403,8 +538,8 @@ func (service *httpRestService) getUnhealthyIPAddresses(w http.ResponseWriter, r log.Request(service.Name, "getUnhealthyIPAddresses", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := cns.Response{ReturnCode: 0} @@ -420,8 +555,8 @@ func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http log.Request(service.Name, "getAllIPAddresses", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := cns.Response{ReturnCode: 0} @@ -434,11 +569,11 @@ func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http // Handles health report requests. func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getHealthReport") - log.Request(service.Name, "getHealthReport", nil) + log.Request(service.Name, "getHealthReport", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := &cns.Response{ReturnCode: 0} @@ -458,7 +593,7 @@ func (service *httpRestService) saveState() error { } // Update time stamp. - service.state.TimeStamp = time.Now() + service.state.TimeStamp = time.Now() err := service.store.Write(storeKey, &service.state) if err == nil { log.Printf("[Azure CNS] State saved successfully.\n") @@ -487,7 +622,7 @@ func (service *httpRestService) restoreState() error { log.Printf("[Azure CNS] No state to restore.\n") return nil } - + log.Printf("[Azure CNS] Failed to restore state, err:%v\n", err) return err } From f2c1b7faa55b855eeadc315c772e9a893d0cc289 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Wed, 14 Jun 2017 16:02:08 -0700 Subject: [PATCH 21/34] Go format. --- cns/api.go | 40 ++--- cns/dockerclient/api.go | 17 +- cns/dockerclient/dockerclient.go | 22 +-- cns/examples/examples.go | 140 +++++++-------- cns/imdsclient/api.go | 12 +- cns/imdsclient/imdsclient.go | 60 +++---- cns/restserver/api.go | 22 +-- cns/restserver/restserver.go | 280 +++++++++++++++--------------- cns/restserver/restserver_test.go | 71 ++++---- cns/routes/routes.go | 16 +- cns/routes/routes_linux.go | 3 +- cns/routes/routes_test.go | 66 +++---- cns/routes/routes_windows.go | 62 ++++--- cns/service.go | 16 +- cns/service/main.go | 6 +- 15 files changed, 414 insertions(+), 419 deletions(-) diff --git a/cns/api.go b/cns/api.go index dcfd530d87..b5bf42fd39 100644 --- a/cns/api.go +++ b/cns/api.go @@ -4,36 +4,36 @@ package cns // Container Network Service remote API Contract -const ( +const ( SetEnvironmentPath = "/network/environment" CreateNetworkPath = "/network/create" DeleteNetworkPath = "/network/delete" ReserveIPAddressPath = "/network/ip/Reserve" - ReleaseIPAddressPath = "/network/ip/Release" + ReleaseIPAddressPath = "/network/ip/Release" GetHostLocalIPPath = "/network/ip/HostLocal" GetIPAddressUtilizationPath = "/network/ip/Utilization" - GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy" + GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy" GetHealthReportPath = "/network/health" - V1Prefix = "/v0.1" + V1Prefix = "/v0.1" ) // SetEnvironmentRequest describes the Request to set the environment in CNS. type SetEnvironmentRequest struct { - Location string + Location string NetworkType string } // OverlayConfiguration describes configuration for all the nodes that are part of overlay. type OverlayConfiguration struct { - NodeCount int - LocalNodeIP string + NodeCount int + LocalNodeIP string OverlaySubent Subnet - NodeConfig []NodeConfiguration + NodeConfig []NodeConfiguration } // CreateNetworkRequest describes request to create the network. type CreateNetworkRequest struct { - NetworkName string + NetworkName string OverlayConfiguration OverlayConfiguration } @@ -49,7 +49,7 @@ type ReserveIPAddressRequest struct { // ReserveIPAddressResponse describes response to reserve an IP address. type ReserveIPAddressResponse struct { - Response Response + Response Response IPAddress string } @@ -60,47 +60,47 @@ type ReleaseIPAddressRequest struct { // IPAddressesUtilizationResponse describes response for ip address utilization. type IPAddressesUtilizationResponse struct { - Response Response + Response Response Available int - Reserved int + Reserved int Unhealthy int } // GetIPAddressesResponse describes response containing requested ip addresses. type GetIPAddressesResponse struct { - Response Response + Response Response IPAddresses []IPAddress } // HostLocalIPAddressResponse describes reponse that returns the host local IP Address. type HostLocalIPAddressResponse struct { - Response Response + Response Response IPAddress string } // IPAddress Contains information about an ip address. type IPAddress struct { - IPAddress string + IPAddress string ReservationID string } // Subnet contains the ip address and the number of bits in prefix. type Subnet struct { - IPAddress string + IPAddress string PrefixLength int } // NodeConfiguration describes confguration for a node in overlay network. type NodeConfiguration struct { - NodeIP string - NodeID string + NodeIP string + NodeID string NodeSubnet Subnet } // Response describes generic response from CNS. type Response struct { ReturnCode int - Message string + Message string } // OptionMap describes generic options that can be passed to CNS. @@ -109,4 +109,4 @@ type OptionMap map[string]interface{} // Response to a failed request. type errorResponse struct { Err string -} \ No newline at end of file +} diff --git a/cns/dockerclient/api.go b/cns/dockerclient/api.go index 4cb8acb7e7..090c423210 100644 --- a/cns/dockerclient/api.go +++ b/cns/dockerclient/api.go @@ -3,12 +3,11 @@ package dockerclient -import ( -) +import () const ( - createNetworkPath = "/networks/create" - inspectNetworkPath = "/networks/" + createNetworkPath = "/networks/create" + inspectNetworkPath = "/networks/" ) // Config describes subnet/gateway for ipam. @@ -19,15 +18,15 @@ type Config struct { // IPAM describes ipam details type IPAM struct { Driver string - Config []Config + Config []Config } // NetworkConfiguration describes configuration for docker network create. type NetworkConfiguration struct { - Name string - Driver string - IPAM IPAM - Internal bool + Name string + Driver string + IPAM IPAM + Internal bool } // DockerErrorResponse defines the error response retunred by docker. diff --git a/cns/dockerclient/dockerclient.go b/cns/dockerclient/dockerclient.go index 3c2c2c9d05..d940f0b02c 100644 --- a/cns/dockerclient/dockerclient.go +++ b/cns/dockerclient/dockerclient.go @@ -45,7 +45,7 @@ func (dockerClient *DockerClient) NetworkExists(networkName string) error { log.Printf("[Azure CNS] NetworkExists") res, err := http.Get( - dockerClient.connectionURL+inspectNetworkPath+networkName) + dockerClient.connectionURL + inspectNetworkPath + networkName) if err != nil { log.Printf("[Azure CNS] Error received from http Post for docker network inspect %v %v", networkName, err.Error()) @@ -75,13 +75,13 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { if err != nil { return err } - + config := &Config{ - Subnet: primaryNic.Subnet, + Subnet: primaryNic.Subnet, } configs := make([]Config, 1) - configs[0] = *config + configs[0] = *config ipamConfig := &IPAM{ Driver: defaultIpamPlugin, Config: configs, @@ -93,7 +93,7 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { IPAM: *ipamConfig, Internal: true, } - + log.Printf("[Azure CNS] Going to create network with config: %+v", netConfig) netConfigJSON := new(bytes.Buffer) @@ -116,11 +116,11 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { err = json.NewDecoder(res.Body).Decode(&createNetworkResponse) var ermsg string ermsg = "" - if(err != nil){ + if err != nil { ermsg = err.Error() } return fmt.Errorf("[Azure CNS] Create docker network failed with error code %v - %v - %v", - res.StatusCode, createNetworkResponse.message, ermsg) + res.StatusCode, createNetworkResponse.message, ermsg) } return nil @@ -128,9 +128,9 @@ func (dockerClient *DockerClient) CreateNetwork(networkName string) error { // DeleteNetwork creates a network using docker network create. func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { - log.Printf("[Azure CNS] DeleteNetwork") + log.Printf("[Azure CNS] DeleteNetwork") - url := dockerClient.connectionURL+inspectNetworkPath+networkName + url := dockerClient.connectionURL + inspectNetworkPath + networkName req, err := http.NewRequest("DELETE", url, nil) if err != nil { log.Printf("[Azure CNS] Error received while creating http DELETE request for network delete %v %v", networkName, err.Error()) @@ -151,6 +151,6 @@ func (dockerClient *DockerClient) DeleteNetwork(networkName string) error { return fmt.Errorf("[Azure CNS] Network not found %v", networkName) } - return fmt.Errorf("[Azure CNS] Unknown return code from docker delete network %v: ret = %d", - networkName, res.StatusCode) + return fmt.Errorf("[Azure CNS] Unknown return code from docker delete network %v: ret = %d", + networkName, res.StatusCode) } diff --git a/cns/examples/examples.go b/cns/examples/examples.go index 9ad6fedb08..d086f104e9 100644 --- a/cns/examples/examples.go +++ b/cns/examples/examples.go @@ -5,8 +5,8 @@ package main import ( "bytes" - "fmt" "encoding/json" + "fmt" "net/http" "github.com/Azure/azure-container-networking/cns" @@ -18,46 +18,46 @@ const ( // These are example showing how to use CNS APIs by clients -func setEnvironment() error{ - envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} - envRequestJSON := new(bytes.Buffer) - json.NewEncoder(envRequestJSON).Encode(envRequest) - res, err := - http.Post( - defaultCNSServerURL+cns.SetEnvironmentPath, - "application/json; charset=utf-8", - envRequestJSON) +func setEnvironment() error { + envRequest := cns.SetEnvironmentRequest{Location: "Azure", NetworkType: "Underlay"} + envRequestJSON := new(bytes.Buffer) + json.NewEncoder(envRequestJSON).Encode(envRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.SetEnvironmentPath, + "application/json; charset=utf-8", + envRequestJSON) if err != nil { fmt.Printf("Error received in Set Env: %v ", err.Error()) return err - } + } var setEnvironmentResponse cns.Response err = json.NewDecoder(res.Body).Decode(&setEnvironmentResponse) - if err != nil{ + if err != nil { fmt.Printf("Error received in decoding response from SetEnvironment: %v ", err.Error()) return err } - fmt.Printf("Response for SetEnvironment: %+v\n", setEnvironmentResponse) + fmt.Printf("Response for SetEnvironment: %+v\n", setEnvironmentResponse) return nil } -func createNetwork() error{ - netRequest := cns.CreateNetworkRequest{NetworkName:"azurenet"} - netRequestJSON := new(bytes.Buffer) - json.NewEncoder(netRequestJSON).Encode(netRequest) - res, err := - http.Post( - defaultCNSServerURL+cns.CreateNetworkPath, - "application/json; charset=utf-8", - netRequestJSON) +func createNetwork() error { + netRequest := cns.CreateNetworkRequest{NetworkName: "azurenet"} + netRequestJSON := new(bytes.Buffer) + json.NewEncoder(netRequestJSON).Encode(netRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.CreateNetworkPath, + "application/json; charset=utf-8", + netRequestJSON) if err != nil { fmt.Printf("Error received in CreateNetwork post: %v ", err.Error()) return err - } - + } + var createNetworkResponse cns.Response err = json.NewDecoder(res.Body).Decode(&createNetworkResponse) - if err != nil{ + if err != nil { fmt.Printf("Error received in decoding response from CreateNEtwork: %v ", err.Error()) return err } @@ -65,23 +65,23 @@ func createNetwork() error{ return nil } -func deleteNetwork() error{ - netRequest := cns.DeleteNetworkRequest{NetworkName:"azurenet"} - netRequestJSON := new(bytes.Buffer) - json.NewEncoder(netRequestJSON).Encode(netRequest) - res, err := - http.Post( - defaultCNSServerURL+cns.DeleteNetworkPath, - "application/json; charset=utf-8", - netRequestJSON) +func deleteNetwork() error { + netRequest := cns.DeleteNetworkRequest{NetworkName: "azurenet"} + netRequestJSON := new(bytes.Buffer) + json.NewEncoder(netRequestJSON).Encode(netRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.DeleteNetworkPath, + "application/json; charset=utf-8", + netRequestJSON) if err != nil { fmt.Printf("Error received in DeleteNetwork post: %v ", err.Error()) return err - } - + } + var deleteNetworkResponse cns.Response err = json.NewDecoder(res.Body).Decode(&deleteNetworkResponse) - if err != nil{ + if err != nil { fmt.Printf("Error received in decoding response from DeleteNetwork: %v ", err.Error()) return err } @@ -89,22 +89,22 @@ func deleteNetwork() error{ return nil } -func reserveIPAddress() error{ - reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID:"ip01"} - reserveIPRequestJSON := new(bytes.Buffer) - json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) - res, err := - http.Post( - defaultCNSServerURL+cns.ReserveIPAddressPath, - "application/json; charset=utf-8", - reserveIPRequestJSON) +func reserveIPAddress() error { + reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID: "ip01"} + reserveIPRequestJSON := new(bytes.Buffer) + json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.ReserveIPAddressPath, + "application/json; charset=utf-8", + reserveIPRequestJSON) if err != nil { fmt.Printf("Error received in reserveIPAddress post: %v ", err.Error()) return err - } + } var reserveIPAddressResponse cns.ReserveIPAddressResponse err = json.NewDecoder(res.Body).Decode(&reserveIPAddressResponse) - if err != nil{ + if err != nil { fmt.Printf("Error received in decoding response from reserveIPAddress: %v ", err.Error()) return err } @@ -112,22 +112,22 @@ func reserveIPAddress() error{ return nil } -func releaseIPAddress() error{ - releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID:"ip01"} - releaseIPAddressRequestJSON := new(bytes.Buffer) - json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) - res, err := - http.Post( - defaultCNSServerURL+cns.ReleaseIPAddressPath, - "application/json; charset=utf-8", - releaseIPAddressRequestJSON) +func releaseIPAddress() error { + releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID: "ip01"} + releaseIPAddressRequestJSON := new(bytes.Buffer) + json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) + res, err := + http.Post( + defaultCNSServerURL+cns.ReleaseIPAddressPath, + "application/json; charset=utf-8", + releaseIPAddressRequestJSON) if err != nil { fmt.Printf("Error received in releaseIPAddress post: %v ", err.Error()) return err - } - var releaseIPAddressResponse cns.Response + } + var releaseIPAddressResponse cns.Response err = json.NewDecoder(res.Body).Decode(&releaseIPAddressResponse) - if err != nil{ + if err != nil { fmt.Printf("Error received in decoding response from releaseIPAddress: %v ", err.Error()) return err } @@ -135,9 +135,9 @@ func releaseIPAddress() error{ return nil } -func getIPAddressUtilization() error{ - res, err := - http.Get(defaultCNSServerURL+cns.GetIPAddressUtilizationPath) +func getIPAddressUtilization() error { + res, err := + http.Get(defaultCNSServerURL + cns.GetIPAddressUtilizationPath) if err != nil { fmt.Printf("Error received in getIPAddressUtilization GET: %v ", err.Error()) return err @@ -152,9 +152,9 @@ func getIPAddressUtilization() error{ return nil } -func getHostLocalIP() error{ - res, err := - http.Get(defaultCNSServerURL+cns.GetHostLocalIPPath) +func getHostLocalIP() error { + res, err := + http.Get(defaultCNSServerURL + cns.GetHostLocalIPPath) if err != nil { fmt.Printf("Error received in getHostLocalIP GET: %v ", err.Error()) return err @@ -169,14 +169,14 @@ func getHostLocalIP() error{ return nil } -func getUnhealthyIPAddresses() error{ - res, err := - http.Get(defaultCNSServerURL+cns.GetUnhealthyIPAddressesPath) +func getUnhealthyIPAddresses() error { + res, err := + http.Get(defaultCNSServerURL + cns.GetUnhealthyIPAddressesPath) if err != nil { fmt.Printf("Error received in GetUnhealthyIPAddresses IP Addresses: %v ", err.Error()) return err } - var getIPAddressesResponse cns.GetIPAddressesResponse + var getIPAddressesResponse cns.GetIPAddressesResponse err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) if err != nil { fmt.Printf("Error received in decoding response from GetUnhealthyIPAddresses: %v ", err.Error()) diff --git a/cns/imdsclient/api.go b/cns/imdsclient/api.go index dec8d918fa..445461eab7 100644 --- a/cns/imdsclient/api.go +++ b/cns/imdsclient/api.go @@ -18,11 +18,11 @@ type ImdsClient struct { // InterfaceInfo specifies the information about an interface as returned by Host Agent. type InterfaceInfo struct { - Subnet string - Gateway string - IsPrimary bool - PrimaryIP string - SecondaryIPs[] string + Subnet string + Gateway string + IsPrimary bool + PrimaryIP string + SecondaryIPs []string } // Azure host agent XML document format. @@ -44,4 +44,4 @@ type xmlDocument struct { } } } -} \ No newline at end of file +} diff --git a/cns/imdsclient/imdsclient.go b/cns/imdsclient/imdsclient.go index 4ddc71163f..2e1335f5cc 100644 --- a/cns/imdsclient/imdsclient.go +++ b/cns/imdsclient/imdsclient.go @@ -4,98 +4,98 @@ package imdsclient import ( - "fmt" - "strings" "encoding/xml" + "fmt" "net/http" + "strings" "github.com/Azure/azure-container-networking/log" ) // GetPrimaryInterfaceInfoFromHost retrieves subnet and gateway of primary NIC from Host. -func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, error) { +func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromHost() (*InterfaceInfo, error) { log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromHost") interfaceInfo := &InterfaceInfo{} resp, err := http.Get(hostQueryURL) - if(err != nil){ + if err != nil { return nil, err } log.Printf("[Azure CNS] Response received from NMAgent: %v", resp.Body) - + var doc xmlDocument decoder := xml.NewDecoder(resp.Body) err = decoder.Decode(&doc) if err != nil { return nil, err } - + foundPrimaryInterface := false // For each interface. - for _, i := range doc.Interface { + for _, i := range doc.Interface { // Find primary Interface. if i.IsPrimary { interfaceInfo.IsPrimary = true // Get the first subnet. for _, s := range i.IPSubnet { - interfaceInfo.Subnet = s.Prefix + interfaceInfo.Subnet = s.Prefix malformedSubnetError := fmt.Errorf("Malformed subnet received from host %s", s.Prefix) st := strings.Split(s.Prefix, "/") - if(len(st) != 2){ + if len(st) != 2 { return nil, malformedSubnetError } - - ip := strings.Split(st[0], ".") - if(len(ip) != 4){ + + ip := strings.Split(st[0], ".") + if len(ip) != 4 { return nil, malformedSubnetError } interfaceInfo.Gateway = fmt.Sprintf("%s.%s.%s.1", ip[0], ip[1], ip[2]) - for _,ip := range s.IPAddress { + for _, ip := range s.IPAddress { if ip.IsPrimary == true { - interfaceInfo.PrimaryIP = ip.Address + interfaceInfo.PrimaryIP = ip.Address } } - + imdsClient.primaryInterface = interfaceInfo - break; + break } - + foundPrimaryInterface = true - break; + break } - } + } var er error er = nil - if (!foundPrimaryInterface) { + if !foundPrimaryInterface { er = fmt.Errorf("Unable to find primary NIC") - } + } - return interfaceInfo, er + return interfaceInfo, er } // GetPrimaryInterfaceInfoFromMemory retrieves subnet and gateway of primary NIC that is saved in memory. -func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromMemory() (*InterfaceInfo, error) { +func (imdsClient *ImdsClient) GetPrimaryInterfaceInfoFromMemory() (*InterfaceInfo, error) { log.Printf("[Azure CNS] GetPrimaryInterfaceInfoFromMemory") var iface *InterfaceInfo var err error - if(imdsClient.primaryInterface == nil) { + if imdsClient.primaryInterface == nil { log.Debugf("Azure-CNS] Primary interface in memory does not exist. Will get it from Host.") - iface, err = imdsClient.GetPrimaryInterfaceInfoFromHost() + iface, err = imdsClient.GetPrimaryInterfaceInfoFromHost() if err != nil { log.Printf("[Azure-CNS] Unable to retrive primary interface info.") - } else { - log.Debugf("Azure-CNS] Primary interface received from HOST: %+v.", iface) - } + } else { + log.Debugf("Azure-CNS] Primary interface received from HOST: %+v.", iface) + } } else { iface = imdsClient.primaryInterface } - + return iface, err -} \ No newline at end of file +} diff --git a/cns/restserver/api.go b/cns/restserver/api.go index af669a7985..11d1b11b5a 100644 --- a/cns/restserver/api.go +++ b/cns/restserver/api.go @@ -4,16 +4,16 @@ package restserver // Container Network Service remote API Contract. -const ( - Success = 0 - UnsupportedNetworkType = 1 - InvalidParameter = 2 - UnsupportedEnvironment = 3 - UnreachableHost = 4 - ReservationNotFound = 5 - MalformedSubnet = 8 +const ( + Success = 0 + UnsupportedNetworkType = 1 + InvalidParameter = 2 + UnsupportedEnvironment = 3 + UnreachableHost = 4 + ReservationNotFound = 5 + MalformedSubnet = 8 UnreachableDockerDaemon = 9 - UnspecifiedNetworkName = 10 - NotFound = 14 - UnexpectedError = 99 + UnspecifiedNetworkName = 10 + NotFound = 14 + UnexpectedError = 99 ) diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 89e700ce7b..4aaa05a721 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -5,16 +5,16 @@ package restserver import ( "fmt" - "time" "net/http" + "time" "github.com/Azure/azure-container-networking/cns" "github.com/Azure/azure-container-networking/cns/dockerclient" "github.com/Azure/azure-container-networking/cns/imdsclient" + "github.com/Azure/azure-container-networking/cns/routes" "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" - "github.com/Azure/azure-container-networking/cns/routes" ) const ( @@ -28,16 +28,16 @@ type httpRestService struct { dockerClient *dockerclient.DockerClient imdsClient *imdsclient.ImdsClient routingTable *routes.RoutingTable - store store.KeyValueStore - state httpRestServiceState + store store.KeyValueStore + state httpRestServiceState } // httpRestServiceState contains the state we would like to persist. type httpRestServiceState struct { - Location string - NetworkType string - Initialized bool - TimeStamp time.Time + Location string + NetworkType string + Initialized bool + TimeStamp time.Time } // HTTPService describes the min API interface that every service should have. @@ -55,15 +55,15 @@ func NewHTTPRestService(config *common.ServiceConfig) (HTTPService, error) { imdsClient := &imdsclient.ImdsClient{} routingTable := &routes.RoutingTable{} dc, err := dockerclient.NewDefaultDockerClient(imdsClient) - if(err != nil){ + if err != nil { return nil, err } - + return &httpRestService{ - Service: service, - store: service.Service.Store, + Service: service, + store: service.Service.Store, dockerClient: dc, - imdsClient: imdsClient, + imdsClient: imdsClient, routingTable: routingTable, }, nil } @@ -91,14 +91,14 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { listener.AddHandler(cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) // handlers for v0.1 - listener.AddHandler(cns.V1Prefix + cns.SetEnvironmentPath, service.setEnvironment) - listener.AddHandler(cns.V1Prefix + cns.CreateNetworkPath, service.createNetwork) - listener.AddHandler(cns.V1Prefix + cns.DeleteNetworkPath, service.deleteNetwork) - listener.AddHandler(cns.V1Prefix + cns.ReserveIPAddressPath, service.reserveIPAddress) - listener.AddHandler(cns.V1Prefix + cns.ReleaseIPAddressPath, service.releaseIPAddress) - listener.AddHandler(cns.V1Prefix + cns.GetHostLocalIPPath, service.getHostLocalIP) - listener.AddHandler(cns.V1Prefix + cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) - listener.AddHandler(cns.V1Prefix + cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) + listener.AddHandler(cns.V1Prefix+cns.SetEnvironmentPath, service.setEnvironment) + listener.AddHandler(cns.V1Prefix+cns.CreateNetworkPath, service.createNetwork) + listener.AddHandler(cns.V1Prefix+cns.DeleteNetworkPath, service.deleteNetwork) + listener.AddHandler(cns.V1Prefix+cns.ReserveIPAddressPath, service.reserveIPAddress) + listener.AddHandler(cns.V1Prefix+cns.ReleaseIPAddressPath, service.releaseIPAddress) + listener.AddHandler(cns.V1Prefix+cns.GetHostLocalIPPath, service.getHostLocalIP) + listener.AddHandler(cns.V1Prefix+cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) + listener.AddHandler(cns.V1Prefix+cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) log.Printf("[Azure CNS] Listening.") return nil @@ -126,7 +126,7 @@ func (service *httpRestService) setEnvironment(w http.ResponseWriter, r *http.Re case "POST": log.Printf("[Azure CNS] POST received for SetEnvironment.") service.state.Location = req.Location - service.state.NetworkType = req.NetworkType + service.state.NetworkType = req.NetworkType service.state.Initialized = true service.saveState() default: @@ -146,67 +146,67 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req returnCode := 0 returnMessage := "" - if(service.state.Initialized) { - var req cns.CreateNetworkRequest + if service.state.Initialized { + var req cns.CreateNetworkRequest err = service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) - + if err != nil { returnMessage = fmt.Sprintf("[Azure CNS] Error. Unable to decode input request.") returnCode = InvalidParameter } else { switch r.Method { - case "POST": - dc := service.dockerClient - rt := service.routingTable - err = dc.NetworkExists(req.NetworkName) - - // Network does not exist. - if(err != nil) { - switch service.state.NetworkType { - case "Underlay": - switch service.state.Location { - case "Azure": - log.Printf("[Azure CNS] Goign to create network with name %v.", req.NetworkName) - - err = rt.GetRoutingTable() - if(err != nil) { - // We should not fail the call to create network for this. - // This is because restoring routes is a fallback mechanism in case - // network driver is not behaving as expected. - // The responsibility to restore routes is with network driver. - log.Printf("[Azure CNS] Unable to get routing table from node, %+v.", err.Error()) - } - - err = dc.CreateNetwork(req.NetworkName) - if(err != nil) { - returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) - returnCode = UnexpectedError - } - - err = rt.RestoreRoutingTable() - if(err != nil) { - log.Printf("[Azure CNS] Unable to restore routing table on node, %+v.", err.Error()) - } - - case "StandAlone": - returnMessage = fmt.Sprintf("[Azure CNS] Error. Underlay network is not supported in StandAlone environment. %v.", err.Error()) - returnCode = UnsupportedEnvironment - } - case "Overlay": - returnMessage = fmt.Sprintf("[Azure CNS] Error. Overlay support not yet available. %v.", err.Error()) - returnCode = UnsupportedEnvironment + case "POST": + dc := service.dockerClient + rt := service.routingTable + err = dc.NetworkExists(req.NetworkName) + + // Network does not exist. + if err != nil { + switch service.state.NetworkType { + case "Underlay": + switch service.state.Location { + case "Azure": + log.Printf("[Azure CNS] Goign to create network with name %v.", req.NetworkName) + + err = rt.GetRoutingTable() + if err != nil { + // We should not fail the call to create network for this. + // This is because restoring routes is a fallback mechanism in case + // network driver is not behaving as expected. + // The responsibility to restore routes is with network driver. + log.Printf("[Azure CNS] Unable to get routing table from node, %+v.", err.Error()) + } + + err = dc.CreateNetwork(req.NetworkName) + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. CreateNetwork failed %v.", err.Error()) + returnCode = UnexpectedError + } + + err = rt.RestoreRoutingTable() + if err != nil { + log.Printf("[Azure CNS] Unable to restore routing table on node, %+v.", err.Error()) + } + + case "StandAlone": + returnMessage = fmt.Sprintf("[Azure CNS] Error. Underlay network is not supported in StandAlone environment. %v.", err.Error()) + returnCode = UnsupportedEnvironment } - } else { - returnMessage = fmt.Sprintf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) - log.Printf(returnMessage) + case "Overlay": + returnMessage = fmt.Sprintf("[Azure CNS] Error. Overlay support not yet available. %v.", err.Error()) + returnCode = UnsupportedEnvironment } - - default: - returnMessage = "[Azure CNS] Error. CreateNetwork did not receive a POST." - returnCode = InvalidParameter + } else { + returnMessage = fmt.Sprintf("[Azure CNS] Received a request to create an already existing network %v", req.NetworkName) + log.Printf(returnMessage) + } + + default: + returnMessage = "[Azure CNS] Error. CreateNetwork did not receive a POST." + returnCode = InvalidParameter } - } + } } else { returnMessage = fmt.Sprintf("[Azure CNS] Error. CNS is not yet initialized with environment.") @@ -214,8 +214,8 @@ func (service *httpRestService) createNetwork(w http.ResponseWriter, r *http.Req } resp := &cns.Response{ - ReturnCode: returnCode, - Message: returnMessage, + ReturnCode: returnCode, + Message: returnMessage, } err = service.Listener.Encode(w, &resp) @@ -238,35 +238,35 @@ func (service *httpRestService) deleteNetwork(w http.ResponseWriter, r *http.Req } switch r.Method { - case "POST": - dc := service.dockerClient - err := dc.NetworkExists(req.NetworkName) - - // Network does exist - if(err == nil) { - log.Printf("[Azure CNS] Goign to delete network with name %v.", req.NetworkName) - err := dc.DeleteNetwork(req.NetworkName) - if(err != nil) { - returnMessage = fmt.Sprintf("[Azure CNS] Error. DeleteNetwork failed %v.", err.Error()) - returnCode = UnexpectedError - } + case "POST": + dc := service.dockerClient + err := dc.NetworkExists(req.NetworkName) + + // Network does exist + if err == nil { + log.Printf("[Azure CNS] Goign to delete network with name %v.", req.NetworkName) + err := dc.DeleteNetwork(req.NetworkName) + if err != nil { + returnMessage = fmt.Sprintf("[Azure CNS] Error. DeleteNetwork failed %v.", err.Error()) + returnCode = UnexpectedError + } + } else { + if err == fmt.Errorf("Network not found") { + log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v.", req.NetworkName) } else { - if(err == fmt.Errorf("Network not found")){ - log.Printf("[Azure CNS] Received a request to delete network that does not exist: %v.", req.NetworkName) - } else { - returnCode = UnexpectedError - returnMessage = err.Error() - } + returnCode = UnexpectedError + returnMessage = err.Error() } - - default: - returnMessage = "[Azure CNS] Error. DeleteNetwork did not receive a POST." - returnCode = InvalidParameter + } + + default: + returnMessage = "[Azure CNS] Error. DeleteNetwork did not receive a POST." + returnCode = InvalidParameter } resp := &cns.Response{ - ReturnCode: returnCode, - Message: returnMessage, + ReturnCode: returnCode, + Message: returnMessage, } err = service.Listener.Encode(w, &resp) @@ -288,8 +288,8 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. } switch r.Method { - case "POST": - default: + case "POST": + default: } resp := cns.Response{ReturnCode: 0} @@ -312,8 +312,8 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. } switch r.Method { - case "POST": - default: + case "POST": + default: } resp := &cns.Response{ReturnCode: 0} @@ -325,40 +325,40 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. // Retrieves the host local ip address. Containers can talk to host using this IP address. func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getHostLocalIP") - log.Request(service.Name, "getHostLocalIP", nil) - + log.Request(service.Name, "getHostLocalIP", nil) + var found bool var errmsg string - hostLocalIP := "0.0.0.0" + hostLocalIP := "0.0.0.0" if service.state.Initialized { switch r.Method { - case "GET": - switch (service.state.NetworkType) { - case "Underlay": - if (service.imdsClient != nil) { - piface, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() - if err == nil { - hostLocalIP = piface.PrimaryIP - found = true; - } else { - log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) - } - } - - case "Overlay": - errmsg = "[Azure-CNS] Overlay is not yet supported." + case "GET": + switch service.state.NetworkType { + case "Underlay": + if service.imdsClient != nil { + piface, err := service.imdsClient.GetPrimaryInterfaceInfoFromMemory() + if err == nil { + hostLocalIP = piface.PrimaryIP + found = true + } else { + log.Printf("[Azure-CNS] Received error from GetPrimaryInterfaceInfoFromMemory. err: %v", err.Error()) + } } - default: - errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." + case "Overlay": + errmsg = "[Azure-CNS] Overlay is not yet supported." + } + + default: + errmsg = "[Azure-CNS] GetHostLocalIP API expects a GET." } } returnCode := 0 if !found { returnCode = NotFound - if(errmsg == ""){ + if errmsg == "" { errmsg = "[Azure-CNS] Unable to get host local ip. Check if environment is initialized.." } } @@ -377,11 +377,11 @@ func (service *httpRestService) getHostLocalIP(w http.ResponseWriter, r *http.Re // Handles ip address utilization requests. func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getIPAddressUtilization") - log.Request(service.Name, "getIPAddressUtilization", nil) + log.Request(service.Name, "getIPAddressUtilization", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := cns.Response{ReturnCode: 0} @@ -401,8 +401,8 @@ func (service *httpRestService) getAvailableIPAddresses(w http.ResponseWriter, r log.Request(service.Name, "getAvailableIPAddresses", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := cns.Response{ReturnCode: 0} @@ -418,8 +418,8 @@ func (service *httpRestService) getReservedIPAddresses(w http.ResponseWriter, r log.Request(service.Name, "getReservedIPAddresses", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := cns.Response{ReturnCode: 0} @@ -435,8 +435,8 @@ func (service *httpRestService) getUnhealthyIPAddresses(w http.ResponseWriter, r log.Request(service.Name, "getUnhealthyIPAddresses", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := cns.Response{ReturnCode: 0} @@ -452,8 +452,8 @@ func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http log.Request(service.Name, "getAllIPAddresses", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := cns.Response{ReturnCode: 0} @@ -466,11 +466,11 @@ func (service *httpRestService) getAllIPAddresses(w http.ResponseWriter, r *http // Handles health report requests. func (service *httpRestService) getHealthReport(w http.ResponseWriter, r *http.Request) { log.Printf("[Azure CNS] getHealthReport") - log.Request(service.Name, "getHealthReport", nil) + log.Request(service.Name, "getHealthReport", nil) switch r.Method { - case "GET": - default: + case "GET": + default: } resp := &cns.Response{ReturnCode: 0} @@ -490,7 +490,7 @@ func (service *httpRestService) saveState() error { } // Update time stamp. - service.state.TimeStamp = time.Now() + service.state.TimeStamp = time.Now() err := service.store.Write(storeKey, &service.state) if err == nil { log.Printf("[Azure CNS] State saved successfully.\n") @@ -519,7 +519,7 @@ func (service *httpRestService) restoreState() error { log.Printf("[Azure CNS] No state to restore.\n") return nil } - + log.Printf("[Azure CNS] Failed to restore state, err:%v\n", err) return err } diff --git a/cns/restserver/restserver_test.go b/cns/restserver/restserver_test.go index b46d35d2fd..bf8b1ddc28 100644 --- a/cns/restserver/restserver_test.go +++ b/cns/restserver/restserver_test.go @@ -12,8 +12,8 @@ import ( "os" "testing" - "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/cns" + "github.com/Azure/azure-container-networking/common" ) var service HTTPService @@ -39,7 +39,7 @@ func TestMain(m *testing.M) { if err != nil { fmt.Printf("Failed to start CNS %v\n", err) os.Exit(2) - } + } // Get the internal http mux as test hook. mux = service.(*httpRestService).Listener.GetMux() @@ -66,12 +66,12 @@ func decodeResponse(w *httptest.ResponseRecorder, response interface{}) error { return json.NewDecoder(w.Body).Decode(&response) } -func setEnv(t *testing.T) (*httptest.ResponseRecorder) { - envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} +func setEnv(t *testing.T) *httptest.ResponseRecorder { + envRequest := cns.SetEnvironmentRequest{Location: "Azure", NetworkType: "Underlay"} envRequestJSON := new(bytes.Buffer) - json.NewEncoder(envRequestJSON).Encode(envRequest) + json.NewEncoder(envRequestJSON).Encode(envRequest) - req, err := http.NewRequest(http.MethodPost, cns.V1Prefix + cns.SetEnvironmentPath, envRequestJSON) + req, err := http.NewRequest(http.MethodPost, cns.V1Prefix+cns.SetEnvironmentPath, envRequestJSON) if err != nil { t.Fatal(err) } @@ -79,7 +79,7 @@ func setEnv(t *testing.T) (*httptest.ResponseRecorder) { mux.ServeHTTP(w, req) return w } -func TestSetEnvironment(t *testing.T) { +func TestSetEnvironment(t *testing.T) { fmt.Println("Test: SetEnvironment") var resp cns.Response w := setEnv(t) @@ -87,13 +87,13 @@ func TestSetEnvironment(t *testing.T) { if err != nil || resp.ReturnCode != 0 { t.Errorf("SetEnvironment failed with response %+v", resp) } else { - fmt.Printf ("SetEnvironment Responded with %+v\n", resp); + fmt.Printf("SetEnvironment Responded with %+v\n", resp) } } // Tests CreateNetwork functionality. func TestCreateNetwork(t *testing.T) { - fmt.Println("Test: CreateNetwork") + fmt.Println("Test: CreateNetwork") var body bytes.Buffer setEnv(t) info := &cns.CreateNetworkRequest{ @@ -103,7 +103,7 @@ func TestCreateNetwork(t *testing.T) { req, err := http.NewRequest(http.MethodPost, cns.CreateNetworkPath, &body) if err != nil { t.Fatal(err) - } + } w := httptest.NewRecorder() mux.ServeHTTP(w, req) var resp cns.Response @@ -111,13 +111,13 @@ func TestCreateNetwork(t *testing.T) { if err != nil || resp.ReturnCode != 0 { t.Errorf("CreateNetwork failed with response %+v", resp) } else { - fmt.Printf ("CreateNetwork Responded with %+v\n", resp); + fmt.Printf("CreateNetwork Responded with %+v\n", resp) } } // Tests DeleteNetwork functionality. func TestDeleteNetwork(t *testing.T) { - fmt.Println("Test: DeleteNetwork") + fmt.Println("Test: DeleteNetwork") var body bytes.Buffer setEnv(t) info := &cns.DeleteNetworkRequest{ @@ -127,7 +127,7 @@ func TestDeleteNetwork(t *testing.T) { req, err := http.NewRequest(http.MethodPost, cns.DeleteNetworkPath, &body) if err != nil { t.Fatal(err) - } + } w := httptest.NewRecorder() mux.ServeHTTP(w, req) var resp cns.Response @@ -135,20 +135,20 @@ func TestDeleteNetwork(t *testing.T) { if err != nil || resp.ReturnCode != 0 { t.Errorf("DeleteNetwork failed with response %+v", resp) } else { - fmt.Printf ("DeleteNetwork Responded with %+v\n", resp); + fmt.Printf("DeleteNetwork Responded with %+v\n", resp) } } -func TestReserveIPAddress(t *testing.T){ +func TestReserveIPAddress(t *testing.T) { fmt.Println("Test: ReserveIPAddress") - reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID:"ip01"} - reserveIPRequestJSON := new(bytes.Buffer) - json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) + reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID: "ip01"} + reserveIPRequestJSON := new(bytes.Buffer) + json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) - envRequest := cns.SetEnvironmentRequest{Location:"Azure", NetworkType: "Underlay"} + envRequest := cns.SetEnvironmentRequest{Location: "Azure", NetworkType: "Underlay"} envRequestJSON := new(bytes.Buffer) - json.NewEncoder(envRequestJSON).Encode(envRequest) + json.NewEncoder(envRequestJSON).Encode(envRequest) req, err := http.NewRequest(http.MethodGet, cns.ReserveIPAddressPath, envRequestJSON) if err != nil { @@ -164,15 +164,15 @@ func TestReserveIPAddress(t *testing.T){ if err != nil || reserveIPAddressResponse.Response.ReturnCode != 0 { t.Errorf("SetEnvironment failed with response %+v", reserveIPAddressResponse) } else { - fmt.Printf ("SetEnvironment Responded with %+v\n", reserveIPAddressResponse); + fmt.Printf("SetEnvironment Responded with %+v\n", reserveIPAddressResponse) } } -func TestReleaseIPAddress(t *testing.T){ +func TestReleaseIPAddress(t *testing.T) { fmt.Println("Test: ReleaseIPAddress") - releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID:"ip01"} - releaseIPAddressRequestJSON := new(bytes.Buffer) - json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) + releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID: "ip01"} + releaseIPAddressRequestJSON := new(bytes.Buffer) + json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) req, err := http.NewRequest(http.MethodGet, cns.ReleaseIPAddressPath, releaseIPAddressRequestJSON) if err != nil { @@ -182,16 +182,16 @@ func TestReleaseIPAddress(t *testing.T){ w := httptest.NewRecorder() mux.ServeHTTP(w, req) - var releaseIPAddressResponse cns.Response + var releaseIPAddressResponse cns.Response err = decodeResponse(w, &releaseIPAddressResponse) if err != nil || releaseIPAddressResponse.ReturnCode != 0 { t.Errorf("SetEnvironment failed with response %+v", releaseIPAddressResponse) } else { - fmt.Printf ("SetEnvironment Responded with %+v\n", releaseIPAddressResponse); + fmt.Printf("SetEnvironment Responded with %+v\n", releaseIPAddressResponse) } } -func TestGetIPAddressUtilization(t *testing.T){ +func TestGetIPAddressUtilization(t *testing.T) { fmt.Println("Test: GetIPAddressUtilization") req, err := http.NewRequest(http.MethodGet, cns.GetIPAddressUtilizationPath, nil) @@ -207,11 +207,11 @@ func TestGetIPAddressUtilization(t *testing.T){ if err != nil || iPAddressesUtilizationResponse.Response.ReturnCode != 0 { t.Errorf("GetIPAddressUtilization failed with response %+v", iPAddressesUtilizationResponse) } else { - fmt.Printf ("GetIPAddressUtilization Responded with %+v\n", iPAddressesUtilizationResponse); + fmt.Printf("GetIPAddressUtilization Responded with %+v\n", iPAddressesUtilizationResponse) } } -func TestGetHostLocalIP(t *testing.T){ +func TestGetHostLocalIP(t *testing.T) { fmt.Println("Test: GetHostLocalIP") setEnv(t) req, err := http.NewRequest(http.MethodGet, cns.GetHostLocalIPPath, nil) @@ -220,18 +220,18 @@ func TestGetHostLocalIP(t *testing.T){ } w := httptest.NewRecorder() mux.ServeHTTP(w, req) - + var hostLocalIPAddressResponse cns.HostLocalIPAddressResponse err = decodeResponse(w, &hostLocalIPAddressResponse) if err != nil || hostLocalIPAddressResponse.Response.ReturnCode != 0 { t.Errorf("GetHostLocalIP failed with response %+v", hostLocalIPAddressResponse) } else { - fmt.Printf ("GetHostLocalIP Responded with %+v\n", hostLocalIPAddressResponse); + fmt.Printf("GetHostLocalIP Responded with %+v\n", hostLocalIPAddressResponse) } } -func TestGetUnhealthyIPAddresses(t *testing.T){ +func TestGetUnhealthyIPAddresses(t *testing.T) { fmt.Println("Test: GetGhostIPAddresses") req, err := http.NewRequest(http.MethodGet, cns.GetUnhealthyIPAddressesPath, nil) if err != nil { @@ -240,13 +240,12 @@ func TestGetUnhealthyIPAddresses(t *testing.T){ w := httptest.NewRecorder() mux.ServeHTTP(w, req) - var getIPAddressesResponse cns.GetIPAddressesResponse + var getIPAddressesResponse cns.GetIPAddressesResponse err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { t.Errorf("GetUnhealthyIPAddresses failed with response %+v", getIPAddressesResponse) } else { - fmt.Printf ("GetUnhealthyIPAddresses Responded with %+v\n", getIPAddressesResponse); + fmt.Printf("GetUnhealthyIPAddresses Responded with %+v\n", getIPAddressesResponse) } } - diff --git a/cns/routes/routes.go b/cns/routes/routes.go index 7f6b072c27..ff758b51b1 100644 --- a/cns/routes/routes.go +++ b/cns/routes/routes.go @@ -10,10 +10,10 @@ import ( // Route describes a single route in the routing table. type Route struct { destination string - mask string - gateway string - metric string - ifaceIndex int + mask string + gateway string + metric string + ifaceIndex int } // RoutingTable describes the routing table on the node. @@ -22,9 +22,9 @@ type RoutingTable struct { } // GetRoutingTable retireves routing table in the node. -func (rt *RoutingTable) GetRoutingTable() (error) { +func (rt *RoutingTable) GetRoutingTable() error { routes, err := getRoutes() - if(err == nil) { + if err == nil { rt.Routes = routes } @@ -32,11 +32,11 @@ func (rt *RoutingTable) GetRoutingTable() (error) { } // RestoreRoutingTable pushes the saved route. -func (rt *RoutingTable) RestoreRoutingTable () (error) { +func (rt *RoutingTable) RestoreRoutingTable() error { if rt.Routes == nil { log.Printf("[Azure CNS] Nothing available in routing table to push") return nil } return putRoutes(rt.Routes) -} \ No newline at end of file +} diff --git a/cns/routes/routes_linux.go b/cns/routes/routes_linux.go index 13d4b3b6af..ea043f7e46 100644 --- a/cns/routes/routes_linux.go +++ b/cns/routes/routes_linux.go @@ -15,8 +15,7 @@ import ( "github.com/Azure/azure-container-networking/log" ) -const ( -) +const () func getRoutes() ([]Route, error) { return nil, nil diff --git a/cns/routes/routes_test.go b/cns/routes/routes_test.go index cab35cdafa..1fa3226b51 100644 --- a/cns/routes/routes_test.go +++ b/cns/routes/routes_test.go @@ -4,85 +4,86 @@ package routes import ( + "github.com/Azure/azure-container-networking/log" "os" "os/exec" "testing" - "github.com/Azure/azure-container-networking/log" ) const ( - testDest = "159.254.169.254" - testMask = "255.255.255.255" + testDest = "159.254.169.254" + testMask = "255.255.255.255" testGateway = "0.0.0.0" - testMetric = "12" + testMetric = "12" ) // Wraps the test run. func TestMain(m *testing.M) { // Run tests. exitCode := m.Run() - os.Exit(exitCode) } func addTestRoute() error { - arg := []string{"/C", "route", "ADD", testDest, + arg := []string{"/C", "route", "ADD", testDest, "MASK", testMask, testGateway, "METRIC", testMetric} log.Printf("[Azure CNS] Adding missing route: %v", arg) - + c := exec.Command("cmd", arg...) bytes, err := c.Output() - if err == nil { - log.Printf("[Azure CNS] Successfully executed add route: %v\n%v", - arg, string(bytes)) + log.Printf("[Azure CNS] Successfully executed add route: %v\n%v", + arg, string(bytes)) } else { - log.Printf("[Azure CNS] Failed to execute add route: %v\n%v\n%v", - arg, string(bytes), err.Error()) - return err + log.Printf("[Azure CNS] Failed to execute add route: %v\n%v\n%v", + arg, string(bytes), err.Error()) + return err } + return nil } func deleteTestRoute() error { - args := []string{"/C", "route", "DELETE", testDest, "MASK", testMask, - testGateway, "METRIC", testMetric} + args := []string{"/C", "route", "DELETE", testDest, "MASK", testMask, + testGateway, "METRIC", testMetric} log.Printf("[Azure CNS] Deleting route: %v", args) + c := exec.Command("cmd", args...) bytes, err := c.Output() if err == nil { - log.Printf("[Azure CNS] Successfully executed delete route: %v\n%v", - args, string(bytes)) + log.Printf("[Azure CNS] Successfully executed delete route: %v\n%v", + args, string(bytes)) } else { - log.Printf("[Azure CNS] Failed to execute delete route: %v\n%v\n%v", - args, string(bytes), err.Error()) - return err + log.Printf("[Azure CNS] Failed to execute delete route: %v\n%v\n%v", + args, string(bytes), err.Error()) + return err } + return nil } // TestPutRoutes tests if a missing route is properly restored or not. -func TestRestoreMissingRoute(t *testing.T){ +func TestRestoreMissingRoute(t *testing.T) { log.Printf("Test: PutMissingRoutes") err := addTestRoute() - if(err != nil) { + if err != nil { t.Errorf("add route failed %+v", err.Error()) } rt := &RoutingTable{} // save routing table. - rt.GetRoutingTable() + rt.GetRoutingTable() cntr := 0 - for _,rt := range rt.Routes { + for _, rt := range rt.Routes { log.Printf("[]: Route[%d]: %+v", cntr, rt) cntr++ } // now delete the route so it goes missing. err = deleteTestRoute() - if(err != nil) { + if err != nil { t.Errorf("delete route failed %+v", err.Error()) } @@ -94,20 +95,19 @@ func TestRestoreMissingRoute(t *testing.T){ restored := false for _, existingRoute := range rt.Routes { log.Printf("Comapring %+v\n", existingRoute) - if(existingRoute.destination == testDest && - existingRoute.gateway == testGateway && - existingRoute.mask == testMask) { - restored = true - } + if existingRoute.destination == testDest && + existingRoute.gateway == testGateway && + existingRoute.mask == testMask { + restored = true + } } - if(!restored){ + if !restored { t.Errorf("unable to restore missing route") } else { err = deleteTestRoute() - if(err != nil) { + if err != nil { t.Errorf("delete route failed %+v", err.Error()) } } } - diff --git a/cns/routes/routes_windows.go b/cns/routes/routes_windows.go index 997567b3dc..521577575b 100644 --- a/cns/routes/routes_windows.go +++ b/cns/routes/routes_windows.go @@ -8,8 +8,8 @@ package routes import ( "fmt" "net" - "strings" "os/exec" + "strings" "github.com/Azure/azure-container-networking/log" ) @@ -34,13 +34,13 @@ func getInterfaceByAddress(address string) (int, error) { addrs, _ := ifaces[i].Addrs() for _, addr := range addrs { log.Debugf("[Azure CNS] ipAddress being compared input=%v %v\n", - address, addr.String()) + address, addr.String()) ip := strings.Split(addr.String(), "/") - if(len(ip) != 2) { + if len(ip) != 2 { return -1, fmt.Errorf("Malformed ip: %v", addr.String()) - } - if ip[0] == address { - return ifaces[i].Index, nil + } + if ip[0] == address { + return ifaces[i].Index, nil } } } @@ -57,7 +57,6 @@ func getRoutes() ([]Route, error) { var routePrintOutput string var routeCount int bytes, err := c.Output() - if err == nil { routePrintOutput = string(bytes) log.Debugf("[Azure CNS] Printing Routing table \n %v\n", routePrintOutput) @@ -71,12 +70,11 @@ func getRoutes() ([]Route, error) { tokens := strings.Split( strings.Split(routeTable[1], "Metric")[1], "=") - table := tokens[0] routes := strings.Split(table, "\r") routeCount = len(routes) log.Debugf("[Azure CNS] Recevied route count: %d", routeCount) - if(routeCount == 0){ + if routeCount == 0 { return nil, nil } @@ -99,7 +97,7 @@ func getRoutes() ([]Route, error) { metric: tokens[4], } - if(rt.gateway == "On-link"){ + if rt.gateway == "On-link" { rt.gateway = "0.0.0.0" } @@ -116,7 +114,7 @@ func getRoutes() ([]Route, error) { } } - if(truncated == routeCount) { + if truncated == routeCount { localRoutes = nil } else { localRoutes = localRoutes[0 : routeCount-truncated-1] @@ -125,18 +123,18 @@ func getRoutes() ([]Route, error) { return localRoutes, nil } -func containsRoute(routes []Route, route Route) (bool, error){ +func containsRoute(routes []Route, route Route) (bool, error) { log.Printf("[Azure CNS] containsRoute") - if(routes == nil){ + if routes == nil { return false, nil } for _, existingRoute := range routes { - if(existingRoute.destination == route.destination && - existingRoute.gateway == route.gateway && - existingRoute.ifaceIndex == route.ifaceIndex && - existingRoute.mask == route.mask) { - return true, nil - } + if existingRoute.destination == route.destination && + existingRoute.gateway == route.gateway && + existingRoute.ifaceIndex == route.ifaceIndex && + existingRoute.mask == route.mask { + return true, nil + } } return false, nil } @@ -147,35 +145,35 @@ func putRoutes(routes []Route) error { var err error log.Printf("[Azure CNS] Going to get current routes") currentRoutes, err := getRoutes() - if( err != nil){ + if err != nil { return err } for _, route := range routes { exists, err := containsRoute(currentRoutes, route) - if(err == nil && !exists) { + if err == nil && !exists { args := []string{"/C", "route", "ADD", - route.destination, - "MASK", - route.mask, - route.gateway, - "METRIC", - route.metric, - "IF", - fmt.Sprintf("%d", route.ifaceIndex)} + route.destination, + "MASK", + route.mask, + route.gateway, + "METRIC", + route.metric, + "IF", + fmt.Sprintf("%d", route.ifaceIndex)} log.Printf("[Azure CNS] Adding missing route: %v", args) - c := exec.Command("cmd", args...) + c := exec.Command("cmd", args...) bytes, err := c.Output() if err == nil { log.Printf("[Azure CNS] Successfully executed add route: %v\n%v", args, string(bytes)) } else { - log.Printf("[Azure CNS] Failed to execute add route: %v\n%v", args, string(bytes)) + log.Printf("[Azure CNS] Failed to execute add route: %v\n%v", args, string(bytes)) } } else { log.Printf("[Azure CNS] Route already exists. skipping %+v", route) } } - + return err } diff --git a/cns/service.go b/cns/service.go index 91639b09f0..9de475c762 100644 --- a/cns/service.go +++ b/cns/service.go @@ -15,7 +15,7 @@ import ( const ( // Default CNS server URL. defaultAPIServerURL = "tcp://localhost:10090" - genericData = "com.microsoft.azure.network.generic" + genericData = "com.microsoft.azure.network.generic" ) // Service defines Container Networking Service. @@ -26,7 +26,7 @@ type Service struct { } // NewService creates a new Service object. -func NewService(name, version string, store store.KeyValueStore) (*Service, error) { +func NewService(name, version string, store store.KeyValueStore) (*Service, error) { service, err := common.NewService(name, version, store) if err != nil { @@ -34,7 +34,7 @@ func NewService(name, version string, store store.KeyValueStore) (*Service, erro } return &Service{ - Service: service, + Service: service, }, nil } @@ -51,7 +51,7 @@ func (service *Service) getAPIServerURL() string { // Initialize initializes the service and starts the listener. func (service *Service) Initialize(config *common.ServiceConfig) error { log.Debugf("[Azure CNS] Going to initialize a service with config: %+v", config) - + // Initialize the base service. service.Service.Initialize(config) @@ -80,24 +80,24 @@ func (service *Service) Initialize(config *common.ServiceConfig) error { service.Listener = config.Listener - log.Debugf("[Azure CNS] Successfully initialized a service with config: %+v", config) + log.Debugf("[Azure CNS] Successfully initialized a service with config: %+v", config) return nil } // Uninitialize cleans up the plugin. -func (service *Service) Uninitialize() { +func (service *Service) Uninitialize() { service.Listener.Stop() service.Service.Uninitialize() } // ParseOptions returns generic options from a libnetwork request. -func (service *Service) ParseOptions(options OptionMap) OptionMap { +func (service *Service) ParseOptions(options OptionMap) OptionMap { opt, _ := options[genericData].(OptionMap) return opt } // SendErrorResponse sends and logs an error response. -func (service *Service) SendErrorResponse(w http.ResponseWriter, errMsg error) { +func (service *Service) SendErrorResponse(w http.ResponseWriter, errMsg error) { resp := errorResponse{errMsg.Error()} err := service.Listener.Encode(w, &resp) log.Response(service.Name, &resp, err) diff --git a/cns/service/main.go b/cns/service/main.go index 6867280a96..cd322e0903 100644 --- a/cns/service/main.go +++ b/cns/service/main.go @@ -8,7 +8,7 @@ import ( "os" "os/signal" "syscall" - + "github.com/Azure/azure-container-networking/cns/restserver" "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" @@ -75,7 +75,7 @@ func printVersion() { func main() { // Initialize and parse command line arguments. common.ParseArgs(&args, printVersion) - + url := common.GetArg(common.OptAPIServerURL).(string) logLevel := common.GetArg(common.OptLogLevel).(int) logTarget := common.GetArg(common.OptLogTarget).(int) @@ -92,7 +92,7 @@ func main() { config.Name = name // Create a channel to receive unhandled errors from CNS. - config.ErrChan = make(chan error, 1) + config.ErrChan = make(chan error, 1) // Create the key value store. var err error From 8e554c07b465516635fcb2618e28f6ef47c9606c Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Wed, 14 Jun 2017 16:29:22 -0700 Subject: [PATCH 22/34] Fixed merge errors and added comments --- cns/ipamclient/api.go | 27 +++++++++++++-------------- cns/ipamclient/ipamclient.go | 7 ++++--- cns/ipamclient/ipamclient_linux.go | 4 ++-- cns/ipamclient/ipamclient_test.go | 10 +++++----- cns/restserver/api.go | 1 - cns/restserver/restserver.go | 17 +++-------------- 6 files changed, 27 insertions(+), 39 deletions(-) diff --git a/cns/ipamclient/api.go b/cns/ipamclient/api.go index b5109ea5ac..800ed71fd6 100644 --- a/cns/ipamclient/api.go +++ b/cns/ipamclient/api.go @@ -1,5 +1,6 @@ package ipamclient +// IPAM Plugin API Contract. const ( getAddressSpacesPath = "/IpamDriver.GetDefaultAddressSpaces" requestPoolPath = "/IpamDriver.RequestPool" @@ -8,17 +9,13 @@ const ( getPoolInfoPath = "/IpamDriver.GetPoolInfo" ) -// Config describes subnet/gateway for ipam. -type Config struct { - Subnet string -} - +// Response received from IPAM Plugin when request AddressSpace. type getAddressSpacesResponse struct { LocalDefaultAddressSpace string GlobalDefaultAddressSpace string } -// Request sent by libnetwork when acquiring a reference to an address pool. +// Request sent to IPAM plugin to request a pool. type requestPoolRequest struct { AddressSpace string Pool string @@ -27,41 +24,43 @@ type requestPoolRequest struct { V6 bool } -// Response sent by plugin when an address pool is successfully referenced. +// Response received from IPAM Plugin when requesting a pool. type requestPoolResponse struct { PoolID string Pool string Data map[string]string } -// NetworkConfiguration describes configuration for docker network create. +// Request sent to IPAM plugin to request IP reservation. type reserveAddrRequest struct { PoolID string Address string Options map[string]string } -// DockerErrorResponse defines the error response retunred by docker. +// Response received from IPAM Plugin when requesting a IP reservation. type reserveAddrResponse struct { Address string } -// NetworkConfiguration describes configuration for docker network create. +// Request sent to IPAM plugin to release IP reservation. type releaseAddrRequest struct { PoolID string Address string Options map[string]string } +// Response received from IPAM Plugin when requesting IP release. type releaseAddrResponse struct { Err string } -type errorResponse struct { - Err string -} +// TODO +// type errorResponse struct { +// Err string +// } -// Request sent when querying address pool information. +// Request sent to IPAM plugin to query address pool information. type getPoolInfoRequest struct { PoolID string } diff --git a/cns/ipamclient/ipamclient.go b/cns/ipamclient/ipamclient.go index e9f1426240..e3db6a9632 100644 --- a/cns/ipamclient/ipamclient.go +++ b/cns/ipamclient/ipamclient.go @@ -134,6 +134,7 @@ func (ic *IpamClient) ReserveIPAddress(poolID string, reservationID string) (str if res.StatusCode == 200 { var reserveResp reserveAddrResponse + // TODO // var errorResp errorResponse // err := json.NewDecoder(res.Body).Decode(&errorResp) @@ -206,7 +207,7 @@ func (ic *IpamClient) ReleaseIPAddress(poolID string, reservationID string) erro } -// GetIPAddressUtilization - returns number of available, reserved and unhealthy addresses list +// GetIPAddressUtilization - returns number of available, reserved and unhealthy addresses list. func (ic *IpamClient) GetIPAddressUtilization(poolID string) (int, int, []string, error) { var body bytes.Buffer log.Printf("[Azure CNS] GetIPAddressUtilization") @@ -237,12 +238,12 @@ func (ic *IpamClient) GetIPAddressUtilization(poolID string) (int, int, []string var poolInfoResp getPoolInfoResponse err := json.NewDecoder(res.Body).Decode(&poolInfoResp) if err != nil { - log.Printf("[Azure CNS] Error received while parsing release response :%v err:%v", res.Body, err.Error()) + log.Printf("[Azure CNS] Error received while parsing GetIPUtilization response :%v err:%v", res.Body, err.Error()) return 0, 0, nil, err } return poolInfoResp.Capacity, poolInfoResp.Available, poolInfoResp.UnhealthyAddresses, nil } - log.Printf("[Azure CNS] ReleaseIP invalid http status code: %v err:%v", res.StatusCode, err.Error()) + log.Printf("[Azure CNS] GetIPUtilization invalid http status code: %v err:%v", res.StatusCode, err.Error()) return 0, 0, nil, err } diff --git a/cns/ipamclient/ipamclient_linux.go b/cns/ipamclient/ipamclient_linux.go index 37a3b0a189..345be8e872 100644 --- a/cns/ipamclient/ipamclient_linux.go +++ b/cns/ipamclient/ipamclient_linux.go @@ -3,8 +3,8 @@ // +build linux -package dockerclient +package ipamclient const ( - ipamPluginURL = "" + ipamPluginURL = "unix:///run/docker/plugins/" ) diff --git a/cns/ipamclient/ipamclient_test.go b/cns/ipamclient/ipamclient_test.go index fa12b86fac..eb47b91351 100644 --- a/cns/ipamclient/ipamclient_test.go +++ b/cns/ipamclient/ipamclient_test.go @@ -95,7 +95,7 @@ func decodeResponse(w *httptest.ResponseRecorder, response interface{}) error { return json.NewDecoder(w.Body).Decode(&response) } -// Tests IpamClient GetAddressSpace function to get AddressSpaceID +// Tests IpamClient GetAddressSpace function to get AddressSpaceID. func TestAddressSpaces(t *testing.T) { asID, err := ic.GetAddressSpace() if err != nil { @@ -108,7 +108,7 @@ func TestAddressSpaces(t *testing.T) { } } -// Tests IpamClient GetPoolID function to get PoolID +// Tests IpamClient GetPoolID function to get PoolID. func TestGetPoolID(t *testing.T) { subnet := "10.0.0.0/16" @@ -129,7 +129,7 @@ func TestGetPoolID(t *testing.T) { } } -// Tests IpamClient ReserveIPAddress function to request IP for ID +// Tests IpamClient ReserveIPAddress function to request IP for ID. func TestReserveIP(t *testing.T) { subnet := "10.0.0.0/16" @@ -166,7 +166,7 @@ func TestReserveIP(t *testing.T) { } -// Tests IpamClient ReleaseIPAddress function to release IP associated with ID +// Tests IpamClient ReleaseIPAddress function to release IP associated with ID. func TestReleaseIP(t *testing.T) { subnet := "10.0.0.0/16" @@ -199,7 +199,7 @@ func TestReleaseIP(t *testing.T) { } } -// Tests IpamClient GetIPAddressUtilization function to retrieve IP Utilization info +// Tests IpamClient GetIPAddressUtilization function to retrieve IP Utilization info. func TestIPAddressUtilization(t *testing.T) { subnet := "10.0.0.0/16" diff --git a/cns/restserver/api.go b/cns/restserver/api.go index eb7e7e2b51..11d1b11b5a 100644 --- a/cns/restserver/api.go +++ b/cns/restserver/api.go @@ -11,7 +11,6 @@ const ( UnsupportedEnvironment = 3 UnreachableHost = 4 ReservationNotFound = 5 - InvalidReservationID = 6 MalformedSubnet = 8 UnreachableDockerDaemon = 9 UnspecifiedNetworkName = 10 diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 169851fbe9..9b2d75f080 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -13,7 +13,6 @@ import ( "github.com/Azure/azure-container-networking/cns/imdsclient" "github.com/Azure/azure-container-networking/cns/ipamclient" "github.com/Azure/azure-container-networking/cns/routes" - "github.com/Azure/azure-container-networking/cns/routes" "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" @@ -89,16 +88,6 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { // Add handlers. listener := service.Listener - // default handlers - listener.AddHandler(cns.SetEnvironmentPath, service.setEnvironment) - listener.AddHandler(cns.CreateNetworkPath, service.createNetwork) - listener.AddHandler(cns.DeleteNetworkPath, service.deleteNetwork) - listener.AddHandler(cns.ReserveIPAddressPath, service.reserveIPAddress) - listener.AddHandler(cns.ReleaseIPAddressPath, service.releaseIPAddress) - listener.AddHandler(cns.GetHostLocalIPPath, service.getHostLocalIP) - listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) - listener.AddHandler(cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) - // handlers for v0.1 listener.AddHandler(cns.V1Prefix+cns.SetEnvironmentPath, service.setEnvironment) listener.AddHandler(cns.V1Prefix+cns.CreateNetworkPath, service.createNetwork) @@ -300,7 +289,7 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. } if req.ReservationID == "" { - returnCode = InvalidReservationID + returnCode = ReservationNotFound returnMessage = fmt.Sprintf("[Azure CNS] Error. ReservationId is empty") } @@ -367,7 +356,7 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. } if req.ReservationID == "" { - returnCode = InvalidReservationID + returnCode = ReservationNotFound returnMessage = fmt.Sprintf("[Azure CNS] Error. ReservationId is empty") } @@ -398,7 +387,7 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. err = ic.ReleaseIPAddress(poolID, req.ReservationID) if err != nil { - returnMessage = fmt.Sprintf("ReserveIpAddress failed with %+v", err.Error()) + returnMessage = fmt.Sprintf("ReleaseIpAddress failed with %+v", err.Error()) returnCode = UnexpectedError } From c43b2bc3df85ba95495ce5198f3070042cccf768 Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Wed, 14 Jun 2017 16:54:44 -0700 Subject: [PATCH 23/34] Added default handlers back --- cns/restserver/restserver.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 9b2d75f080..5e1e86991f 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -87,7 +87,16 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { // Add handlers. listener := service.Listener - +// default handlers + listener.AddHandler(cns.SetEnvironmentPath, service.setEnvironment) + listener.AddHandler(cns.CreateNetworkPath, service.createNetwork) + listener.AddHandler(cns.DeleteNetworkPath, service.deleteNetwork) + listener.AddHandler(cns.ReserveIPAddressPath, service.reserveIPAddress) + listener.AddHandler(cns.ReleaseIPAddressPath, service.releaseIPAddress) + listener.AddHandler(cns.GetHostLocalIPPath, service.getHostLocalIP) + listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) + listener.AddHandler(cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) + // handlers for v0.1 listener.AddHandler(cns.V1Prefix+cns.SetEnvironmentPath, service.setEnvironment) listener.AddHandler(cns.V1Prefix+cns.CreateNetworkPath, service.createNetwork) From cf5143e219bb42cd702269d45cab07928cb1909c Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Wed, 14 Jun 2017 17:33:03 -0700 Subject: [PATCH 24/34] Added validation checks and modified format of unhealthy addrs --- cns/api.go | 8 +------- cns/ipamclient/ipamclient.go | 29 +++++++++++++---------------- cns/ipamclient/ipamclient_test.go | 1 - cns/restserver/restserver.go | 27 +++++++++++---------------- 4 files changed, 25 insertions(+), 40 deletions(-) diff --git a/cns/api.go b/cns/api.go index b5bf42fd39..150d13b997 100644 --- a/cns/api.go +++ b/cns/api.go @@ -69,7 +69,7 @@ type IPAddressesUtilizationResponse struct { // GetIPAddressesResponse describes response containing requested ip addresses. type GetIPAddressesResponse struct { Response Response - IPAddresses []IPAddress + IPAddresses []string } // HostLocalIPAddressResponse describes reponse that returns the host local IP Address. @@ -78,12 +78,6 @@ type HostLocalIPAddressResponse struct { IPAddress string } -// IPAddress Contains information about an ip address. -type IPAddress struct { - IPAddress string - ReservationID string -} - // Subnet contains the ip address and the number of bits in prefix. type Subnet struct { IPAddress string diff --git a/cns/ipamclient/ipamclient.go b/cns/ipamclient/ipamclient.go index e3db6a9632..d7d9154797 100644 --- a/cns/ipamclient/ipamclient.go +++ b/cns/ipamclient/ipamclient.go @@ -8,6 +8,8 @@ import ( "encoding/json" "net/http" + "fmt" + "github.com/Azure/azure-container-networking/ipam" "github.com/Azure/azure-container-networking/log" ) @@ -134,27 +136,18 @@ func (ic *IpamClient) ReserveIPAddress(poolID string, reservationID string) (str if res.StatusCode == 200 { var reserveResp reserveAddrResponse - // TODO - // var errorResp errorResponse - - // err := json.NewDecoder(res.Body).Decode(&errorResp) - // if err != nil { - // log.Printf("[Azure CNS] Error received while parsing reserve response resp:%v err:%v", res.Body, err.Error()) - // return "", err - // } - - // if errorResp.Err != "" { - // log.Printf("[Azure CNS] Received Error Response from IPAM :%v", errorResp.Err) - // return "", fmt.Errorf(errorResp.Err) - // } + err = json.NewDecoder(res.Body).Decode(&reserveResp) if err != nil { log.Printf("[Azure CNS] Error received while parsing reserve response resp:%v err:%v", res.Body, err.Error()) return "", err } - - return reserveResp.Address, nil + if reserveResp.Address != "" { + return reserveResp.Address, nil + } + return "", fmt.Errorf("Address not available") } + log.Printf("[Azure CNS] ReserveIp invalid http status code: %v err:%v", res.StatusCode, err.Error()) return "", err } @@ -235,12 +228,16 @@ func (ic *IpamClient) GetIPAddressUtilization(poolID string) (int, int, []string } if res.StatusCode == 200 { - var poolInfoResp getPoolInfoResponse + poolInfoResp := &getPoolInfoResponse{-1, -1, nil} err := json.NewDecoder(res.Body).Decode(&poolInfoResp) if err != nil { log.Printf("[Azure CNS] Error received while parsing GetIPUtilization response :%v err:%v", res.Body, err.Error()) return 0, 0, nil, err } + if poolInfoResp.Available == -1 { + log.Printf("[Azure CNS] Invalid IPUtilization Response") + return 0, 0, nil, fmt.Errorf("Invalid IPUtilization Response") + } return poolInfoResp.Capacity, poolInfoResp.Available, poolInfoResp.UnhealthyAddresses, nil } log.Printf("[Azure CNS] GetIPUtilization invalid http status code: %v err:%v", res.StatusCode, err.Error()) diff --git a/cns/ipamclient/ipamclient_test.go b/cns/ipamclient/ipamclient_test.go index eb47b91351..c890ae79be 100644 --- a/cns/ipamclient/ipamclient_test.go +++ b/cns/ipamclient/ipamclient_test.go @@ -67,7 +67,6 @@ func handlePoolIDQuery(w http.ResponseWriter, r *http.Request) { // Handles queries from ReserveIPAddress. func handleReserveIPQuery(w http.ResponseWriter, r *http.Request) { var reserveIPResp = "{\"Address\":\"10.0.0.2/16\"}" - //var reserveIPResp = "{\"Err\":\"addr not found\"}" w.Write([]byte(reserveIPResp)) } diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 5e1e86991f..7b59fd4615 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -87,16 +87,16 @@ func (service *httpRestService) Start(config *common.ServiceConfig) error { // Add handlers. listener := service.Listener -// default handlers - listener.AddHandler(cns.SetEnvironmentPath, service.setEnvironment) - listener.AddHandler(cns.CreateNetworkPath, service.createNetwork) + // default handlers + listener.AddHandler(cns.SetEnvironmentPath, service.setEnvironment) + listener.AddHandler(cns.CreateNetworkPath, service.createNetwork) listener.AddHandler(cns.DeleteNetworkPath, service.deleteNetwork) - listener.AddHandler(cns.ReserveIPAddressPath, service.reserveIPAddress) - listener.AddHandler(cns.ReleaseIPAddressPath, service.releaseIPAddress) - listener.AddHandler(cns.GetHostLocalIPPath, service.getHostLocalIP) - listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) - listener.AddHandler(cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) - + listener.AddHandler(cns.ReserveIPAddressPath, service.reserveIPAddress) + listener.AddHandler(cns.ReleaseIPAddressPath, service.releaseIPAddress) + listener.AddHandler(cns.GetHostLocalIPPath, service.getHostLocalIP) + listener.AddHandler(cns.GetIPAddressUtilizationPath, service.getIPAddressUtilization) + listener.AddHandler(cns.GetUnhealthyIPAddressesPath, service.getUnhealthyIPAddresses) + // handlers for v0.1 listener.AddHandler(cns.V1Prefix+cns.SetEnvironmentPath, service.setEnvironment) listener.AddHandler(cns.V1Prefix+cns.CreateNetworkPath, service.createNetwork) @@ -616,13 +616,8 @@ func (service *httpRestService) getUnhealthyIPAddresses(w http.ResponseWriter, r } ipResp := &cns.GetIPAddressesResponse{ - Response: resp, - } - - ipResp.IPAddresses = make([]cns.IPAddress, len(unhealthyAddrs)) - - for index, addr := range unhealthyAddrs { - ipResp.IPAddresses[index].IPAddress = addr + Response: resp, + IPAddresses: unhealthyAddrs, } err := service.Listener.Encode(w, &ipResp) From 70d770df5cb0227ac60a3b3af0cdc25a823a8eb6 Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Wed, 14 Jun 2017 17:46:36 -0700 Subject: [PATCH 25/34] Handled error conditions --- cns/restserver/restserver.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 7b59fd4615..57a6aeb460 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -401,6 +401,8 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. } default: + returnMessage = "[Azure CNS] Error. ReleaseIP did not receive a POST." + returnCode = InvalidParameter } resp := cns.Response{ @@ -510,6 +512,8 @@ func (service *httpRestService) getIPAddressUtilization(w http.ResponseWriter, r log.Printf("Capacity %v Available %v UnhealthyAddrs %v", capacity, available, unhealthyAddrs) default: + returnMessage = "[Azure CNS] Error. GetIPUtilization did not receive a GET." + returnCode = InvalidParameter } resp := cns.Response{ @@ -608,6 +612,8 @@ func (service *httpRestService) getUnhealthyIPAddresses(w http.ResponseWriter, r log.Printf("Capacity %v Available %v UnhealthyAddrs %v", capacity, available, unhealthyAddrs) default: + returnMessage = "[Azure CNS] Error. GetUnhealthyIP did not receive a POST." + returnCode = InvalidParameter } resp := cns.Response{ From fe9c2dda5d13f9a37b8d940541f26fe819145ca9 Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Thu, 15 Jun 2017 13:32:26 -0700 Subject: [PATCH 26/34] Removed CIDR notation --- cns/restserver/restserver.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 57a6aeb460..d03d02cc1f 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -8,6 +8,8 @@ import ( "net/http" "time" + "net" + "github.com/Azure/azure-container-networking/cns" "github.com/Azure/azure-container-networking/cns/dockerclient" "github.com/Azure/azure-container-networking/cns/imdsclient" @@ -289,6 +291,7 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. returnMessage := "" returnCode := 0 addr := "" + address := "" err := service.Listener.Decode(w, r, &req) log.Request(service.Name, &req, err) @@ -333,6 +336,9 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. returnCode = UnexpectedError } + addressIP, _, err := net.ParseCIDR(addr) + address = addressIP.String() + default: returnMessage = "[Azure CNS] Error. ReserveIP did not receive a POST." returnCode = InvalidParameter @@ -343,7 +349,7 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. ReturnCode: returnCode, Message: returnMessage, } - reserveResp := &cns.ReserveIPAddressResponse{Response: resp, IPAddress: addr} + reserveResp := &cns.ReserveIPAddressResponse{Response: resp, IPAddress: address} err = service.Listener.Encode(w, &reserveResp) log.Response(service.Name, reserveResp, err) From 8261115a0b4529ac9e300d650cbcb83e5fc639e4 Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Thu, 15 Jun 2017 13:36:58 -0700 Subject: [PATCH 27/34] Handled error condition --- cns/restserver/restserver.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index d03d02cc1f..cf96988314 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -334,9 +334,15 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. if err != nil { returnMessage = fmt.Sprintf("ReserveIpAddress failed with %+v", err.Error()) returnCode = UnexpectedError + break } addressIP, _, err := net.ParseCIDR(addr) + if err != nil { + returnMessage = fmt.Sprintf("ParseCIDR failed with %+v", err.Error()) + returnCode = UnexpectedError + break + } address = addressIP.String() default: From 07bf9d4aa1f93409ee98bd0d75970ac0000e4d48 Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Thu, 15 Jun 2017 14:15:31 -0700 Subject: [PATCH 28/34] Modified error checks as per IPAM changes --- cns/ipamclient/api.go | 10 ++++----- cns/ipamclient/ipamclient.go | 40 +++++++++++++++++++++++++++--------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/cns/ipamclient/api.go b/cns/ipamclient/api.go index 800ed71fd6..f3838abd4e 100644 --- a/cns/ipamclient/api.go +++ b/cns/ipamclient/api.go @@ -11,6 +11,7 @@ const ( // Response received from IPAM Plugin when request AddressSpace. type getAddressSpacesResponse struct { + Err string LocalDefaultAddressSpace string GlobalDefaultAddressSpace string } @@ -26,6 +27,7 @@ type requestPoolRequest struct { // Response received from IPAM Plugin when requesting a pool. type requestPoolResponse struct { + Err string PoolID string Pool string Data map[string]string @@ -40,7 +42,9 @@ type reserveAddrRequest struct { // Response received from IPAM Plugin when requesting a IP reservation. type reserveAddrResponse struct { + Err string Address string + Data map[string]string } // Request sent to IPAM plugin to release IP reservation. @@ -55,11 +59,6 @@ type releaseAddrResponse struct { Err string } -// TODO -// type errorResponse struct { -// Err string -// } - // Request sent to IPAM plugin to query address pool information. type getPoolInfoRequest struct { PoolID string @@ -67,6 +66,7 @@ type getPoolInfoRequest struct { // Response sent by plugin when returning address pool information. type getPoolInfoResponse struct { + Err string Capacity int Available int UnhealthyAddresses []string diff --git a/cns/ipamclient/ipamclient.go b/cns/ipamclient/ipamclient.go index d7d9154797..513192a30a 100644 --- a/cns/ipamclient/ipamclient.go +++ b/cns/ipamclient/ipamclient.go @@ -57,6 +57,12 @@ func (ic *IpamClient) GetAddressSpace() (string, error) { log.Printf("[Azure CNS] Error received while parsing GetAddressSpace response resp:%v err:%v", res.Body, err.Error()) return "", err } + + if resp.Err != "" { + log.Printf("[Azure CNS] GetAddressSpace received error response :%v", resp.Err) + return "", fmt.Errorf(resp.Err) + } + return resp.LocalDefaultAddressSpace, nil } log.Printf("[Azure CNS] GetAddressSpace invalid http status code: %v err:%v", res.StatusCode, err.Error()) @@ -98,6 +104,12 @@ func (ic *IpamClient) GetPoolID(asID, subnet string) (string, error) { log.Printf("[Azure CNS] Error received while parsing GetPoolID response resp:%v err:%v", res.Body, err.Error()) return "", err } + + if resp.Err != "" { + log.Printf("[Azure CNS] GetPoolID received error response :%v", resp.Err) + return "", fmt.Errorf(resp.Err) + } + return resp.PoolID, nil } log.Printf("[Azure CNS] GetPoolID invalid http status code: %v err:%v", res.StatusCode, err.Error()) @@ -142,10 +154,13 @@ func (ic *IpamClient) ReserveIPAddress(poolID string, reservationID string) (str log.Printf("[Azure CNS] Error received while parsing reserve response resp:%v err:%v", res.Body, err.Error()) return "", err } - if reserveResp.Address != "" { - return reserveResp.Address, nil + + if reserveResp.Err != "" { + log.Printf("[Azure CNS] ReserveIP received error response :%v", reserveResp.Err) + return "", fmt.Errorf(reserveResp.Err) } - return "", fmt.Errorf("Address not available") + + return reserveResp.Address, nil } log.Printf("[Azure CNS] ReserveIp invalid http status code: %v err:%v", res.StatusCode, err.Error()) @@ -189,10 +204,13 @@ func (ic *IpamClient) ReleaseIPAddress(poolID string, reservationID string) erro if err != nil { log.Printf("[Azure CNS] Error received while parsing release response :%v err:%v", res.Body, err.Error()) return err - } else if releaseResp.Err != "" { - log.Printf("[Azure CNS] Error received while parsing release response :%v err:%v", res.Body, err.Error()) - return err } + + if releaseResp.Err != "" { + log.Printf("[Azure CNS] ReleaseIP received error response :%v", releaseResp.Err) + return fmt.Errorf(releaseResp.Err) + } + return nil } log.Printf("[Azure CNS] ReleaseIP invalid http status code: %v err:%v", res.StatusCode, err.Error()) @@ -228,16 +246,18 @@ func (ic *IpamClient) GetIPAddressUtilization(poolID string) (int, int, []string } if res.StatusCode == 200 { - poolInfoResp := &getPoolInfoResponse{-1, -1, nil} + var poolInfoResp getPoolInfoResponse err := json.NewDecoder(res.Body).Decode(&poolInfoResp) if err != nil { log.Printf("[Azure CNS] Error received while parsing GetIPUtilization response :%v err:%v", res.Body, err.Error()) return 0, 0, nil, err } - if poolInfoResp.Available == -1 { - log.Printf("[Azure CNS] Invalid IPUtilization Response") - return 0, 0, nil, fmt.Errorf("Invalid IPUtilization Response") + + if poolInfoResp.Err != "" { + log.Printf("[Azure CNS] GetIPUtilization received error response :%v", poolInfoResp.Err) + return 0, 0, nil, fmt.Errorf(poolInfoResp.Err) } + return poolInfoResp.Capacity, poolInfoResp.Available, poolInfoResp.UnhealthyAddresses, nil } log.Printf("[Azure CNS] GetIPUtilization invalid http status code: %v err:%v", res.StatusCode, err.Error()) From 47a8924f7b1d2da23f95a6944e7784d41f17824c Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Thu, 15 Jun 2017 14:34:49 -0700 Subject: [PATCH 29/34] Added proper returncodes for reserve and release --- cns/restserver/api.go | 1 + cns/restserver/restserver.go | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cns/restserver/api.go b/cns/restserver/api.go index 11d1b11b5a..5065050d0b 100644 --- a/cns/restserver/api.go +++ b/cns/restserver/api.go @@ -15,5 +15,6 @@ const ( UnreachableDockerDaemon = 9 UnspecifiedNetworkName = 10 NotFound = 14 + AddressUnavailable = 15 UnexpectedError = 99 ) diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index cf96988314..1a1883b7a1 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -333,7 +333,7 @@ func (service *httpRestService) reserveIPAddress(w http.ResponseWriter, r *http. addr, err = ic.ReserveIPAddress(poolID, req.ReservationID) if err != nil { returnMessage = fmt.Sprintf("ReserveIpAddress failed with %+v", err.Error()) - returnCode = UnexpectedError + returnCode = AddressUnavailable break } @@ -409,7 +409,7 @@ func (service *httpRestService) releaseIPAddress(w http.ResponseWriter, r *http. err = ic.ReleaseIPAddress(poolID, req.ReservationID) if err != nil { returnMessage = fmt.Sprintf("ReleaseIpAddress failed with %+v", err.Error()) - returnCode = UnexpectedError + returnCode = ReservationNotFound } default: From 83df0cb88c9bbb4379c4dc60c227200172494764 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Tue, 20 Jun 2017 10:05:55 -0700 Subject: [PATCH 30/34] Fix the case of api calls --- cns/api.go | 8 +++--- cns/examples/examples.go | 61 +++++++++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 27 deletions(-) diff --git a/cns/api.go b/cns/api.go index 150d13b997..57c0a2f0dc 100644 --- a/cns/api.go +++ b/cns/api.go @@ -8,10 +8,10 @@ const ( SetEnvironmentPath = "/network/environment" CreateNetworkPath = "/network/create" DeleteNetworkPath = "/network/delete" - ReserveIPAddressPath = "/network/ip/Reserve" - ReleaseIPAddressPath = "/network/ip/Release" - GetHostLocalIPPath = "/network/ip/HostLocal" - GetIPAddressUtilizationPath = "/network/ip/Utilization" + ReserveIPAddressPath = "/network/ip/reserve" + ReleaseIPAddressPath = "/network/ip/release" + GetHostLocalIPPath = "/network/ip/hostlocal" + GetIPAddressUtilizationPath = "/network/ip/utilization" GetUnhealthyIPAddressesPath = "/network/ipaddresses/unhealthy" GetHealthReportPath = "/network/health" V1Prefix = "/v0.1" diff --git a/cns/examples/examples.go b/cns/examples/examples.go index d086f104e9..2f0a576b00 100644 --- a/cns/examples/examples.go +++ b/cns/examples/examples.go @@ -28,13 +28,13 @@ func setEnvironment() error { "application/json; charset=utf-8", envRequestJSON) if err != nil { - fmt.Printf("Error received in Set Env: %v ", err.Error()) + fmt.Printf("Error received in Set Env: %v\n", err.Error()) return err } var setEnvironmentResponse cns.Response err = json.NewDecoder(res.Body).Decode(&setEnvironmentResponse) if err != nil { - fmt.Printf("Error received in decoding response from SetEnvironment: %v ", err.Error()) + fmt.Printf("Error received in decoding response from SetEnvironment: %v\n", err.Error()) return err } fmt.Printf("Response for SetEnvironment: %+v\n", setEnvironmentResponse) @@ -51,14 +51,14 @@ func createNetwork() error { "application/json; charset=utf-8", netRequestJSON) if err != nil { - fmt.Printf("Error received in CreateNetwork post: %v ", err.Error()) + fmt.Printf("Error received in CreateNetwork post: %v\n", err.Error()) return err } var createNetworkResponse cns.Response err = json.NewDecoder(res.Body).Decode(&createNetworkResponse) if err != nil { - fmt.Printf("Error received in decoding response from CreateNEtwork: %v ", err.Error()) + fmt.Printf("Error received in decoding response from CreateNEtwork: %v\n", err.Error()) return err } fmt.Printf("Response for CreateNetwork: %+v\n", createNetworkResponse) @@ -75,22 +75,22 @@ func deleteNetwork() error { "application/json; charset=utf-8", netRequestJSON) if err != nil { - fmt.Printf("Error received in DeleteNetwork post: %v ", err.Error()) + fmt.Printf("Error received in DeleteNetwork post: %v\n", err.Error()) return err } var deleteNetworkResponse cns.Response err = json.NewDecoder(res.Body).Decode(&deleteNetworkResponse) if err != nil { - fmt.Printf("Error received in decoding response from DeleteNetwork: %v ", err.Error()) + fmt.Printf("Error received in decoding response from DeleteNetwork: %v\n", err.Error()) return err } fmt.Printf("Response for DeleteNetwork: %+v\n", deleteNetworkResponse) return nil } -func reserveIPAddress() error { - reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID: "ip01"} +func reserveIPAddress(ipID string) error { + reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID: ipID} reserveIPRequestJSON := new(bytes.Buffer) json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) res, err := @@ -99,21 +99,21 @@ func reserveIPAddress() error { "application/json; charset=utf-8", reserveIPRequestJSON) if err != nil { - fmt.Printf("Error received in reserveIPAddress post: %v ", err.Error()) + fmt.Printf("Error received in reserveIPAddress post: %v\n", err.Error()) return err } var reserveIPAddressResponse cns.ReserveIPAddressResponse err = json.NewDecoder(res.Body).Decode(&reserveIPAddressResponse) if err != nil { - fmt.Printf("Error received in decoding response from reserveIPAddress: %v ", err.Error()) + fmt.Printf("Error received in decoding response from reserveIPAddress: %v\n", err.Error()) return err } fmt.Printf("Response for reserveIPAddress: %+v\n", reserveIPAddressResponse) return nil } -func releaseIPAddress() error { - releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID: "ip01"} +func releaseIPAddress(ipID string) error { + releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID: ipID} releaseIPAddressRequestJSON := new(bytes.Buffer) json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) res, err := @@ -122,13 +122,13 @@ func releaseIPAddress() error { "application/json; charset=utf-8", releaseIPAddressRequestJSON) if err != nil { - fmt.Printf("Error received in releaseIPAddress post: %v ", err.Error()) + fmt.Printf("Error received in releaseIPAddress post: %v\n", err.Error()) return err } var releaseIPAddressResponse cns.Response err = json.NewDecoder(res.Body).Decode(&releaseIPAddressResponse) if err != nil { - fmt.Printf("Error received in decoding response from releaseIPAddress: %v ", err.Error()) + fmt.Printf("Error received in decoding response from releaseIPAddress: %v\n", err.Error()) return err } fmt.Printf("Response for releaseIPAddress: %+v\n", releaseIPAddressResponse) @@ -139,13 +139,13 @@ func getIPAddressUtilization() error { res, err := http.Get(defaultCNSServerURL + cns.GetIPAddressUtilizationPath) if err != nil { - fmt.Printf("Error received in getIPAddressUtilization GET: %v ", err.Error()) + fmt.Printf("Error received in getIPAddressUtilization GET: %v\n", err.Error()) return err } var iPAddressesUtilizationResponse cns.IPAddressesUtilizationResponse err = json.NewDecoder(res.Body).Decode(&iPAddressesUtilizationResponse) if err != nil { - fmt.Printf("Error received in decoding response from getIPAddressUtilization: %v ", err.Error()) + fmt.Printf("Error received in decoding response from getIPAddressUtilization: %v\n", err.Error()) return err } fmt.Printf("Response for getIPAddressUtilization: %+v\n", iPAddressesUtilizationResponse) @@ -156,13 +156,13 @@ func getHostLocalIP() error { res, err := http.Get(defaultCNSServerURL + cns.GetHostLocalIPPath) if err != nil { - fmt.Printf("Error received in getHostLocalIP GET: %v ", err.Error()) + fmt.Printf("Error received in getHostLocalIP GET: %v\n", err.Error()) return err } var hostLocalIPAddressResponse cns.HostLocalIPAddressResponse err = json.NewDecoder(res.Body).Decode(&hostLocalIPAddressResponse) if err != nil { - fmt.Printf("Error received in decoding response from getHostLocalIP: %v ", err.Error()) + fmt.Printf("Error received in decoding response from getHostLocalIP: %v\n", err.Error()) return err } fmt.Printf("Response for getHostLocalIP: %+v\n", hostLocalIPAddressResponse) @@ -173,13 +173,13 @@ func getUnhealthyIPAddresses() error { res, err := http.Get(defaultCNSServerURL + cns.GetUnhealthyIPAddressesPath) if err != nil { - fmt.Printf("Error received in GetUnhealthyIPAddresses IP Addresses: %v ", err.Error()) + fmt.Printf("Error received in GetUnhealthyIPAddresses IP Addresses: %v\n", err.Error()) return err } var getIPAddressesResponse cns.GetIPAddressesResponse err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) if err != nil { - fmt.Printf("Error received in decoding response from GetUnhealthyIPAddresses: %v ", err.Error()) + fmt.Printf("Error received in decoding response from GetUnhealthyIPAddresses: %v\n", err.Error()) return err } fmt.Printf("Response for GetUnhealthyIPAddresses: %+v\n", getIPAddressesResponse) @@ -189,10 +189,25 @@ func getUnhealthyIPAddresses() error { func main() { setEnvironment() createNetwork() - deleteNetwork() - reserveIPAddress() - releaseIPAddress() + reserveIPAddress("ip0") + reserveIPAddress("ip0") + reserveIPAddress("ip0") + reserveIPAddress("ip0") + reserveIPAddress("ip1") + reserveIPAddress("ip2") getUnhealthyIPAddresses() getIPAddressUtilization() + releaseIPAddress("ip10") + getIPAddressUtilization() + releaseIPAddress("ip2") + getIPAddressUtilization() + releaseIPAddress("ip1") + getIPAddressUtilization() + releaseIPAddress("ip0") + getIPAddressUtilization() + releaseIPAddress("ip0") + getIPAddressUtilization() getHostLocalIP() + deleteNetwork() + deleteNetwork() } From ca201327b7f68ec79091d7039b785aa3c09b2862 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Thu, 29 Jun 2017 10:01:19 -0700 Subject: [PATCH 31/34] Move cns common service code inside cns folder --- {common => cns/common}/service.go | 7 +++-- cns/restserver/restserver.go | 2 +- cns/restserver/restserver_test.go | 6 ++-- cns/service.go | 9 +++--- cns/service/main.go | 47 ++++++++++++++++--------------- 5 files changed, 37 insertions(+), 34 deletions(-) rename {common => cns/common}/service.go (96%) diff --git a/common/service.go b/cns/common/service.go similarity index 96% rename from common/service.go rename to cns/common/service.go index ae9fc2ab41..278c83aa19 100644 --- a/common/service.go +++ b/cns/common/service.go @@ -6,6 +6,7 @@ package common import ( "errors" + acn "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" ) @@ -31,7 +32,7 @@ type ServiceAPI interface { type ServiceConfig struct { Name string Version string - Listener *Listener + Listener *acn.Listener ErrChan chan error Store store.KeyValueStore } @@ -60,11 +61,11 @@ func (service *Service) Initialize(config *ServiceConfig) error { } log.Debugf("[Azure CNS] Going to initialize the service: %+v with config: %+v.", service, config) - + service.ErrChan = config.ErrChan service.Store = config.Store service.Version = config.Version - + log.Debugf("[Azure CNS] nitialized service: %+v with config: %+v.", service, config) return nil diff --git a/cns/restserver/restserver.go b/cns/restserver/restserver.go index 1a1883b7a1..ce95aa3d0c 100644 --- a/cns/restserver/restserver.go +++ b/cns/restserver/restserver.go @@ -11,11 +11,11 @@ import ( "net" "github.com/Azure/azure-container-networking/cns" + "github.com/Azure/azure-container-networking/cns/common" "github.com/Azure/azure-container-networking/cns/dockerclient" "github.com/Azure/azure-container-networking/cns/imdsclient" "github.com/Azure/azure-container-networking/cns/ipamclient" "github.com/Azure/azure-container-networking/cns/routes" - "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" ) diff --git a/cns/restserver/restserver_test.go b/cns/restserver/restserver_test.go index bf8b1ddc28..e7b3305659 100644 --- a/cns/restserver/restserver_test.go +++ b/cns/restserver/restserver_test.go @@ -13,7 +13,7 @@ import ( "testing" "github.com/Azure/azure-container-networking/cns" - "github.com/Azure/azure-container-networking/common" + "github.com/Azure/azure-container-networking/cns/common" ) var service HTTPService @@ -150,7 +150,7 @@ func TestReserveIPAddress(t *testing.T) { envRequestJSON := new(bytes.Buffer) json.NewEncoder(envRequestJSON).Encode(envRequest) - req, err := http.NewRequest(http.MethodGet, cns.ReserveIPAddressPath, envRequestJSON) + req, err := http.NewRequest(http.MethodPost, cns.ReserveIPAddressPath, envRequestJSON) if err != nil { t.Fatal(err) } @@ -174,7 +174,7 @@ func TestReleaseIPAddress(t *testing.T) { releaseIPAddressRequestJSON := new(bytes.Buffer) json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) - req, err := http.NewRequest(http.MethodGet, cns.ReleaseIPAddressPath, releaseIPAddressRequestJSON) + req, err := http.NewRequest(http.MethodPost, cns.ReleaseIPAddressPath, releaseIPAddressRequestJSON) if err != nil { t.Fatal(err) } diff --git a/cns/service.go b/cns/service.go index 9de475c762..62e8707913 100644 --- a/cns/service.go +++ b/cns/service.go @@ -7,7 +7,8 @@ import ( "net/http" "net/url" - "github.com/Azure/azure-container-networking/common" + "github.com/Azure/azure-container-networking/cns/common" + acn "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/store" ) @@ -22,7 +23,7 @@ const ( type Service struct { *common.Service EndpointType string - Listener *common.Listener + Listener *acn.Listener } // NewService creates a new Service object. @@ -40,7 +41,7 @@ func NewService(name, version string, store store.KeyValueStore) (*Service, erro // GetAPIServerURL returns the API server URL. func (service *Service) getAPIServerURL() string { - urls, _ := service.GetOption(common.OptAPIServerURL).(string) + urls, _ := service.GetOption(acn.OptAPIServerURL).(string) if urls == "" { urls = defaultAPIServerURL } @@ -64,7 +65,7 @@ func (service *Service) Initialize(config *common.ServiceConfig) error { } // Create the listener. - listener, err := common.NewListener(u) + listener, err := acn.NewListener(u) if err != nil { return err } diff --git a/cns/service/main.go b/cns/service/main.go index cd322e0903..99ec1e5937 100644 --- a/cns/service/main.go +++ b/cns/service/main.go @@ -9,8 +9,9 @@ import ( "os/signal" "syscall" + "github.com/Azure/azure-container-networking/cns/common" "github.com/Azure/azure-container-networking/cns/restserver" - "github.com/Azure/azure-container-networking/common" + acn "github.com/Azure/azure-container-networking/common" "github.com/Azure/azure-container-networking/log" "github.com/Azure/azure-container-networking/platform" "github.com/Azure/azure-container-networking/store" @@ -25,40 +26,40 @@ const ( var version string // Command line arguments for CNM plugin. -var args = common.ArgumentList{ +var args = acn.ArgumentList{ { - Name: common.OptAPIServerURL, - Shorthand: common.OptAPIServerURLAlias, + Name: acn.OptAPIServerURL, + Shorthand: acn.OptAPIServerURLAlias, Description: "Set the API server URL", Type: "string", DefaultValue: "", }, { - Name: common.OptLogLevel, - Shorthand: common.OptLogLevelAlias, + Name: acn.OptLogLevel, + Shorthand: acn.OptLogLevelAlias, Description: "Set the logging level", Type: "int", - DefaultValue: common.OptLogLevelInfo, + DefaultValue: acn.OptLogLevelInfo, ValueMap: map[string]interface{}{ - common.OptLogLevelInfo: log.LevelInfo, - common.OptLogLevelDebug: log.LevelDebug, + acn.OptLogLevelInfo: log.LevelInfo, + acn.OptLogLevelDebug: log.LevelDebug, }, }, { - Name: common.OptLogTarget, - Shorthand: common.OptLogTargetAlias, + Name: acn.OptLogTarget, + Shorthand: acn.OptLogTargetAlias, Description: "Set the logging target", Type: "int", - DefaultValue: common.OptLogTargetFile, + DefaultValue: acn.OptLogTargetFile, ValueMap: map[string]interface{}{ - common.OptLogTargetSyslog: log.TargetSyslog, - common.OptLogTargetStderr: log.TargetStderr, - common.OptLogTargetFile: log.TargetLogfile, + acn.OptLogTargetSyslog: log.TargetSyslog, + acn.OptLogTargetStderr: log.TargetStderr, + acn.OptLogTargetFile: log.TargetLogfile, }, }, { - Name: common.OptVersion, - Shorthand: common.OptVersionAlias, + Name: acn.OptVersion, + Shorthand: acn.OptVersionAlias, Description: "Print version information", Type: "bool", DefaultValue: false, @@ -74,12 +75,12 @@ func printVersion() { // Main is the entry point for CNS. func main() { // Initialize and parse command line arguments. - common.ParseArgs(&args, printVersion) + acn.ParseArgs(&args, printVersion) - url := common.GetArg(common.OptAPIServerURL).(string) - logLevel := common.GetArg(common.OptLogLevel).(int) - logTarget := common.GetArg(common.OptLogTarget).(int) - vers := common.GetArg(common.OptVersion).(bool) + url := acn.GetArg(acn.OptAPIServerURL).(string) + logLevel := acn.GetArg(acn.OptLogLevel).(int) + logTarget := acn.GetArg(acn.OptLogTarget).(int) + vers := acn.GetArg(acn.OptVersion).(bool) if vers { printVersion() @@ -122,7 +123,7 @@ func main() { log.Printf("Running on %v", platform.GetOSInfo()) // Set CNS options. - httpRestService.SetOption(common.OptAPIServerURL, url) + httpRestService.SetOption(acn.OptAPIServerURL, url) // Start CNS. if httpRestService != nil { From 5554350dfd09e7e8a1e2d29ae7d3c50d4bc63b66 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Thu, 29 Jun 2017 10:09:23 -0700 Subject: [PATCH 32/34] Add line spacing in restserver test code. --- cns/restserver/restserver_test.go | 36 +++++++++++++++++++++---------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/cns/restserver/restserver_test.go b/cns/restserver/restserver_test.go index e7b3305659..7a73bd6967 100644 --- a/cns/restserver/restserver_test.go +++ b/cns/restserver/restserver_test.go @@ -75,14 +75,17 @@ func setEnv(t *testing.T) *httptest.ResponseRecorder { if err != nil { t.Fatal(err) } + w := httptest.NewRecorder() mux.ServeHTTP(w, req) return w } func TestSetEnvironment(t *testing.T) { fmt.Println("Test: SetEnvironment") + var resp cns.Response w := setEnv(t) + err := decodeResponse(w, &resp) if err != nil || resp.ReturnCode != 0 { t.Errorf("SetEnvironment failed with response %+v", resp) @@ -94,19 +97,24 @@ func TestSetEnvironment(t *testing.T) { // Tests CreateNetwork functionality. func TestCreateNetwork(t *testing.T) { fmt.Println("Test: CreateNetwork") + var body bytes.Buffer setEnv(t) info := &cns.CreateNetworkRequest{ NetworkName: "azurenet", } + json.NewEncoder(&body).Encode(info) + req, err := http.NewRequest(http.MethodPost, cns.CreateNetworkPath, &body) if err != nil { t.Fatal(err) } + w := httptest.NewRecorder() mux.ServeHTTP(w, req) var resp cns.Response + err = decodeResponse(w, &resp) if err != nil || resp.ReturnCode != 0 { t.Errorf("CreateNetwork failed with response %+v", resp) @@ -118,19 +126,24 @@ func TestCreateNetwork(t *testing.T) { // Tests DeleteNetwork functionality. func TestDeleteNetwork(t *testing.T) { fmt.Println("Test: DeleteNetwork") + var body bytes.Buffer setEnv(t) info := &cns.DeleteNetworkRequest{ NetworkName: "azurenet", } + json.NewEncoder(&body).Encode(info) + req, err := http.NewRequest(http.MethodPost, cns.DeleteNetworkPath, &body) if err != nil { t.Fatal(err) } + w := httptest.NewRecorder() mux.ServeHTTP(w, req) var resp cns.Response + err = decodeResponse(w, &resp) if err != nil || resp.ReturnCode != 0 { t.Errorf("DeleteNetwork failed with response %+v", resp) @@ -145,7 +158,6 @@ func TestReserveIPAddress(t *testing.T) { reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID: "ip01"} reserveIPRequestJSON := new(bytes.Buffer) json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) - envRequest := cns.SetEnvironmentRequest{Location: "Azure", NetworkType: "Underlay"} envRequestJSON := new(bytes.Buffer) json.NewEncoder(envRequestJSON).Encode(envRequest) @@ -157,10 +169,9 @@ func TestReserveIPAddress(t *testing.T) { w := httptest.NewRecorder() mux.ServeHTTP(w, req) - var reserveIPAddressResponse cns.ReserveIPAddressResponse - err = decodeResponse(w, &reserveIPAddressResponse) + err = decodeResponse(w, &reserveIPAddressResponse) if err != nil || reserveIPAddressResponse.Response.ReturnCode != 0 { t.Errorf("SetEnvironment failed with response %+v", reserveIPAddressResponse) } else { @@ -170,6 +181,7 @@ func TestReserveIPAddress(t *testing.T) { func TestReleaseIPAddress(t *testing.T) { fmt.Println("Test: ReleaseIPAddress") + releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID: "ip01"} releaseIPAddressRequestJSON := new(bytes.Buffer) json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) @@ -181,10 +193,9 @@ func TestReleaseIPAddress(t *testing.T) { w := httptest.NewRecorder() mux.ServeHTTP(w, req) - var releaseIPAddressResponse cns.Response - err = decodeResponse(w, &releaseIPAddressResponse) + err = decodeResponse(w, &releaseIPAddressResponse) if err != nil || releaseIPAddressResponse.ReturnCode != 0 { t.Errorf("SetEnvironment failed with response %+v", releaseIPAddressResponse) } else { @@ -198,12 +209,12 @@ func TestGetIPAddressUtilization(t *testing.T) { if err != nil { t.Fatal(err) } + w := httptest.NewRecorder() mux.ServeHTTP(w, req) - var iPAddressesUtilizationResponse cns.IPAddressesUtilizationResponse - err = decodeResponse(w, &iPAddressesUtilizationResponse) + err = decodeResponse(w, &iPAddressesUtilizationResponse) if err != nil || iPAddressesUtilizationResponse.Response.ReturnCode != 0 { t.Errorf("GetIPAddressUtilization failed with response %+v", iPAddressesUtilizationResponse) } else { @@ -213,17 +224,19 @@ func TestGetIPAddressUtilization(t *testing.T) { func TestGetHostLocalIP(t *testing.T) { fmt.Println("Test: GetHostLocalIP") + setEnv(t) + req, err := http.NewRequest(http.MethodGet, cns.GetHostLocalIPPath, nil) if err != nil { t.Fatal(err) } + w := httptest.NewRecorder() mux.ServeHTTP(w, req) - var hostLocalIPAddressResponse cns.HostLocalIPAddressResponse - err = decodeResponse(w, &hostLocalIPAddressResponse) + err = decodeResponse(w, &hostLocalIPAddressResponse) if err != nil || hostLocalIPAddressResponse.Response.ReturnCode != 0 { t.Errorf("GetHostLocalIP failed with response %+v", hostLocalIPAddressResponse) } else { @@ -233,16 +246,17 @@ func TestGetHostLocalIP(t *testing.T) { func TestGetUnhealthyIPAddresses(t *testing.T) { fmt.Println("Test: GetGhostIPAddresses") + req, err := http.NewRequest(http.MethodGet, cns.GetUnhealthyIPAddressesPath, nil) if err != nil { t.Fatal(err) } + w := httptest.NewRecorder() mux.ServeHTTP(w, req) - var getIPAddressesResponse cns.GetIPAddressesResponse - err = decodeResponse(w, &getIPAddressesResponse) + err = decodeResponse(w, &getIPAddressesResponse) if err != nil || getIPAddressesResponse.Response.ReturnCode != 0 { t.Errorf("GetUnhealthyIPAddresses failed with response %+v", getIPAddressesResponse) } else { From 6731050515ff2c01b911b5d633a83a5e8987d426 Mon Sep 17 00:00:00 2001 From: Tamilmani Manoharan Date: Fri, 30 Jun 2017 12:04:27 -0700 Subject: [PATCH 33/34] Removed ipamclient api.go and resused cnm ipam package --- cns/ipamclient/api.go | 73 ------------------------------------ cns/ipamclient/ipamclient.go | 31 +++++++-------- 2 files changed, 16 insertions(+), 88 deletions(-) delete mode 100644 cns/ipamclient/api.go diff --git a/cns/ipamclient/api.go b/cns/ipamclient/api.go deleted file mode 100644 index f3838abd4e..0000000000 --- a/cns/ipamclient/api.go +++ /dev/null @@ -1,73 +0,0 @@ -package ipamclient - -// IPAM Plugin API Contract. -const ( - getAddressSpacesPath = "/IpamDriver.GetDefaultAddressSpaces" - requestPoolPath = "/IpamDriver.RequestPool" - reserveAddrPath = "/IpamDriver.RequestAddress" - releaseAddrPath = "/IpamDriver.ReleaseAddress" - getPoolInfoPath = "/IpamDriver.GetPoolInfo" -) - -// Response received from IPAM Plugin when request AddressSpace. -type getAddressSpacesResponse struct { - Err string - LocalDefaultAddressSpace string - GlobalDefaultAddressSpace string -} - -// Request sent to IPAM plugin to request a pool. -type requestPoolRequest struct { - AddressSpace string - Pool string - SubPool string - Options map[string]string - V6 bool -} - -// Response received from IPAM Plugin when requesting a pool. -type requestPoolResponse struct { - Err string - PoolID string - Pool string - Data map[string]string -} - -// Request sent to IPAM plugin to request IP reservation. -type reserveAddrRequest struct { - PoolID string - Address string - Options map[string]string -} - -// Response received from IPAM Plugin when requesting a IP reservation. -type reserveAddrResponse struct { - Err string - Address string - Data map[string]string -} - -// Request sent to IPAM plugin to release IP reservation. -type releaseAddrRequest struct { - PoolID string - Address string - Options map[string]string -} - -// Response received from IPAM Plugin when requesting IP release. -type releaseAddrResponse struct { - Err string -} - -// Request sent to IPAM plugin to query address pool information. -type getPoolInfoRequest struct { - PoolID string -} - -// Response sent by plugin when returning address pool information. -type getPoolInfoResponse struct { - Err string - Capacity int - Available int - UnhealthyAddresses []string -} diff --git a/cns/ipamclient/ipamclient.go b/cns/ipamclient/ipamclient.go index 513192a30a..0c14175221 100644 --- a/cns/ipamclient/ipamclient.go +++ b/cns/ipamclient/ipamclient.go @@ -10,7 +10,8 @@ import ( "fmt" - "github.com/Azure/azure-container-networking/ipam" + cnmIpam "github.com/Azure/azure-container-networking/cnm/ipam" + ipam "github.com/Azure/azure-container-networking/ipam" "github.com/Azure/azure-container-networking/log" ) @@ -33,7 +34,7 @@ func NewIpamClient(url string) (*IpamClient, error) { func (ic *IpamClient) GetAddressSpace() (string, error) { log.Printf("[Azure CNS] GetAddressSpace Request") - url := ic.connectionURL + getAddressSpacesPath + url := ic.connectionURL + cnmIpam.GetAddressSpacesPath req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { @@ -51,7 +52,7 @@ func (ic *IpamClient) GetAddressSpace() (string, error) { } if res.StatusCode == 200 { - var resp getAddressSpacesResponse + var resp cnmIpam.GetDefaultAddressSpacesResponse err := json.NewDecoder(res.Body).Decode(&resp) if err != nil { log.Printf("[Azure CNS] Error received while parsing GetAddressSpace response resp:%v err:%v", res.Body, err.Error()) @@ -74,9 +75,9 @@ func (ic *IpamClient) GetPoolID(asID, subnet string) (string, error) { var body bytes.Buffer log.Printf("[Azure CNS] GetPoolID Request") - url := ic.connectionURL + requestPoolPath + url := ic.connectionURL + cnmIpam.RequestPoolPath - payload := &requestPoolRequest{ + payload := &cnmIpam.RequestPoolRequest{ AddressSpace: asID, Pool: subnet, } @@ -98,7 +99,7 @@ func (ic *IpamClient) GetPoolID(asID, subnet string) (string, error) { } if res.StatusCode == 200 { - var resp requestPoolResponse + var resp cnmIpam.RequestPoolResponse err := json.NewDecoder(res.Body).Decode(&resp) if err != nil { log.Printf("[Azure CNS] Error received while parsing GetPoolID response resp:%v err:%v", res.Body, err.Error()) @@ -122,9 +123,9 @@ func (ic *IpamClient) ReserveIPAddress(poolID string, reservationID string) (str var body bytes.Buffer log.Printf("[Azure CNS] ReserveIpAddress") - url := ic.connectionURL + reserveAddrPath + url := ic.connectionURL + cnmIpam.RequestAddressPath - payload := &reserveAddrRequest{ + payload := &cnmIpam.RequestAddressRequest{ PoolID: poolID, Address: "", Options: make(map[string]string), @@ -147,7 +148,7 @@ func (ic *IpamClient) ReserveIPAddress(poolID string, reservationID string) (str } if res.StatusCode == 200 { - var reserveResp reserveAddrResponse + var reserveResp cnmIpam.RequestAddressResponse err = json.NewDecoder(res.Body).Decode(&reserveResp) if err != nil { @@ -172,9 +173,9 @@ func (ic *IpamClient) ReleaseIPAddress(poolID string, reservationID string) erro var body bytes.Buffer log.Printf("[Azure CNS] ReleaseIpAddress") - url := ic.connectionURL + releaseAddrPath + url := ic.connectionURL + cnmIpam.ReleaseAddressPath - payload := &releaseAddrRequest{ + payload := &cnmIpam.ReleaseAddressRequest{ PoolID: poolID, Address: "", Options: make(map[string]string), @@ -199,7 +200,7 @@ func (ic *IpamClient) ReleaseIPAddress(poolID string, reservationID string) erro } if res.StatusCode == 200 { - var releaseResp releaseAddrResponse + var releaseResp cnmIpam.ReleaseAddressResponse err := json.NewDecoder(res.Body).Decode(&releaseResp) if err != nil { log.Printf("[Azure CNS] Error received while parsing release response :%v err:%v", res.Body, err.Error()) @@ -223,9 +224,9 @@ func (ic *IpamClient) GetIPAddressUtilization(poolID string) (int, int, []string var body bytes.Buffer log.Printf("[Azure CNS] GetIPAddressUtilization") - url := ic.connectionURL + getPoolInfoPath + url := ic.connectionURL + cnmIpam.GetPoolInfoPath - payload := &getPoolInfoRequest{ + payload := &cnmIpam.GetPoolInfoRequest{ PoolID: poolID, } @@ -246,7 +247,7 @@ func (ic *IpamClient) GetIPAddressUtilization(poolID string) (int, int, []string } if res.StatusCode == 200 { - var poolInfoResp getPoolInfoResponse + var poolInfoResp cnmIpam.GetPoolInfoResponse err := json.NewDecoder(res.Body).Decode(&poolInfoResp) if err != nil { log.Printf("[Azure CNS] Error received while parsing GetIPUtilization response :%v err:%v", res.Body, err.Error()) From d18b329c6f81cbe91522367540453c070c96cbd1 Mon Sep 17 00:00:00 2001 From: Sushant Sharma Date: Wed, 5 Jul 2017 17:05:06 -0700 Subject: [PATCH 34/34] Remove examples --- cns/examples/examples.go | 213 ------------------------------ cns/restserver/restserver_test.go | 2 + 2 files changed, 2 insertions(+), 213 deletions(-) delete mode 100644 cns/examples/examples.go diff --git a/cns/examples/examples.go b/cns/examples/examples.go deleted file mode 100644 index 2f0a576b00..0000000000 --- a/cns/examples/examples.go +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2017 Microsoft. All rights reserved. -// MIT License - -package main - -import ( - "bytes" - "encoding/json" - "fmt" - "net/http" - - "github.com/Azure/azure-container-networking/cns" -) - -const ( - defaultCNSServerURL = "http://localhost:10090" -) - -// These are example showing how to use CNS APIs by clients - -func setEnvironment() error { - envRequest := cns.SetEnvironmentRequest{Location: "Azure", NetworkType: "Underlay"} - envRequestJSON := new(bytes.Buffer) - json.NewEncoder(envRequestJSON).Encode(envRequest) - res, err := - http.Post( - defaultCNSServerURL+cns.SetEnvironmentPath, - "application/json; charset=utf-8", - envRequestJSON) - if err != nil { - fmt.Printf("Error received in Set Env: %v\n", err.Error()) - return err - } - var setEnvironmentResponse cns.Response - err = json.NewDecoder(res.Body).Decode(&setEnvironmentResponse) - if err != nil { - fmt.Printf("Error received in decoding response from SetEnvironment: %v\n", err.Error()) - return err - } - fmt.Printf("Response for SetEnvironment: %+v\n", setEnvironmentResponse) - return nil -} - -func createNetwork() error { - netRequest := cns.CreateNetworkRequest{NetworkName: "azurenet"} - netRequestJSON := new(bytes.Buffer) - json.NewEncoder(netRequestJSON).Encode(netRequest) - res, err := - http.Post( - defaultCNSServerURL+cns.CreateNetworkPath, - "application/json; charset=utf-8", - netRequestJSON) - if err != nil { - fmt.Printf("Error received in CreateNetwork post: %v\n", err.Error()) - return err - } - - var createNetworkResponse cns.Response - err = json.NewDecoder(res.Body).Decode(&createNetworkResponse) - if err != nil { - fmt.Printf("Error received in decoding response from CreateNEtwork: %v\n", err.Error()) - return err - } - fmt.Printf("Response for CreateNetwork: %+v\n", createNetworkResponse) - return nil -} - -func deleteNetwork() error { - netRequest := cns.DeleteNetworkRequest{NetworkName: "azurenet"} - netRequestJSON := new(bytes.Buffer) - json.NewEncoder(netRequestJSON).Encode(netRequest) - res, err := - http.Post( - defaultCNSServerURL+cns.DeleteNetworkPath, - "application/json; charset=utf-8", - netRequestJSON) - if err != nil { - fmt.Printf("Error received in DeleteNetwork post: %v\n", err.Error()) - return err - } - - var deleteNetworkResponse cns.Response - err = json.NewDecoder(res.Body).Decode(&deleteNetworkResponse) - if err != nil { - fmt.Printf("Error received in decoding response from DeleteNetwork: %v\n", err.Error()) - return err - } - fmt.Printf("Response for DeleteNetwork: %+v\n", deleteNetworkResponse) - return nil -} - -func reserveIPAddress(ipID string) error { - reserveIPRequest := cns.ReserveIPAddressRequest{ReservationID: ipID} - reserveIPRequestJSON := new(bytes.Buffer) - json.NewEncoder(reserveIPRequestJSON).Encode(reserveIPRequest) - res, err := - http.Post( - defaultCNSServerURL+cns.ReserveIPAddressPath, - "application/json; charset=utf-8", - reserveIPRequestJSON) - if err != nil { - fmt.Printf("Error received in reserveIPAddress post: %v\n", err.Error()) - return err - } - var reserveIPAddressResponse cns.ReserveIPAddressResponse - err = json.NewDecoder(res.Body).Decode(&reserveIPAddressResponse) - if err != nil { - fmt.Printf("Error received in decoding response from reserveIPAddress: %v\n", err.Error()) - return err - } - fmt.Printf("Response for reserveIPAddress: %+v\n", reserveIPAddressResponse) - return nil -} - -func releaseIPAddress(ipID string) error { - releaseIPRequest := cns.ReleaseIPAddressRequest{ReservationID: ipID} - releaseIPAddressRequestJSON := new(bytes.Buffer) - json.NewEncoder(releaseIPAddressRequestJSON).Encode(releaseIPRequest) - res, err := - http.Post( - defaultCNSServerURL+cns.ReleaseIPAddressPath, - "application/json; charset=utf-8", - releaseIPAddressRequestJSON) - if err != nil { - fmt.Printf("Error received in releaseIPAddress post: %v\n", err.Error()) - return err - } - var releaseIPAddressResponse cns.Response - err = json.NewDecoder(res.Body).Decode(&releaseIPAddressResponse) - if err != nil { - fmt.Printf("Error received in decoding response from releaseIPAddress: %v\n", err.Error()) - return err - } - fmt.Printf("Response for releaseIPAddress: %+v\n", releaseIPAddressResponse) - return nil -} - -func getIPAddressUtilization() error { - res, err := - http.Get(defaultCNSServerURL + cns.GetIPAddressUtilizationPath) - if err != nil { - fmt.Printf("Error received in getIPAddressUtilization GET: %v\n", err.Error()) - return err - } - var iPAddressesUtilizationResponse cns.IPAddressesUtilizationResponse - err = json.NewDecoder(res.Body).Decode(&iPAddressesUtilizationResponse) - if err != nil { - fmt.Printf("Error received in decoding response from getIPAddressUtilization: %v\n", err.Error()) - return err - } - fmt.Printf("Response for getIPAddressUtilization: %+v\n", iPAddressesUtilizationResponse) - return nil -} - -func getHostLocalIP() error { - res, err := - http.Get(defaultCNSServerURL + cns.GetHostLocalIPPath) - if err != nil { - fmt.Printf("Error received in getHostLocalIP GET: %v\n", err.Error()) - return err - } - var hostLocalIPAddressResponse cns.HostLocalIPAddressResponse - err = json.NewDecoder(res.Body).Decode(&hostLocalIPAddressResponse) - if err != nil { - fmt.Printf("Error received in decoding response from getHostLocalIP: %v\n", err.Error()) - return err - } - fmt.Printf("Response for getHostLocalIP: %+v\n", hostLocalIPAddressResponse) - return nil -} - -func getUnhealthyIPAddresses() error { - res, err := - http.Get(defaultCNSServerURL + cns.GetUnhealthyIPAddressesPath) - if err != nil { - fmt.Printf("Error received in GetUnhealthyIPAddresses IP Addresses: %v\n", err.Error()) - return err - } - var getIPAddressesResponse cns.GetIPAddressesResponse - err = json.NewDecoder(res.Body).Decode(&getIPAddressesResponse) - if err != nil { - fmt.Printf("Error received in decoding response from GetUnhealthyIPAddresses: %v\n", err.Error()) - return err - } - fmt.Printf("Response for GetUnhealthyIPAddresses: %+v\n", getIPAddressesResponse) - return nil -} - -func main() { - setEnvironment() - createNetwork() - reserveIPAddress("ip0") - reserveIPAddress("ip0") - reserveIPAddress("ip0") - reserveIPAddress("ip0") - reserveIPAddress("ip1") - reserveIPAddress("ip2") - getUnhealthyIPAddresses() - getIPAddressUtilization() - releaseIPAddress("ip10") - getIPAddressUtilization() - releaseIPAddress("ip2") - getIPAddressUtilization() - releaseIPAddress("ip1") - getIPAddressUtilization() - releaseIPAddress("ip0") - getIPAddressUtilization() - releaseIPAddress("ip0") - getIPAddressUtilization() - getHostLocalIP() - deleteNetwork() - deleteNetwork() -} diff --git a/cns/restserver/restserver_test.go b/cns/restserver/restserver_test.go index 7a73bd6967..9e9247862f 100644 --- a/cns/restserver/restserver_test.go +++ b/cns/restserver/restserver_test.go @@ -80,6 +80,7 @@ func setEnv(t *testing.T) *httptest.ResponseRecorder { mux.ServeHTTP(w, req) return w } + func TestSetEnvironment(t *testing.T) { fmt.Println("Test: SetEnvironment") @@ -202,6 +203,7 @@ func TestReleaseIPAddress(t *testing.T) { fmt.Printf("SetEnvironment Responded with %+v\n", releaseIPAddressResponse) } } + func TestGetIPAddressUtilization(t *testing.T) { fmt.Println("Test: GetIPAddressUtilization")