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