github.com/uriddle/docker@v0.0.0-20210926094723-4072e6aeb013/docker/daemon.go (about) 1 // +build daemon 2 3 package main 4 5 import ( 6 "crypto/tls" 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "strings" 12 "time" 13 14 "github.com/Sirupsen/logrus" 15 "github.com/docker/distribution/uuid" 16 apiserver "github.com/docker/docker/api/server" 17 "github.com/docker/docker/cli" 18 "github.com/docker/docker/cliconfig" 19 "github.com/docker/docker/daemon" 20 "github.com/docker/docker/daemon/logger" 21 "github.com/docker/docker/dockerversion" 22 "github.com/docker/docker/opts" 23 "github.com/docker/docker/pkg/jsonlog" 24 flag "github.com/docker/docker/pkg/mflag" 25 "github.com/docker/docker/pkg/pidfile" 26 "github.com/docker/docker/pkg/signal" 27 "github.com/docker/docker/pkg/system" 28 "github.com/docker/docker/registry" 29 "github.com/docker/docker/utils" 30 "github.com/docker/go-connections/tlsconfig" 31 ) 32 33 const ( 34 daemonUsage = " docker daemon [ --help | ... ]\n" 35 daemonConfigFileFlag = "-config-file" 36 ) 37 38 var ( 39 daemonCli cli.Handler = NewDaemonCli() 40 ) 41 42 // DaemonCli represents the daemon CLI. 43 type DaemonCli struct { 44 *daemon.Config 45 registryOptions *registry.Options 46 flags *flag.FlagSet 47 } 48 49 func presentInHelp(usage string) string { return usage } 50 func absentFromHelp(string) string { return "" } 51 52 // NewDaemonCli returns a pre-configured daemon CLI 53 func NewDaemonCli() *DaemonCli { 54 daemonFlags := cli.Subcmd("daemon", nil, "Enable daemon mode", true) 55 56 // TODO(tiborvass): remove InstallFlags? 57 daemonConfig := new(daemon.Config) 58 daemonConfig.LogConfig.Config = make(map[string]string) 59 daemonConfig.ClusterOpts = make(map[string]string) 60 61 daemonConfig.InstallFlags(daemonFlags, presentInHelp) 62 daemonConfig.InstallFlags(flag.CommandLine, absentFromHelp) 63 registryOptions := new(registry.Options) 64 registryOptions.InstallFlags(daemonFlags, presentInHelp) 65 registryOptions.InstallFlags(flag.CommandLine, absentFromHelp) 66 daemonFlags.Require(flag.Exact, 0) 67 68 return &DaemonCli{ 69 Config: daemonConfig, 70 registryOptions: registryOptions, 71 flags: daemonFlags, 72 } 73 } 74 75 func migrateKey() (err error) { 76 // Migrate trust key if exists at ~/.docker/key.json and owned by current user 77 oldPath := filepath.Join(cliconfig.ConfigDir(), defaultTrustKeyFile) 78 newPath := filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) 79 if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { 80 defer func() { 81 // Ensure old path is removed if no error occurred 82 if err == nil { 83 err = os.Remove(oldPath) 84 } else { 85 logrus.Warnf("Key migration failed, key file not removed at %s", oldPath) 86 os.Remove(newPath) 87 } 88 }() 89 90 if err := system.MkdirAll(getDaemonConfDir(), os.FileMode(0644)); err != nil { 91 return fmt.Errorf("Unable to create daemon configuration directory: %s", err) 92 } 93 94 newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) 95 if err != nil { 96 return fmt.Errorf("error creating key file %q: %s", newPath, err) 97 } 98 defer newFile.Close() 99 100 oldFile, err := os.Open(oldPath) 101 if err != nil { 102 return fmt.Errorf("error opening key file %q: %s", oldPath, err) 103 } 104 defer oldFile.Close() 105 106 if _, err := io.Copy(newFile, oldFile); err != nil { 107 return fmt.Errorf("error copying key: %s", err) 108 } 109 110 logrus.Infof("Migrated key from %s to %s", oldPath, newPath) 111 } 112 113 return nil 114 } 115 116 func getGlobalFlag() (globalFlag *flag.Flag) { 117 defer func() { 118 if x := recover(); x != nil { 119 switch f := x.(type) { 120 case *flag.Flag: 121 globalFlag = f 122 default: 123 panic(x) 124 } 125 } 126 }() 127 visitor := func(f *flag.Flag) { panic(f) } 128 commonFlags.FlagSet.Visit(visitor) 129 clientFlags.FlagSet.Visit(visitor) 130 return 131 } 132 133 // CmdDaemon is the daemon command, called the raw arguments after `docker daemon`. 134 func (cli *DaemonCli) CmdDaemon(args ...string) error { 135 // warn from uuid package when running the daemon 136 uuid.Loggerf = logrus.Warnf 137 138 if !commonFlags.FlagSet.IsEmpty() || !clientFlags.FlagSet.IsEmpty() { 139 // deny `docker -D daemon` 140 illegalFlag := getGlobalFlag() 141 fmt.Fprintf(os.Stderr, "invalid flag '-%s'.\nSee 'docker daemon --help'.\n", illegalFlag.Names[0]) 142 os.Exit(1) 143 } else { 144 // allow new form `docker daemon -D` 145 flag.Merge(cli.flags, commonFlags.FlagSet) 146 } 147 148 configFile := cli.flags.String([]string{daemonConfigFileFlag}, defaultDaemonConfigFile, "Daemon configuration file") 149 150 cli.flags.ParseFlags(args, true) 151 commonFlags.PostParse() 152 153 if commonFlags.TrustKey == "" { 154 commonFlags.TrustKey = filepath.Join(getDaemonConfDir(), defaultTrustKeyFile) 155 } 156 cliConfig, err := loadDaemonCliConfig(cli.Config, cli.flags, commonFlags, *configFile) 157 if err != nil { 158 fmt.Fprint(os.Stderr, err) 159 os.Exit(1) 160 } 161 cli.Config = cliConfig 162 163 if cli.Config.Debug { 164 utils.EnableDebug() 165 } 166 167 if utils.ExperimentalBuild() { 168 logrus.Warn("Running experimental build") 169 } 170 171 logrus.SetFormatter(&logrus.TextFormatter{TimestampFormat: jsonlog.RFC3339NanoFixed}) 172 173 if err := setDefaultUmask(); err != nil { 174 logrus.Fatalf("Failed to set umask: %v", err) 175 } 176 177 if len(cli.LogConfig.Config) > 0 { 178 if err := logger.ValidateLogOpts(cli.LogConfig.Type, cli.LogConfig.Config); err != nil { 179 logrus.Fatalf("Failed to set log opts: %v", err) 180 } 181 } 182 183 var pfile *pidfile.PIDFile 184 if cli.Pidfile != "" { 185 pf, err := pidfile.New(cli.Pidfile) 186 if err != nil { 187 logrus.Fatalf("Error starting daemon: %v", err) 188 } 189 pfile = pf 190 defer func() { 191 if err := pfile.Remove(); err != nil { 192 logrus.Error(err) 193 } 194 }() 195 } 196 197 serverConfig := &apiserver.Config{ 198 AuthorizationPluginNames: cli.Config.AuthorizationPlugins, 199 Logging: true, 200 Version: dockerversion.Version, 201 } 202 serverConfig = setPlatformServerConfig(serverConfig, cli.Config) 203 204 defaultHost := opts.DefaultHost 205 if cli.Config.TLS { 206 tlsOptions := tlsconfig.Options{ 207 CAFile: cli.Config.CommonTLSOptions.CAFile, 208 CertFile: cli.Config.CommonTLSOptions.CertFile, 209 KeyFile: cli.Config.CommonTLSOptions.KeyFile, 210 } 211 212 if cli.Config.TLSVerify { 213 // server requires and verifies client's certificate 214 tlsOptions.ClientAuth = tls.RequireAndVerifyClientCert 215 } 216 tlsConfig, err := tlsconfig.Server(tlsOptions) 217 if err != nil { 218 logrus.Fatal(err) 219 } 220 serverConfig.TLSConfig = tlsConfig 221 defaultHost = opts.DefaultTLSHost 222 } 223 224 if len(cli.Config.Hosts) == 0 { 225 cli.Config.Hosts = make([]string, 1) 226 } 227 for i := 0; i < len(cli.Config.Hosts); i++ { 228 var err error 229 if cli.Config.Hosts[i], err = opts.ParseHost(defaultHost, cli.Config.Hosts[i]); err != nil { 230 logrus.Fatalf("error parsing -H %s : %v", cli.Config.Hosts[i], err) 231 } 232 233 protoAddr := cli.Config.Hosts[i] 234 protoAddrParts := strings.SplitN(protoAddr, "://", 2) 235 if len(protoAddrParts) != 2 { 236 logrus.Fatalf("bad format %s, expected PROTO://ADDR", protoAddr) 237 } 238 serverConfig.Addrs = append(serverConfig.Addrs, apiserver.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]}) 239 } 240 241 api, err := apiserver.New(serverConfig) 242 if err != nil { 243 logrus.Fatal(err) 244 } 245 246 if err := migrateKey(); err != nil { 247 logrus.Fatal(err) 248 } 249 cli.TrustKeyPath = commonFlags.TrustKey 250 251 registryService := registry.NewService(cli.registryOptions) 252 d, err := daemon.NewDaemon(cli.Config, registryService) 253 if err != nil { 254 if pfile != nil { 255 if err := pfile.Remove(); err != nil { 256 logrus.Error(err) 257 } 258 } 259 logrus.Fatalf("Error starting daemon: %v", err) 260 } 261 262 logrus.Info("Daemon has completed initialization") 263 264 logrus.WithFields(logrus.Fields{ 265 "version": dockerversion.Version, 266 "commit": dockerversion.GitCommit, 267 "execdriver": d.ExecutionDriver().Name(), 268 "graphdriver": d.GraphDriverName(), 269 }).Info("Docker daemon") 270 271 api.InitRouters(d) 272 273 reload := func(config *daemon.Config) { 274 if err := d.Reload(config); err != nil { 275 logrus.Errorf("Error reconfiguring the daemon: %v", err) 276 return 277 } 278 api.Reload(config) 279 } 280 281 setupConfigReloadTrap(*configFile, cli.flags, reload) 282 283 // The serve API routine never exits unless an error occurs 284 // We need to start it as a goroutine and wait on it so 285 // daemon doesn't exit 286 serveAPIWait := make(chan error) 287 go api.Wait(serveAPIWait) 288 289 signal.Trap(func() { 290 api.Close() 291 <-serveAPIWait 292 shutdownDaemon(d, 15) 293 if pfile != nil { 294 if err := pfile.Remove(); err != nil { 295 logrus.Error(err) 296 } 297 } 298 }) 299 300 // after the daemon is done setting up we can notify systemd api 301 notifySystem() 302 303 // Daemon is fully initialized and handling API traffic 304 // Wait for serve API to complete 305 errAPI := <-serveAPIWait 306 shutdownDaemon(d, 15) 307 if errAPI != nil { 308 if pfile != nil { 309 if err := pfile.Remove(); err != nil { 310 logrus.Error(err) 311 } 312 } 313 logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI) 314 } 315 return nil 316 } 317 318 // shutdownDaemon just wraps daemon.Shutdown() to handle a timeout in case 319 // d.Shutdown() is waiting too long to kill container or worst it's 320 // blocked there 321 func shutdownDaemon(d *daemon.Daemon, timeout time.Duration) { 322 ch := make(chan struct{}) 323 go func() { 324 d.Shutdown() 325 close(ch) 326 }() 327 select { 328 case <-ch: 329 logrus.Debug("Clean shutdown succeeded") 330 case <-time.After(timeout * time.Second): 331 logrus.Error("Force shutdown daemon") 332 } 333 } 334 335 func loadDaemonCliConfig(config *daemon.Config, daemonFlags *flag.FlagSet, commonConfig *cli.CommonFlags, configFile string) (*daemon.Config, error) { 336 config.Debug = commonConfig.Debug 337 config.Hosts = commonConfig.Hosts 338 config.LogLevel = commonConfig.LogLevel 339 config.TLS = commonConfig.TLS 340 config.TLSVerify = commonConfig.TLSVerify 341 config.CommonTLSOptions = daemon.CommonTLSOptions{} 342 343 if commonConfig.TLSOptions != nil { 344 config.CommonTLSOptions.CAFile = commonConfig.TLSOptions.CAFile 345 config.CommonTLSOptions.CertFile = commonConfig.TLSOptions.CertFile 346 config.CommonTLSOptions.KeyFile = commonConfig.TLSOptions.KeyFile 347 } 348 349 if configFile != "" { 350 c, err := daemon.MergeDaemonConfigurations(config, daemonFlags, configFile) 351 if err != nil { 352 if daemonFlags.IsSet(daemonConfigFileFlag) || !os.IsNotExist(err) { 353 return nil, fmt.Errorf("unable to configure the Docker daemon with file %s: %v\n", configFile, err) 354 } 355 } 356 // the merged configuration can be nil if the config file didn't exist. 357 // leave the current configuration as it is if when that happens. 358 if c != nil { 359 config = c 360 } 361 } 362 363 // Regardless of whether the user sets it to true or false, if they 364 // specify TLSVerify at all then we need to turn on TLS 365 if config.IsValueSet(tlsVerifyKey) { 366 config.TLS = true 367 } 368 369 // ensure that the log level is the one set after merging configurations 370 setDaemonLogLevel(config.LogLevel) 371 372 return config, nil 373 }