123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311 |
- package main
- import (
- "bytes"
- "encoding/json"
- "fmt"
- "io"
- "io/ioutil"
- "net/http"
- "net/http/httptest"
- "os"
- "strings"
- "github.com/codegangsta/cli"
- "github.com/docker/docker/pkg/parsers"
- "github.com/docker/docker/pkg/reexec"
- "github.com/Sirupsen/logrus"
- "github.com/docker/docker/pkg/term"
- "github.com/docker/libnetwork"
- "github.com/docker/libnetwork/api"
- "github.com/docker/libnetwork/config"
- "github.com/docker/libnetwork/driverapi"
- "github.com/docker/libnetwork/netlabel"
- "github.com/docker/libnetwork/options"
- "github.com/gorilla/mux"
- )
- const (
- // DefaultHTTPHost is used if only port is provided to -H flag e.g. docker -d -H tcp://:8080
- DefaultHTTPHost = "0.0.0.0"
- // DefaultHTTPPort is the default http port used by dnet
- DefaultHTTPPort = 2385
- // DefaultUnixSocket exported
- DefaultUnixSocket = "/var/run/dnet.sock"
- cfgFileEnv = "LIBNETWORK_CFG"
- defaultCfgFile = "/etc/default/libnetwork.toml"
- )
- var epConn *dnetConnection
- func main() {
- if reexec.Init() {
- return
- }
- _, stdout, stderr := term.StdStreams()
- logrus.SetOutput(stderr)
- err := dnetApp(stdout, stderr)
- if err != nil {
- os.Exit(1)
- }
- }
- func parseConfig(cfgFile string) (*config.Config, error) {
- if strings.Trim(cfgFile, " ") == "" {
- cfgFile = os.Getenv(cfgFileEnv)
- if strings.Trim(cfgFile, " ") == "" {
- cfgFile = defaultCfgFile
- }
- }
- return config.ParseConfig(cfgFile)
- }
- func processConfig(cfg *config.Config) []config.Option {
- options := []config.Option{}
- if cfg == nil {
- return options
- }
- dn := "bridge"
- if strings.TrimSpace(cfg.Daemon.DefaultNetwork) != "" {
- dn = cfg.Daemon.DefaultNetwork
- }
- options = append(options, config.OptionDefaultNetwork(dn))
- dd := "bridge"
- if strings.TrimSpace(cfg.Daemon.DefaultDriver) != "" {
- dd = cfg.Daemon.DefaultDriver
- }
- options = append(options, config.OptionDefaultDriver(dd))
- if cfg.Daemon.Labels != nil {
- options = append(options, config.OptionLabels(cfg.Daemon.Labels))
- }
- if strings.TrimSpace(cfg.GlobalStore.Client.Provider) != "" {
- options = append(options, config.OptionKVProvider(cfg.GlobalStore.Client.Provider))
- }
- if strings.TrimSpace(cfg.GlobalStore.Client.Address) != "" {
- options = append(options, config.OptionKVProviderURL(cfg.GlobalStore.Client.Address))
- }
- return options
- }
- func dnetApp(stdout, stderr io.Writer) error {
- app := cli.NewApp()
- app.Name = "dnet"
- app.Usage = "A self-sufficient runtime for container networking."
- app.Flags = dnetFlags
- app.Before = processFlags
- app.Commands = dnetCommands
- app.Run(os.Args)
- return nil
- }
- func createDefaultNetwork(c libnetwork.NetworkController) {
- nw := c.Config().Daemon.DefaultNetwork
- d := c.Config().Daemon.DefaultDriver
- createOptions := []libnetwork.NetworkOption{}
- genericOption := options.Generic{}
- if nw != "" && d != "" {
- // Bridge driver is special due to legacy reasons
- if d == "bridge" {
- genericOption[netlabel.GenericData] = map[string]interface{}{
- "BridgeName": nw,
- }
- networkOption := libnetwork.NetworkOptionGeneric(genericOption)
- createOptions = append(createOptions, networkOption)
- }
- _, err := c.NewNetwork(d, nw, createOptions...)
- if err != nil {
- logrus.Errorf("Error creating default network : %s : %v", nw, err)
- }
- }
- }
- type dnetConnection struct {
- // proto holds the client protocol i.e. unix.
- proto string
- // addr holds the client address.
- addr string
- }
- func (d *dnetConnection) dnetDaemon(cfgFile string) error {
- if err := startTestDriver(); err != nil {
- return fmt.Errorf("failed to start test driver: %v\n", err)
- }
- cfg, err := parseConfig(cfgFile)
- var cOptions []config.Option
- if err == nil {
- cOptions = processConfig(cfg)
- }
- controller, err := libnetwork.New(cOptions...)
- if err != nil {
- fmt.Println("Error starting dnetDaemon :", err)
- return err
- }
- createDefaultNetwork(controller)
- httpHandler := api.NewHTTPHandler(controller)
- r := mux.NewRouter().StrictSlash(false)
- post := r.PathPrefix("/{.*}/networks").Subrouter()
- post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
- post = r.PathPrefix("/networks").Subrouter()
- post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
- post = r.PathPrefix("/{.*}/services").Subrouter()
- post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
- post = r.PathPrefix("/services").Subrouter()
- post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
- post = r.PathPrefix("/{.*}/sandboxes").Subrouter()
- post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
- post = r.PathPrefix("/sandboxes").Subrouter()
- post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
- return http.ListenAndServe(d.addr, r)
- }
- func startTestDriver() error {
- mux := http.NewServeMux()
- server := httptest.NewServer(mux)
- if server == nil {
- return fmt.Errorf("Failed to start a HTTP Server")
- }
- 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"]}`, driverapi.NetworkPluginEndpointType)
- })
- 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.CreateNetwork", 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.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, "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")
- fmt.Fprintf(w, "null")
- })
- 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")
- fmt.Fprintf(w, "null")
- })
- 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")
- })
- if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil {
- return err
- }
- if err := ioutil.WriteFile("/usr/share/docker/plugins/test.spec", []byte(server.URL), 0644); err != nil {
- return err
- }
- return nil
- }
- func newDnetConnection(val string) (*dnetConnection, error) {
- url, err := parsers.ParseHost(DefaultHTTPHost, DefaultUnixSocket, val)
- if err != nil {
- return nil, err
- }
- protoAddrParts := strings.SplitN(url, "://", 2)
- if len(protoAddrParts) != 2 {
- return nil, fmt.Errorf("bad format, expected tcp://ADDR")
- }
- if strings.ToLower(protoAddrParts[0]) != "tcp" {
- return nil, fmt.Errorf("dnet currently only supports tcp transport")
- }
- return &dnetConnection{protoAddrParts[0], protoAddrParts[1]}, nil
- }
- func (d *dnetConnection) httpCall(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, http.Header, int, error) {
- var in io.Reader
- in, err := encodeData(data)
- if err != nil {
- return nil, nil, -1, err
- }
- req, err := http.NewRequest(method, fmt.Sprintf("%s", path), in)
- if err != nil {
- return nil, nil, -1, err
- }
- setupRequestHeaders(method, data, req, headers)
- req.URL.Host = d.addr
- req.URL.Scheme = "http"
- httpClient := &http.Client{}
- resp, err := httpClient.Do(req)
- statusCode := -1
- if resp != nil {
- statusCode = resp.StatusCode
- }
- if err != nil {
- return nil, nil, statusCode, fmt.Errorf("error when trying to connect: %v", err)
- }
- if statusCode < 200 || statusCode >= 400 {
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, nil, statusCode, err
- }
- return nil, nil, statusCode, fmt.Errorf("error : %s", bytes.TrimSpace(body))
- }
- return resp.Body, resp.Header, statusCode, nil
- }
- func setupRequestHeaders(method string, data interface{}, req *http.Request, headers map[string][]string) {
- if data != nil {
- if headers == nil {
- headers = make(map[string][]string)
- }
- headers["Content-Type"] = []string{"application/json"}
- }
- expectedPayload := (method == "POST" || method == "PUT")
- if expectedPayload && req.Header.Get("Content-Type") == "" {
- req.Header.Set("Content-Type", "text/plain")
- }
- if headers != nil {
- for k, v := range headers {
- req.Header[k] = v
- }
- }
- }
- func encodeData(data interface{}) (*bytes.Buffer, error) {
- params := bytes.NewBuffer(nil)
- if data != nil {
- if err := json.NewEncoder(params).Encode(data); err != nil {
- return nil, err
- }
- }
- return params, nil
- }
|