mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
9f3d1ce3ff
The configuration format for docker runtime is based on daemon flags and hence adjusting the libnetwork configuration to accomodate it by moving the TOML based configuration to the dnet tool. Also changed the controller configuration via options Signed-off-by: Madhu Venugopal <madhu@docker.com>
253 lines
6.2 KiB
Go
253 lines
6.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
|
|
flag "github.com/docker/docker/pkg/mflag"
|
|
"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/client"
|
|
"github.com/docker/libnetwork/config"
|
|
"github.com/gorilla/mux"
|
|
)
|
|
|
|
const (
|
|
// DefaultHTTPHost is used if only port is provided to -H flag e.g. docker -d -H tcp://:8080
|
|
DefaultHTTPHost = "127.0.0.1"
|
|
// 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"
|
|
)
|
|
|
|
func main() {
|
|
if reexec.Init() {
|
|
return
|
|
}
|
|
|
|
_, stdout, stderr := term.StdStreams()
|
|
logrus.SetOutput(stderr)
|
|
|
|
err := dnetCommand(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
|
|
}
|
|
if strings.TrimSpace(cfg.Daemon.DefaultNetwork) != "" {
|
|
options = append(options, config.OptionDefaultNetwork(cfg.Daemon.DefaultNetwork))
|
|
}
|
|
if strings.TrimSpace(cfg.Daemon.DefaultDriver) != "" {
|
|
options = append(options, config.OptionDefaultDriver(cfg.Daemon.DefaultDriver))
|
|
}
|
|
if strings.TrimSpace(cfg.Datastore.Client.Provider) != "" {
|
|
options = append(options, config.OptionKVProvider(cfg.Datastore.Client.Provider))
|
|
}
|
|
if strings.TrimSpace(cfg.Datastore.Client.Address) != "" {
|
|
options = append(options, config.OptionKVProviderURL(cfg.Datastore.Client.Address))
|
|
}
|
|
return options
|
|
}
|
|
|
|
func dnetCommand(stdout, stderr io.Writer) error {
|
|
flag.Parse()
|
|
|
|
if *flHelp {
|
|
flag.Usage()
|
|
return nil
|
|
}
|
|
|
|
if *flLogLevel != "" {
|
|
lvl, err := logrus.ParseLevel(*flLogLevel)
|
|
if err != nil {
|
|
fmt.Fprintf(stderr, "Unable to parse logging level: %s\n", *flLogLevel)
|
|
return err
|
|
}
|
|
logrus.SetLevel(lvl)
|
|
} else {
|
|
logrus.SetLevel(logrus.InfoLevel)
|
|
}
|
|
|
|
if *flDebug {
|
|
logrus.SetLevel(logrus.DebugLevel)
|
|
}
|
|
|
|
if *flHost == "" {
|
|
defaultHost := os.Getenv("DNET_HOST")
|
|
if defaultHost == "" {
|
|
// TODO : Add UDS support
|
|
defaultHost = fmt.Sprintf("tcp://%s:%d", DefaultHTTPHost, DefaultHTTPPort)
|
|
}
|
|
*flHost = defaultHost
|
|
}
|
|
|
|
dc, err := newDnetConnection(*flHost)
|
|
if err != nil {
|
|
if *flDaemon {
|
|
logrus.Error(err)
|
|
} else {
|
|
fmt.Fprint(stderr, err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
if *flDaemon {
|
|
err := dc.dnetDaemon()
|
|
if err != nil {
|
|
logrus.Errorf("dnet Daemon exited with an error : %v", err)
|
|
}
|
|
return err
|
|
}
|
|
|
|
cli := client.NewNetworkCli(stdout, stderr, dc.httpCall)
|
|
if err := cli.Cmd("dnet", flag.Args()...); err != nil {
|
|
fmt.Fprintln(stderr, err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type dnetConnection struct {
|
|
// proto holds the client protocol i.e. unix.
|
|
proto string
|
|
// addr holds the client address.
|
|
addr string
|
|
}
|
|
|
|
func (d *dnetConnection) dnetDaemon() error {
|
|
cfg, err := parseConfig(*flCfgFile)
|
|
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
|
|
}
|
|
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)
|
|
return http.ListenAndServe(d.addr, r)
|
|
}
|
|
|
|
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, int, error) {
|
|
var in io.Reader
|
|
in, err := encodeData(data)
|
|
if err != nil {
|
|
return nil, -1, err
|
|
}
|
|
|
|
req, err := http.NewRequest(method, fmt.Sprintf("%s", path), in)
|
|
if err != nil {
|
|
return 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, 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, statusCode, err
|
|
}
|
|
return nil, statusCode, fmt.Errorf("error : %s", bytes.TrimSpace(body))
|
|
}
|
|
|
|
return resp.Body, 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
|
|
}
|