11package daemon
22
33import (
4+ "bytes"
5+ "encoding/json"
6+ "fmt"
7+ "io"
8+ "io/ioutil"
9+ "strings"
10+ "sync"
11+
12+ "github.com/Sirupsen/logrus"
413 "github.com/docker/docker/opts"
14+ "github.com/docker/docker/pkg/discovery"
515 flag "github.com/docker/docker/pkg/mflag"
6- "github.com/docker/engine-api/types/container "
16+ "github.com/imdario/mergo "
717)
818
919const (
1020 defaultNetworkMtu = 1500
1121 disableNetworkBridge = "none"
1222)
1323
24+ // LogConfig represents the default log configuration.
25+ // It includes json tags to deserialize configuration from a file
26+ // using the same names that the flags in the command line uses.
27+ type LogConfig struct {
28+ Type string `json:"log-driver,omitempty"`
29+ Config map [string ]string `json:"log-opts,omitempty"`
30+ }
31+
32+ // CommonTLSOptions defines TLS configuration for the daemon server.
33+ // It includes json tags to deserialize configuration from a file
34+ // using the same names that the flags in the command line uses.
35+ type CommonTLSOptions struct {
36+ CAFile string `json:"tlscacert,omitempty"`
37+ CertFile string `json:"tlscert,omitempty"`
38+ KeyFile string `json:"tlskey,omitempty"`
39+ }
40+
1441// CommonConfig defines the configuration of a docker daemon which are
1542// common across platforms.
43+ // It includes json tags to deserialize configuration from a file
44+ // using the same names that the flags in the command line uses.
1645type CommonConfig struct {
17- AuthorizationPlugins []string // AuthorizationPlugins holds list of authorization plugins
18- AutoRestart bool
19- Bridge bridgeConfig // Bridge holds bridge network specific configuration.
20- Context map [string ][]string
21- DisableBridge bool
22- DNS []string
23- DNSOptions []string
24- DNSSearch []string
25- ExecOptions []string
26- ExecRoot string
27- GraphDriver string
28- GraphOptions []string
29- Labels []string
30- LogConfig container.LogConfig
31- Mtu int
32- Pidfile string
33- RemappedRoot string
34- Root string
35- TrustKeyPath string
46+ AuthorizationPlugins []string `json:"authorization-plugins,omitempty"` // AuthorizationPlugins holds list of authorization plugins
47+ AutoRestart bool `json:"-"`
48+ Bridge bridgeConfig `json:"-"` // Bridge holds bridge network specific configuration.
49+ Context map [string ][]string `json:"-"`
50+ DisableBridge bool `json:"-"`
51+ DNS []string `json:"dns,omitempty"`
52+ DNSOptions []string `json:"dns-opts,omitempty"`
53+ DNSSearch []string `json:"dns-search,omitempty"`
54+ ExecOptions []string `json:"exec-opts,omitempty"`
55+ ExecRoot string `json:"exec-root,omitempty"`
56+ GraphDriver string `json:"storage-driver,omitempty"`
57+ GraphOptions []string `json:"storage-opts,omitempty"`
58+ Labels []string `json:"labels,omitempty"`
59+ LogConfig LogConfig `json:"log-config,omitempty"`
60+ Mtu int `json:"mtu,omitempty"`
61+ Pidfile string `json:"pidfile,omitempty"`
62+ Root string `json:"graph,omitempty"`
63+ TrustKeyPath string `json:"-"`
3664
3765 // ClusterStore is the storage backend used for the cluster information. It is used by both
3866 // multihost networking (to store networks and endpoints information) and by the node discovery
3967 // mechanism.
40- ClusterStore string
68+ ClusterStore string `json:"cluster-store,omitempty"`
4169
4270 // ClusterOpts is used to pass options to the discovery package for tuning libkv settings, such
4371 // as TLS configuration settings.
44- ClusterOpts map [string ]string
72+ ClusterOpts map [string ]string `json:"cluster-store-opts,omitempty"`
4573
4674 // ClusterAdvertise is the network endpoint that the Engine advertises for the purpose of node
4775 // discovery. This should be a 'host:port' combination on which that daemon instance is
4876 // reachable by other hosts.
49- ClusterAdvertise string
77+ ClusterAdvertise string `json:"cluster-advertise,omitempty"`
78+
79+ Debug bool `json:"debug,omitempty"`
80+ Hosts []string `json:"hosts,omitempty"`
81+ LogLevel string `json:"log-level,omitempty"`
82+ TLS bool `json:"tls,omitempty"`
83+ TLSVerify bool `json:"tls-verify,omitempty"`
84+ TLSOptions CommonTLSOptions `json:"tls-opts,omitempty"`
85+
86+ reloadLock sync.Mutex
5087}
5188
5289// InstallCommonFlags adds command-line options to the top-level flag parser for
5390// the current process.
5491// Subsequent calls to `flag.Parse` will populate config with values parsed
5592// from the command-line.
5693func (config * Config ) InstallCommonFlags (cmd * flag.FlagSet , usageFn func (string ) string ) {
57- cmd .Var (opts .NewListOptsRef ( & config .GraphOptions , nil ), []string {"-storage-opt" }, usageFn ("Set storage driver options" ))
58- cmd .Var (opts .NewListOptsRef ( & config .AuthorizationPlugins , nil ), []string {"-authorization-plugin" }, usageFn ("List authorization plugins in order from first evaluator to last" ))
59- cmd .Var (opts .NewListOptsRef ( & config .ExecOptions , nil ), []string {"-exec-opt" }, usageFn ("Set exec driver options" ))
94+ cmd .Var (opts .NewNamedListOptsRef ( "storage-opts" , & config .GraphOptions , nil ), []string {"-storage-opt" }, usageFn ("Set storage driver options" ))
95+ cmd .Var (opts .NewNamedListOptsRef ( "authorization-plugins" , & config .AuthorizationPlugins , nil ), []string {"-authorization-plugin" }, usageFn ("List authorization plugins in order from first evaluator to last" ))
96+ cmd .Var (opts .NewNamedListOptsRef ( "exec-opts" , & config .ExecOptions , nil ), []string {"-exec-opt" }, usageFn ("Set exec driver options" ))
6097 cmd .StringVar (& config .Pidfile , []string {"p" , "-pidfile" }, defaultPidFile , usageFn ("Path to use for daemon PID file" ))
6198 cmd .StringVar (& config .Root , []string {"g" , "-graph" }, defaultGraph , usageFn ("Root of the Docker runtime" ))
6299 cmd .StringVar (& config .ExecRoot , []string {"-exec-root" }, "/var/run/docker" , usageFn ("Root of the Docker execdriver" ))
@@ -65,12 +102,131 @@ func (config *Config) InstallCommonFlags(cmd *flag.FlagSet, usageFn func(string)
65102 cmd .IntVar (& config .Mtu , []string {"#mtu" , "-mtu" }, 0 , usageFn ("Set the containers network MTU" ))
66103 // FIXME: why the inconsistency between "hosts" and "sockets"?
67104 cmd .Var (opts .NewListOptsRef (& config .DNS , opts .ValidateIPAddress ), []string {"#dns" , "-dns" }, usageFn ("DNS server to use" ))
68- cmd .Var (opts .NewListOptsRef ( & config .DNSOptions , nil ), []string {"-dns-opt" }, usageFn ("DNS options to use" ))
105+ cmd .Var (opts .NewNamedListOptsRef ( "dns-opts" , & config .DNSOptions , nil ), []string {"-dns-opt" }, usageFn ("DNS options to use" ))
69106 cmd .Var (opts .NewListOptsRef (& config .DNSSearch , opts .ValidateDNSSearch ), []string {"-dns-search" }, usageFn ("DNS search domains to use" ))
70- cmd .Var (opts .NewListOptsRef ( & config .Labels , opts .ValidateLabel ), []string {"-label" }, usageFn ("Set key=value labels to the daemon" ))
107+ cmd .Var (opts .NewNamedListOptsRef ( "labels" , & config .Labels , opts .ValidateLabel ), []string {"-label" }, usageFn ("Set key=value labels to the daemon" ))
71108 cmd .StringVar (& config .LogConfig .Type , []string {"-log-driver" }, "json-file" , usageFn ("Default driver for container logs" ))
72- cmd .Var (opts .NewMapOpts ( config .LogConfig .Config , nil ), []string {"-log-opt" }, usageFn ("Set log driver options" ))
109+ cmd .Var (opts .NewNamedMapOpts ( "log-opts" , config .LogConfig .Config , nil ), []string {"-log-opt" }, usageFn ("Set log driver options" ))
73110 cmd .StringVar (& config .ClusterAdvertise , []string {"-cluster-advertise" }, "" , usageFn ("Address or interface name to advertise" ))
74111 cmd .StringVar (& config .ClusterStore , []string {"-cluster-store" }, "" , usageFn ("Set the cluster store" ))
75- cmd .Var (opts .NewMapOpts (config .ClusterOpts , nil ), []string {"-cluster-store-opt" }, usageFn ("Set cluster store options" ))
112+ cmd .Var (opts .NewNamedMapOpts ("cluster-store-opts" , config .ClusterOpts , nil ), []string {"-cluster-store-opt" }, usageFn ("Set cluster store options" ))
113+ }
114+
115+ func parseClusterAdvertiseSettings (clusterStore , clusterAdvertise string ) (string , error ) {
116+ if clusterAdvertise == "" {
117+ return "" , errDiscoveryDisabled
118+ }
119+ if clusterStore == "" {
120+ return "" , fmt .Errorf ("invalid cluster configuration. --cluster-advertise must be accompanied by --cluster-store configuration" )
121+ }
122+
123+ advertise , err := discovery .ParseAdvertise (clusterAdvertise )
124+ if err != nil {
125+ return "" , fmt .Errorf ("discovery advertise parsing failed (%v)" , err )
126+ }
127+ return advertise , nil
128+ }
129+
130+ // ReloadConfiguration reads the configuration in the host and reloads the daemon and server.
131+ func ReloadConfiguration (configFile string , flags * flag.FlagSet , reload func (* Config )) {
132+ logrus .Infof ("Got signal to reload configuration, reloading from: %s" , configFile )
133+ newConfig , err := getConflictFreeConfiguration (configFile , flags )
134+ if err != nil {
135+ logrus .Error (err )
136+ } else {
137+ reload (newConfig )
138+ }
139+ }
140+
141+ // MergeDaemonConfigurations reads a configuration file,
142+ // loads the file configuration in an isolated structure,
143+ // and merges the configuration provided from flags on top
144+ // if there are no conflicts.
145+ func MergeDaemonConfigurations (flagsConfig * Config , flags * flag.FlagSet , configFile string ) (* Config , error ) {
146+ fileConfig , err := getConflictFreeConfiguration (configFile , flags )
147+ if err != nil {
148+ return nil , err
149+ }
150+
151+ // merge flags configuration on top of the file configuration
152+ if err := mergo .Merge (fileConfig , flagsConfig ); err != nil {
153+ return nil , err
154+ }
155+
156+ return fileConfig , nil
157+ }
158+
159+ // getConflictFreeConfiguration loads the configuration from a JSON file.
160+ // It compares that configuration with the one provided by the flags,
161+ // and returns an error if there are conflicts.
162+ func getConflictFreeConfiguration (configFile string , flags * flag.FlagSet ) (* Config , error ) {
163+ b , err := ioutil .ReadFile (configFile )
164+ if err != nil {
165+ return nil , err
166+ }
167+
168+ var reader io.Reader
169+ if flags != nil {
170+ var jsonConfig map [string ]interface {}
171+ reader = bytes .NewReader (b )
172+ if err := json .NewDecoder (reader ).Decode (& jsonConfig ); err != nil {
173+ return nil , err
174+ }
175+
176+ if err := findConfigurationConflicts (jsonConfig , flags ); err != nil {
177+ return nil , err
178+ }
179+ }
180+
181+ var config Config
182+ reader = bytes .NewReader (b )
183+ err = json .NewDecoder (reader ).Decode (& config )
184+ return & config , err
185+ }
186+
187+ // findConfigurationConflicts iterates over the provided flags searching for
188+ // duplicated configurations. It returns an error with all the conflicts if
189+ // it finds any.
190+ func findConfigurationConflicts (config map [string ]interface {}, flags * flag.FlagSet ) error {
191+ var conflicts []string
192+ flatten := make (map [string ]interface {})
193+ for k , v := range config {
194+ if m , ok := v .(map [string ]interface {}); ok {
195+ for km , vm := range m {
196+ flatten [km ] = vm
197+ }
198+ } else {
199+ flatten [k ] = v
200+ }
201+ }
202+
203+ printConflict := func (name string , flagValue , fileValue interface {}) string {
204+ return fmt .Sprintf ("%s: (from flag: %v, from file: %v)" , name , flagValue , fileValue )
205+ }
206+
207+ collectConflicts := func (f * flag.Flag ) {
208+ // search option name in the json configuration payload if the value is a named option
209+ if namedOption , ok := f .Value .(opts.NamedOption ); ok {
210+ if optsValue , ok := flatten [namedOption .Name ()]; ok {
211+ conflicts = append (conflicts , printConflict (namedOption .Name (), f .Value .String (), optsValue ))
212+ }
213+ } else {
214+ // search flag name in the json configuration payload without trailing dashes
215+ for _ , name := range f .Names {
216+ name = strings .TrimLeft (name , "-" )
217+
218+ if value , ok := flatten [name ]; ok {
219+ conflicts = append (conflicts , printConflict (name , f .Value .String (), value ))
220+ break
221+ }
222+ }
223+ }
224+ }
225+
226+ flags .Visit (collectConflicts )
227+
228+ if len (conflicts ) > 0 {
229+ return fmt .Errorf ("the following directives are specified both as a flag and in the configuration file: %s" , strings .Join (conflicts , ", " ))
230+ }
231+ return nil
76232}
0 commit comments