reload.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. package daemon // import "github.com/docker/docker/daemon"
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "strconv"
  7. "github.com/containerd/log"
  8. "github.com/docker/docker/api/types/events"
  9. "github.com/hashicorp/go-multierror"
  10. "github.com/mitchellh/copystructure"
  11. "github.com/docker/docker/daemon/config"
  12. )
  13. // reloadTxn is used to defer side effects of a config reload.
  14. type reloadTxn struct {
  15. onCommit, onRollback []func() error
  16. }
  17. // OnCommit defers a function to be called when a config reload is being finalized.
  18. // The error returned from cb is purely informational.
  19. func (tx *reloadTxn) OnCommit(cb func() error) {
  20. tx.onCommit = append(tx.onCommit, cb)
  21. }
  22. // OnRollback defers a function to be called when a config reload is aborted.
  23. // The error returned from cb is purely informational.
  24. func (tx *reloadTxn) OnRollback(cb func() error) {
  25. tx.onCommit = append(tx.onRollback, cb)
  26. }
  27. func (tx *reloadTxn) run(cbs []func() error) error {
  28. tx.onCommit = nil
  29. tx.onRollback = nil
  30. var res *multierror.Error
  31. for _, cb := range cbs {
  32. res = multierror.Append(res, cb())
  33. }
  34. return res.ErrorOrNil()
  35. }
  36. // Commit calls all functions registered with OnCommit.
  37. // Any errors returned by the functions are collated into a
  38. // *github.com/hashicorp/go-multierror.Error value.
  39. func (tx *reloadTxn) Commit() error {
  40. return tx.run(tx.onCommit)
  41. }
  42. // Rollback calls all functions registered with OnRollback.
  43. // Any errors returned by the functions are collated into a
  44. // *github.com/hashicorp/go-multierror.Error value.
  45. func (tx *reloadTxn) Rollback() error {
  46. return tx.run(tx.onRollback)
  47. }
  48. // Reload modifies the live daemon configuration from conf.
  49. // conf is assumed to be a validated configuration.
  50. //
  51. // These are the settings that Reload changes:
  52. // - Platform runtime
  53. // - Daemon debug log level
  54. // - Daemon max concurrent downloads
  55. // - Daemon max concurrent uploads
  56. // - Daemon max download attempts
  57. // - Daemon shutdown timeout (in seconds)
  58. // - Cluster discovery (reconfigure and restart)
  59. // - Daemon labels
  60. // - Insecure registries
  61. // - Registry mirrors
  62. // - Daemon live restore
  63. func (daemon *Daemon) Reload(conf *config.Config) error {
  64. daemon.configReload.Lock()
  65. defer daemon.configReload.Unlock()
  66. copied, err := copystructure.Copy(daemon.config().Config)
  67. if err != nil {
  68. return err
  69. }
  70. newCfg := &configStore{
  71. Config: copied.(config.Config),
  72. }
  73. attributes := map[string]string{}
  74. // Ideally reloading should be transactional: the reload either completes
  75. // successfully, or the daemon config and state are left untouched. We use a
  76. // two-phase commit protocol to achieve this. Any fallible reload operation is
  77. // split into two phases. The first phase performs all the fallible operations
  78. // and mutates the newCfg copy. The second phase atomically swaps newCfg into
  79. // the live daemon configuration and executes any commit functions the first
  80. // phase registered to apply the side effects. If any first-phase returns an
  81. // error, the reload transaction is rolled back by discarding newCfg and
  82. // executing any registered rollback functions.
  83. var txn reloadTxn
  84. for _, reload := range []func(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error{
  85. daemon.reloadPlatform,
  86. daemon.reloadDebug,
  87. daemon.reloadMaxConcurrentDownloadsAndUploads,
  88. daemon.reloadMaxDownloadAttempts,
  89. daemon.reloadShutdownTimeout,
  90. daemon.reloadFeatures,
  91. daemon.reloadLabels,
  92. daemon.reloadRegistryConfig,
  93. daemon.reloadLiveRestore,
  94. daemon.reloadNetworkDiagnosticPort,
  95. } {
  96. if err := reload(&txn, newCfg, conf, attributes); err != nil {
  97. if rollbackErr := txn.Rollback(); rollbackErr != nil {
  98. return multierror.Append(nil, err, rollbackErr)
  99. }
  100. return err
  101. }
  102. }
  103. jsonString, _ := json.Marshal(&struct {
  104. *config.Config
  105. config.Proxies `json:"proxies"`
  106. }{
  107. Config: &newCfg.Config,
  108. Proxies: config.Proxies{
  109. HTTPProxy: config.MaskCredentials(newCfg.HTTPProxy),
  110. HTTPSProxy: config.MaskCredentials(newCfg.HTTPSProxy),
  111. NoProxy: config.MaskCredentials(newCfg.NoProxy),
  112. },
  113. })
  114. log.G(context.TODO()).Infof("Reloaded configuration: %s", jsonString)
  115. daemon.configStore.Store(newCfg)
  116. daemon.LogDaemonEventWithAttributes(events.ActionReload, attributes)
  117. return txn.Commit()
  118. }
  119. func marshalAttributeSlice(v []string) string {
  120. if v == nil {
  121. return "[]"
  122. }
  123. b, err := json.Marshal(v)
  124. if err != nil {
  125. panic(err) // Should never happen as the input type is fixed.
  126. }
  127. return string(b)
  128. }
  129. // reloadDebug updates configuration with Debug option
  130. // and updates the passed attributes
  131. func (daemon *Daemon) reloadDebug(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
  132. // update corresponding configuration
  133. if conf.IsValueSet("debug") {
  134. newCfg.Debug = conf.Debug
  135. }
  136. // prepare reload event attributes with updatable configurations
  137. attributes["debug"] = strconv.FormatBool(newCfg.Debug)
  138. return nil
  139. }
  140. // reloadMaxConcurrentDownloadsAndUploads updates configuration with max concurrent
  141. // download and upload options and updates the passed attributes
  142. func (daemon *Daemon) reloadMaxConcurrentDownloadsAndUploads(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
  143. // We always "reset" as the cost is lightweight and easy to maintain.
  144. newCfg.MaxConcurrentDownloads = config.DefaultMaxConcurrentDownloads
  145. newCfg.MaxConcurrentUploads = config.DefaultMaxConcurrentUploads
  146. if conf.IsValueSet("max-concurrent-downloads") && conf.MaxConcurrentDownloads != 0 {
  147. newCfg.MaxConcurrentDownloads = conf.MaxConcurrentDownloads
  148. }
  149. if conf.IsValueSet("max-concurrent-uploads") && conf.MaxConcurrentUploads != 0 {
  150. newCfg.MaxConcurrentUploads = conf.MaxConcurrentUploads
  151. }
  152. txn.OnCommit(func() error {
  153. if daemon.imageService != nil {
  154. daemon.imageService.UpdateConfig(
  155. newCfg.MaxConcurrentDownloads,
  156. newCfg.MaxConcurrentUploads,
  157. )
  158. }
  159. return nil
  160. })
  161. // prepare reload event attributes with updatable configurations
  162. attributes["max-concurrent-downloads"] = strconv.Itoa(newCfg.MaxConcurrentDownloads)
  163. attributes["max-concurrent-uploads"] = strconv.Itoa(newCfg.MaxConcurrentUploads)
  164. log.G(context.TODO()).Debug("Reset Max Concurrent Downloads: ", attributes["max-concurrent-downloads"])
  165. log.G(context.TODO()).Debug("Reset Max Concurrent Uploads: ", attributes["max-concurrent-uploads"])
  166. return nil
  167. }
  168. // reloadMaxDownloadAttempts updates configuration with max concurrent
  169. // download attempts when a connection is lost and updates the passed attributes
  170. func (daemon *Daemon) reloadMaxDownloadAttempts(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
  171. // We always "reset" as the cost is lightweight and easy to maintain.
  172. newCfg.MaxDownloadAttempts = config.DefaultDownloadAttempts
  173. if conf.IsValueSet("max-download-attempts") && conf.MaxDownloadAttempts != 0 {
  174. newCfg.MaxDownloadAttempts = conf.MaxDownloadAttempts
  175. }
  176. // prepare reload event attributes with updatable configurations
  177. attributes["max-download-attempts"] = strconv.Itoa(newCfg.MaxDownloadAttempts)
  178. log.G(context.TODO()).Debug("Reset Max Download Attempts: ", attributes["max-download-attempts"])
  179. return nil
  180. }
  181. // reloadShutdownTimeout updates configuration with daemon shutdown timeout option
  182. // and updates the passed attributes
  183. func (daemon *Daemon) reloadShutdownTimeout(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
  184. // update corresponding configuration
  185. if conf.IsValueSet("shutdown-timeout") {
  186. newCfg.ShutdownTimeout = conf.ShutdownTimeout
  187. log.G(context.TODO()).Debugf("Reset Shutdown Timeout: %d", newCfg.ShutdownTimeout)
  188. }
  189. // prepare reload event attributes with updatable configurations
  190. attributes["shutdown-timeout"] = strconv.Itoa(newCfg.ShutdownTimeout)
  191. return nil
  192. }
  193. // reloadLabels updates configuration with engine labels
  194. // and updates the passed attributes
  195. func (daemon *Daemon) reloadLabels(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
  196. // update corresponding configuration
  197. if conf.IsValueSet("labels") {
  198. newCfg.Labels = conf.Labels
  199. }
  200. // prepare reload event attributes with updatable configurations
  201. attributes["labels"] = marshalAttributeSlice(newCfg.Labels)
  202. return nil
  203. }
  204. // reloadRegistryConfig updates the configuration with registry options
  205. // and updates the passed attributes.
  206. func (daemon *Daemon) reloadRegistryConfig(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
  207. // Update corresponding configuration.
  208. if conf.IsValueSet("allow-nondistributable-artifacts") {
  209. newCfg.ServiceOptions.AllowNondistributableArtifacts = conf.AllowNondistributableArtifacts
  210. }
  211. if conf.IsValueSet("insecure-registries") {
  212. newCfg.ServiceOptions.InsecureRegistries = conf.InsecureRegistries
  213. }
  214. if conf.IsValueSet("registry-mirrors") {
  215. newCfg.ServiceOptions.Mirrors = conf.Mirrors
  216. }
  217. commit, err := daemon.registryService.ReplaceConfig(newCfg.ServiceOptions)
  218. if err != nil {
  219. return err
  220. }
  221. txn.OnCommit(func() error { commit(); return nil })
  222. attributes["allow-nondistributable-artifacts"] = marshalAttributeSlice(newCfg.ServiceOptions.AllowNondistributableArtifacts)
  223. attributes["insecure-registries"] = marshalAttributeSlice(newCfg.ServiceOptions.InsecureRegistries)
  224. attributes["registry-mirrors"] = marshalAttributeSlice(newCfg.ServiceOptions.Mirrors)
  225. return nil
  226. }
  227. // reloadLiveRestore updates configuration with live restore option
  228. // and updates the passed attributes
  229. func (daemon *Daemon) reloadLiveRestore(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
  230. // update corresponding configuration
  231. if conf.IsValueSet("live-restore") {
  232. newCfg.LiveRestoreEnabled = conf.LiveRestoreEnabled
  233. }
  234. // prepare reload event attributes with updatable configurations
  235. attributes["live-restore"] = strconv.FormatBool(newCfg.LiveRestoreEnabled)
  236. return nil
  237. }
  238. // reloadNetworkDiagnosticPort updates the network controller starting the diagnostic if the config is valid
  239. func (daemon *Daemon) reloadNetworkDiagnosticPort(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
  240. txn.OnCommit(func() error {
  241. if conf == nil || daemon.netController == nil || !conf.IsValueSet("network-diagnostic-port") ||
  242. conf.NetworkDiagnosticPort < 1 || conf.NetworkDiagnosticPort > 65535 {
  243. // If there is no config make sure that the diagnostic is off
  244. if daemon.netController != nil {
  245. daemon.netController.StopDiagnostic()
  246. }
  247. return nil
  248. }
  249. // Enable the network diagnostic if the flag is set with a valid port within the range
  250. log.G(context.TODO()).WithFields(log.Fields{"port": conf.NetworkDiagnosticPort, "ip": "127.0.0.1"}).Warn("Starting network diagnostic server")
  251. daemon.netController.StartDiagnostic(conf.NetworkDiagnosticPort)
  252. return nil
  253. })
  254. return nil
  255. }
  256. // reloadFeatures updates configuration with enabled/disabled features
  257. func (daemon *Daemon) reloadFeatures(txn *reloadTxn, newCfg *configStore, conf *config.Config, attributes map[string]string) error {
  258. // update corresponding configuration
  259. // note that we allow features option to be entirely unset
  260. newCfg.Features = conf.Features
  261. // prepare reload event attributes with updatable configurations
  262. attributes["features"] = fmt.Sprintf("%v", newCfg.Features)
  263. return nil
  264. }