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  }