mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
IT for e2e network-plugin support for swarmkit & services
As of https://github.com/docker/swarmkit/pull/1607, swarmkit honors global network plugins while allocating network resources. This IT covers the e2e integration between libnetwork, swarmkit and docker engine to support global network-plugins for swarm-mode Signed-off-by: Madhu Venugopal <madhu@docker.com>
This commit is contained in:
parent
5e9c78aeaf
commit
af185a3806
2 changed files with 206 additions and 0 deletions
|
@ -2,6 +2,7 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net/http/httptest"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -240,6 +241,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
type DockerSwarmSuite struct {
|
type DockerSwarmSuite struct {
|
||||||
|
server *httptest.Server
|
||||||
ds *DockerSuite
|
ds *DockerSuite
|
||||||
daemons []*SwarmDaemon
|
daemons []*SwarmDaemon
|
||||||
daemonsLock sync.Mutex // protect access to daemons
|
daemonsLock sync.Mutex // protect access to daemons
|
||||||
|
|
|
@ -3,13 +3,22 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/swarm"
|
"github.com/docker/docker/api/types/swarm"
|
||||||
"github.com/docker/docker/pkg/integration/checker"
|
"github.com/docker/docker/pkg/integration/checker"
|
||||||
|
"github.com/docker/libnetwork/driverapi"
|
||||||
|
"github.com/docker/libnetwork/ipamapi"
|
||||||
|
remoteipam "github.com/docker/libnetwork/ipams/remote/api"
|
||||||
"github.com/go-check/check"
|
"github.com/go-check/check"
|
||||||
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
|
func (s *DockerSwarmSuite) TestSwarmUpdate(c *check.C) {
|
||||||
|
@ -364,3 +373,198 @@ func (s *DockerSwarmSuite) TestPsListContainersFilterIsTask(c *check.C) {
|
||||||
c.Assert(lines, checker.HasLen, 1)
|
c.Assert(lines, checker.HasLen, 1)
|
||||||
c.Assert(lines[0], checker.Not(checker.Equals), bareID, check.Commentf("Expected not %s, but got it for is-task label, output %q", bareID, out))
|
c.Assert(lines[0], checker.Not(checker.Equals), bareID, check.Commentf("Expected not %s, but got it for is-task label, output %q", bareID, out))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const globalNetworkPlugin = "global-network-plugin"
|
||||||
|
const globalIPAMPlugin = "global-ipam-plugin"
|
||||||
|
|
||||||
|
func (s *DockerSwarmSuite) SetUpSuite(c *check.C) {
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
s.server = httptest.NewServer(mux)
|
||||||
|
c.Assert(s.server, check.NotNil, check.Commentf("Failed to start an HTTP Server"))
|
||||||
|
setupRemoteGlobalNetworkPlugin(c, mux, s.server.URL, globalNetworkPlugin, globalIPAMPlugin)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setupRemoteGlobalNetworkPlugin(c *check.C, mux *http.ServeMux, url, netDrv, ipamDrv string) {
|
||||||
|
|
||||||
|
mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
fmt.Fprintf(w, `{"Implements": ["%s", "%s"]}`, driverapi.NetworkPluginEndpointType, ipamapi.PluginEndpointType)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Network driver implementation
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.GetCapabilities", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
fmt.Fprintf(w, `{"Scope":"global"}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.AllocateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
fmt.Fprintf(w, "null")
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.FreeNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
fmt.Fprintf(w, "null")
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&remoteDriverNetworkRequest)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
fmt.Fprintf(w, "null")
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.DeleteNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
fmt.Fprintf(w, "null")
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.CreateEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
fmt.Fprintf(w, `{"Interface":{"MacAddress":"a0:b1:c2:d3:e4:f5"}}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.Join", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
|
||||||
|
veth := &netlink.Veth{
|
||||||
|
LinkAttrs: netlink.LinkAttrs{Name: "randomIfName", TxQLen: 0}, PeerName: "cnt0"}
|
||||||
|
if err := netlink.LinkAdd(veth); err != nil {
|
||||||
|
fmt.Fprintf(w, `{"Error":"failed to add veth pair: `+err.Error()+`"}`)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, `{"InterfaceName":{ "SrcName":"cnt0", "DstPrefix":"veth"}}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.Leave", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
fmt.Fprintf(w, "null")
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.DeleteEndpoint", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
if link, err := netlink.LinkByName("cnt0"); err == nil {
|
||||||
|
netlink.LinkDel(link)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "null")
|
||||||
|
})
|
||||||
|
|
||||||
|
// IPAM Driver implementation
|
||||||
|
var (
|
||||||
|
poolRequest remoteipam.RequestPoolRequest
|
||||||
|
poolReleaseReq remoteipam.ReleasePoolRequest
|
||||||
|
addressRequest remoteipam.RequestAddressRequest
|
||||||
|
addressReleaseReq remoteipam.ReleaseAddressRequest
|
||||||
|
lAS = "localAS"
|
||||||
|
gAS = "globalAS"
|
||||||
|
pool = "172.28.0.0/16"
|
||||||
|
poolID = lAS + "/" + pool
|
||||||
|
gw = "172.28.255.254/16"
|
||||||
|
)
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.GetDefaultAddressSpaces", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
fmt.Fprintf(w, `{"LocalDefaultAddressSpace":"`+lAS+`", "GlobalDefaultAddressSpace": "`+gAS+`"}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.RequestPool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&poolRequest)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
if poolRequest.AddressSpace != lAS && poolRequest.AddressSpace != gAS {
|
||||||
|
fmt.Fprintf(w, `{"Error":"Unknown address space in pool request: `+poolRequest.AddressSpace+`"}`)
|
||||||
|
} else if poolRequest.Pool != "" && poolRequest.Pool != pool {
|
||||||
|
fmt.Fprintf(w, `{"Error":"Cannot handle explicit pool requests yet"}`)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, `{"PoolID":"`+poolID+`", "Pool":"`+pool+`"}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.RequestAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&addressRequest)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
// make sure libnetwork is now querying on the expected pool id
|
||||||
|
if addressRequest.PoolID != poolID {
|
||||||
|
fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
|
||||||
|
} else if addressRequest.Address != "" {
|
||||||
|
fmt.Fprintf(w, `{"Error":"Cannot handle explicit address requests yet"}`)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, `{"Address":"`+gw+`"}`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.ReleaseAddress", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&addressReleaseReq)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
// make sure libnetwork is now asking to release the expected address from the expected poolid
|
||||||
|
if addressRequest.PoolID != poolID {
|
||||||
|
fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
|
||||||
|
} else if addressReleaseReq.Address != gw {
|
||||||
|
fmt.Fprintf(w, `{"Error":"unknown address"}`)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "null")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc(fmt.Sprintf("/%s.ReleasePool", ipamapi.PluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
err := json.NewDecoder(r.Body).Decode(&poolReleaseReq)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Unable to decode JSON payload: "+err.Error(), http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
|
||||||
|
// make sure libnetwork is now asking to release the expected poolid
|
||||||
|
if addressRequest.PoolID != poolID {
|
||||||
|
fmt.Fprintf(w, `{"Error":"unknown pool id"}`)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(w, "null")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
err := os.MkdirAll("/etc/docker/plugins", 0755)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
fileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", netDrv)
|
||||||
|
err = ioutil.WriteFile(fileName, []byte(url), 0644)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
|
||||||
|
ipamFileName := fmt.Sprintf("/etc/docker/plugins/%s.spec", ipamDrv)
|
||||||
|
err = ioutil.WriteFile(ipamFileName, []byte(url), 0644)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *DockerSwarmSuite) TestSwarmNetworkPlugin(c *check.C) {
|
||||||
|
d := s.AddDaemon(c, true, true)
|
||||||
|
|
||||||
|
out, err := d.Cmd("network", "create", "-d", globalNetworkPlugin, "foo")
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
|
||||||
|
|
||||||
|
name := "top"
|
||||||
|
out, err = d.Cmd("service", "create", "--name", name, "--network", "foo", "busybox", "top")
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Not(checker.Equals), "")
|
||||||
|
|
||||||
|
out, err = d.Cmd("service", "inspect", "--format", "{{range .Spec.Networks}}{{.Target}}{{end}}", name)
|
||||||
|
c.Assert(err, checker.IsNil)
|
||||||
|
c.Assert(strings.TrimSpace(out), checker.Equals, "foo")
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue