// Trap sets up a simplified signal "trap", appropriate for common // behavior expected from a vanilla unix command-line tool in general // (and the Docker engine in particular). // // * If SIGINT or SIGTERM are received, `cleanup` is called, then the process is terminated. // * If SIGINT or SIGTERM are repeated 3 times before cleanup is complete, then cleanup is // skipped and the process terminated directly. // * If "DEBUG" is set in the environment, SIGQUIT causes an exit without cleanup. // func Trap(cleanup func()) { c := make(chan os.Signal, 1) signals := []os.Signal{os.Interrupt, syscall.SIGTERM} if os.Getenv("DEBUG") == "" { signals = append(signals, syscall.SIGQUIT) } gosignal.Notify(c, signals...) go func() { interruptCount := uint32(0) for sig := range c { go func(sig os.Signal) { log.Infof("Received signal '%v', starting shutdown of docker...", sig) switch sig { case os.Interrupt, syscall.SIGTERM: // If the user really wants to interrupt, let him do so. if atomic.LoadUint32(&interruptCount) < 3 { atomic.AddUint32(&interruptCount, 1) // Initiate the cleanup only once if atomic.LoadUint32(&interruptCount) == 1 { // Call cleanup handler cleanup() os.Exit(0) } else { return } } else { log.Infof("Force shutdown of docker, interrupting cleanup") } case syscall.SIGQUIT: } os.Exit(128 + int(sig.(syscall.Signal))) }(sig) } }() }
// Make sure the config is compatible with the current kernel func (container *Container) verifyDaemonSettings() { if container.Config.Memory > 0 && !container.daemon.sysInfo.MemoryLimit { log.Infof("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.") container.Config.Memory = 0 } if container.Config.Memory > 0 && !container.daemon.sysInfo.SwapLimit { log.Infof("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.") container.Config.MemorySwap = -1 } if container.daemon.sysInfo.IPv4ForwardingDisabled { log.Infof("WARNING: IPv4 forwarding is disabled. Networking will not work") } }
// fetch retrieves updated base graphs. This function cannot error, it // should only log errors func (t *TrustStore) fetch() { t.Lock() defer t.Unlock() if t.autofetch && t.fetcher == nil { // Do nothing ?? return } fetchCount := 0 for bg, ep := range t.baseEndpoints { statement, err := t.fetchBaseGraph(ep) if err != nil { log.Infof("Trust graph fetch failed: %s", err) continue } b, err := statement.Bytes() if err != nil { log.Infof("Bad trust graph statement: %s", err) continue } // TODO check if value differs err = ioutil.WriteFile(path.Join(t.path, bg+".json"), b, 0600) if err != nil { log.Infof("Error writing trust graph statement: %s", err) } fetchCount++ } log.Debugf("Fetched %d base graphs at %s", fetchCount, time.Now()) if fetchCount > 0 { go func() { err := t.reload() if err != nil { // TODO log log.Infof("Reload of trust graph failed: %s", err) } }() t.fetchTime = defaultFetchtime t.fetcher = nil } else if t.autofetch { maxTime := 10 * defaultFetchtime t.fetchTime = time.Duration(1.5 * (float64)(t.fetchTime+time.Second)) if t.fetchTime > maxTime { t.fetchTime = maxTime } t.fetcher = time.AfterFunc(t.fetchTime, t.fetch) } }
func updateConfig(c *Container, subsystem string, value string) error { if subsystem == "cpuset.cpus" { c.Config.Cpuset = value } else if subsystem == "memory.limit_in_bytes" { parsedMemory, err := units.RAMInBytes(value) if err != nil { log.Errorf("Update memory.limit_in_bytes for container %s error %v", c.ID, err) return err } c.Config.Memory = parsedMemory } else if subsystem == "cpu.shares" { parsedCpu, err := strconv.ParseInt(value, 10, 64) if err != nil { log.Errorf("Update cpu.shares for container %s error %v", c.ID, err) return err } c.Config.CpuShares = parsedCpu } else if subsystem == "memory.memsw.limit_in_bytes" { parsedMemsw, err := units.RAMInBytes(value) if err != nil { log.Errorf("Update memory.memsw.limit_in_bytes for container %s error %v", c.ID, err) return err } c.Config.MemorySwap = parsedMemsw } else { log.Infof("Ignore config update container %s, subsystem %s ", c.ID, subsystem) } return nil }
// ServeApi loops through all of the protocols sent in to docker and spawns // off a go routine to setup a serving http.Server for each. func ServeApi(job *engine.Job) engine.Status { if len(job.Args) == 0 { return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name) } var ( protoAddrs = job.Args chErrors = make(chan error, len(protoAddrs)) ) activationLock = make(chan struct{}) for _, protoAddr := range protoAddrs { protoAddrParts := strings.SplitN(protoAddr, "://", 2) if len(protoAddrParts) != 2 { return job.Errorf("usage: %s PROTO://ADDR [PROTO://ADDR ...]", job.Name) } go func() { log.Infof("Listening for HTTP on %s (%s)", protoAddrParts[0], protoAddrParts[1]) chErrors <- ListenAndServe(protoAddrParts[0], protoAddrParts[1], job) }() } for i := 0; i < len(protoAddrs); i++ { err := <-chErrors if err != nil { return job.Error(err) } } return engine.StatusOK }
func (daemon *Daemon) shutdown() error { group := sync.WaitGroup{} log.Debugf("starting clean shutdown of all containers...") for _, container := range daemon.List() { c := container if c.IsRunning() { log.Debugf("stopping %s", c.ID) group.Add(1) go func() { defer group.Done() if err := c.KillSig(15); err != nil { log.Debugf("kill 15 error for %s - %s", c.ID, err) } if _, err := c.WaitStop(3 * time.Second); err != nil { log.Infof("Container %v failed to exit within %d seconds of SIGTERM - using the force", c.ID, 3) if err := c.Kill(); err != nil { c.WaitStop(-1 * time.Second) } } log.Debugf("container stopped %s", c.ID) }() } } group.Wait() return nil }
func makeHttpHandler(eng *engine.Engine, logging bool, localMethod string, localRoute string, handlerFunc HttpApiFunc, enableCors bool, dockerVersion version.Version) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { // log the request log.Debugf("Calling %s %s", localMethod, localRoute) if logging { log.Infof("%s %s", r.Method, r.RequestURI) } if strings.Contains(r.Header.Get("User-Agent"), "Docker-Client/") { userAgent := strings.Split(r.Header.Get("User-Agent"), "/") if len(userAgent) == 2 && !dockerVersion.Equal(version.Version(userAgent[1])) { log.Debugf("Warning: client and server don't have the same version (client: %s, server: %s)", userAgent[1], dockerVersion) } } version := version.Version(mux.Vars(r)["version"]) if version == "" { version = api.APIVERSION } if enableCors { writeCorsHeaders(w, r) } if version.GreaterThan(api.APIVERSION) { http.Error(w, fmt.Errorf("client and server don't have same version (client : %s, server: %s)", version, api.APIVERSION).Error(), http.StatusNotFound) return } if err := handlerFunc(eng, version, w, r, mux.Vars(r)); err != nil { log.Errorf("Handler for %s %s returned error: %s", localMethod, localRoute, err) httpError(w, err) } } }
func (daemon *Daemon) DeleteVolumes(volumeIDs map[string]struct{}) { for id := range volumeIDs { if err := daemon.volumes.Delete(id); err != nil { log.Infof("%s", err) continue } } }
func mainDaemon() { if flag.NArg() != 0 { flag.Usage() return } eng := engine.New() signal.Trap(eng.Shutdown) // Load builtins if err := builtins.Register(eng); err != nil { log.Fatal(err) } // load the daemon in the background so we can immediately start // the http api so that connections don't fail while the daemon // is booting go func() { d, err := daemon.NewDaemon(daemonCfg, eng) if err != nil { log.Fatal(err) } if err := d.Install(eng); err != nil { log.Fatal(err) } b := &builder.BuilderJob{eng, d} b.Install() // after the daemon is done setting up we can tell the api to start // accepting connections if err := eng.Job("acceptconnections").Run(); err != nil { log.Fatal(err) } }() // TODO actually have a resolved graphdriver to show? log.Infof("docker daemon: %s %s; execdriver: %s; graphdriver: %s", dockerversion.VERSION, dockerversion.GITCOMMIT, daemonCfg.ExecDriver, daemonCfg.GraphDriver, ) // Serve api job := eng.Job("serveapi", flHosts...) job.SetenvBool("Logging", true) job.SetenvBool("EnableCors", *flEnableCors) job.Setenv("Version", dockerversion.VERSION) job.Setenv("SocketGroup", *flSocketGroup) job.SetenvBool("Tls", *flTls) job.SetenvBool("TlsVerify", *flTlsVerify) job.Setenv("TlsCa", *flCa) job.Setenv("TlsCert", *flCert) job.Setenv("TlsKey", *flKey) job.SetenvBool("BufferRequests", true) if err := job.Run(); err != nil { log.Fatal(err) } }
func (ts *TarSum) Sum(extra []byte) string { var sums []string for _, sum := range ts.sums { sums = append(sums, sum) } sort.Strings(sums) h := sha256.New() if extra != nil { h.Write(extra) } for _, sum := range sums { log.Infof("-->%s<--", sum) h.Write([]byte(sum)) } checksum := "tarsum+sha256:" + hex.EncodeToString(h.Sum(nil)) log.Infof("checksum processed: %s", checksum) return checksum }
func (daemon *Daemon) checkLocaldns() error { resolvConf, err := resolvconf.Get() if err != nil { return err } if len(daemon.config.Dns) == 0 && utils.CheckLocalDns(resolvConf) { log.Infof("Local (127.0.0.1) DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v", DefaultDns) daemon.config.Dns = DefaultDns } return nil }
// release an interface for a select ip func Release(job *engine.Job) engine.Status { var ( id = job.Args[0] containerInterface = currentInterfaces.Get(id) ) if containerInterface == nil { return job.Errorf("No network information to release for %s", id) } for _, nat := range containerInterface.PortMappings { if err := portmapper.Unmap(nat); err != nil { log.Infof("Unable to unmap port %s: %s", nat, err) } } if err := ipallocator.ReleaseIP(bridgeNetwork, &containerInterface.IP); err != nil { log.Infof("Unable to release ip %s", err) } return engine.StatusOK }
func (daemon *Daemon) checkLocaldns() error { resolvConf, err := resolvconf.Get() if err != nil { return err } resolvConf = utils.RemoveLocalDns(resolvConf) if len(daemon.config.Dns) == 0 && !bytes.Contains(resolvConf, []byte("nameserver")) { log.Infof("No non localhost DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v", DefaultDns) daemon.config.Dns = DefaultDns } return nil }
func (container *Container) setupContainerDns() error { if container.ResolvConfPath != "" { return nil } var ( config = container.hostConfig daemon = container.daemon ) resolvConf, err := resolvconf.Get() if err != nil { return err } container.ResolvConfPath, err = container.getRootResourcePath("resolv.conf") if err != nil { return err } if config.NetworkMode != "host" { // check configurations for any container/daemon dns settings if len(config.Dns) > 0 || len(daemon.config.Dns) > 0 || len(config.DnsSearch) > 0 || len(daemon.config.DnsSearch) > 0 { var ( dns = resolvconf.GetNameservers(resolvConf) dnsSearch = resolvconf.GetSearchDomains(resolvConf) ) if len(config.Dns) > 0 { dns = config.Dns } else if len(daemon.config.Dns) > 0 { dns = daemon.config.Dns } if len(config.DnsSearch) > 0 { dnsSearch = config.DnsSearch } else if len(daemon.config.DnsSearch) > 0 { dnsSearch = daemon.config.DnsSearch } return resolvconf.Build(container.ResolvConfPath, dns, dnsSearch) } // replace any localhost/127.* nameservers resolvConf = utils.RemoveLocalDns(resolvConf) // if the resulting resolvConf is empty, use DefaultDns if !bytes.Contains(resolvConf, []byte("nameserver")) { log.Infof("No non localhost DNS resolver found in resolv.conf and containers can't use it. Using default external servers : %v", DefaultDns) // prefix the default dns options with nameserver resolvConf = append(resolvConf, []byte("\nnameserver "+strings.Join(DefaultDns, "\nnameserver "))...) } } return ioutil.WriteFile(container.ResolvConfPath, resolvConf, 0644) }
func checkKernelAndArch() error { // Check for unsupported architectures if runtime.GOARCH != "amd64" { return fmt.Errorf("The Docker runtime currently only supports amd64 (not %s). This will change in the future. Aborting.", runtime.GOARCH) } // Check for unsupported kernel versions // FIXME: it would be cleaner to not test for specific versions, but rather // test for specific functionalities. // Unfortunately we can't test for the feature "does not cause a kernel panic" // without actually causing a kernel panic, so we need this workaround until // the circumstances of pre-3.8 crashes are clearer. // For details see http://github.com/docker/docker/issues/407 if k, err := kernel.GetKernelVersion(); err != nil { log.Infof("WARNING: %s", err) } else { if kernel.CompareKernelVersion(k, &kernel.KernelVersionInfo{Kernel: 3, Major: 8, Minor: 0}) < 0 { if os.Getenv("DOCKER_NOWARN_KERNEL_VERSION") == "" { log.Infof("WARNING: You are running linux kernel version %s, which might be unstable running docker. Please upgrade your kernel to 3.8.0.", k.String()) } } } return nil }
func (container *Container) Stop(seconds int) error { if !container.State.IsRunning() { return nil } // 1. Send a SIGTERM if err := container.KillSig(15); err != nil { log.Infof("Failed to send SIGTERM to the process, force killing") if err := container.KillSig(9); err != nil { return err } } // 2. Wait for the process to exit on its own if _, err := container.State.WaitStop(time.Duration(seconds) * time.Second); err != nil { log.Infof("Container %v failed to exit within %d seconds of SIGTERM - using the force", container.ID, seconds) // 3. If it doesn't, then send SIGKILL if err := container.Kill(); err != nil { container.State.WaitStop(-1 * time.Second) return err } } return nil }
func (r *resumableRequestReader) Read(p []byte) (n int, err error) { if r.client == nil || r.request == nil { return 0, fmt.Errorf("client and request can't be nil\n") } isFreshRequest := false if r.lastRange != 0 && r.currentResponse == nil { readRange := fmt.Sprintf("bytes=%d-%d", r.lastRange, r.totalSize) r.request.Header.Set("Range", readRange) time.Sleep(5 * time.Second) } if r.currentResponse == nil { r.currentResponse, err = r.client.Do(r.request) isFreshRequest = true } if err != nil && r.failures+1 != r.maxFailures { r.cleanUpResponse() r.failures += 1 time.Sleep(5 * time.Duration(r.failures) * time.Second) return 0, nil } else if err != nil { r.cleanUpResponse() return 0, err } if r.currentResponse.StatusCode == 416 && r.lastRange == r.totalSize && r.currentResponse.ContentLength == 0 { r.cleanUpResponse() return 0, io.EOF } else if r.currentResponse.StatusCode != 206 && r.lastRange != 0 && isFreshRequest { r.cleanUpResponse() return 0, fmt.Errorf("the server doesn't support byte ranges") } if r.totalSize == 0 { r.totalSize = r.currentResponse.ContentLength } else if r.totalSize <= 0 { r.cleanUpResponse() return 0, fmt.Errorf("failed to auto detect content length") } n, err = r.currentResponse.Body.Read(p) r.lastRange += int64(n) if err != nil { r.cleanUpResponse() } if err != nil && err != io.EOF { log.Infof("encountered error during pull and clearing it before resume: %s", err) err = nil } return n, err }
func (container *Container) Kill() error { if !container.State.IsRunning() { return nil } // 1. Send SIGKILL if err := container.KillSig(9); err != nil { return err } // 2. Wait for the process to die, in last resort, try to kill the process directly if _, err := container.State.WaitStop(10 * time.Second); err != nil { // Ensure that we don't kill ourselves if pid := container.State.GetPid(); pid != 0 { log.Infof("Container %s failed to exit within 10 seconds of kill - trying direct SIGKILL", utils.TruncateID(container.ID)) if err := syscall.Kill(pid, 9); err != nil { return err } } } container.State.WaitStop(-1 * time.Second) return nil }
func main() { if reexec.Init() { return } flag.Parse() // FIXME: validate daemon flags here if *flVersion { showVersion() return } if *flDebug { os.Setenv("DEBUG", "1") } if len(flHosts) == 0 { defaultHost := os.Getenv("DOCKER_HOST") if defaultHost == "" || *flDaemon { // If we do not have a host, default to unix socket defaultHost = fmt.Sprintf("unix://%s", api.DEFAULTUNIXSOCKET) } defaultHost, err := api.ValidateHost(defaultHost) if err != nil { log.Fatal(err) } flHosts = append(flHosts, defaultHost) } if *flDaemon { mainDaemon() return } if len(flHosts) > 1 { log.Fatal("Please specify only one -H") } protoAddrParts := strings.SplitN(flHosts[0], "://", 2) var ( cli *client.DockerCli tlsConfig tls.Config ) tlsConfig.InsecureSkipVerify = true // If we should verify the server, we need to load a trusted ca if *flTlsVerify { *flTls = true certPool := x509.NewCertPool() file, err := ioutil.ReadFile(*flCa) if err != nil { log.Fatalf("Couldn't read ca cert %s: %s", *flCa, err) } certPool.AppendCertsFromPEM(file) tlsConfig.RootCAs = certPool tlsConfig.InsecureSkipVerify = false } // If tls is enabled, try to load and send client certificates if *flTls || *flTlsVerify { _, errCert := os.Stat(*flCert) _, errKey := os.Stat(*flKey) if errCert == nil && errKey == nil { *flTls = true cert, err := tls.LoadX509KeyPair(*flCert, *flKey) if err != nil { log.Fatalf("Couldn't load X509 key pair: %s. Key encrypted?", err) } tlsConfig.Certificates = []tls.Certificate{cert} } } if *flTls || *flTlsVerify { cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, nil, protoAddrParts[0], protoAddrParts[1], &tlsConfig) } else { cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, nil, protoAddrParts[0], protoAddrParts[1], nil) } if err := cli.Cmd(flag.Args()...); err != nil { if sterr, ok := err.(*utils.StatusError); ok { if sterr.Status != "" { log.Infof("%s", sterr.Status) } os.Exit(sterr.StatusCode) } log.Fatal(err) } }
func (daemon *Daemon) ContainerRm(job *engine.Job) engine.Status { if len(job.Args) != 1 { return job.Errorf("Not enough arguments. Usage: %s CONTAINER\n", job.Name) } name := job.Args[0] removeVolume := job.GetenvBool("removeVolume") removeLink := job.GetenvBool("removeLink") forceRemove := job.GetenvBool("forceRemove") container := daemon.Get(name) if removeLink { if container == nil { return job.Errorf("No such link: %s", name) } name, err := GetFullContainerName(name) if err != nil { job.Error(err) } parent, n := path.Split(name) if parent == "/" { return job.Errorf("Conflict, cannot remove the default name of the container") } pe := daemon.ContainerGraph().Get(parent) if pe == nil { return job.Errorf("Cannot get parent %s for name %s", parent, name) } parentContainer := daemon.Get(pe.ID()) if parentContainer != nil { parentContainer.DisableLink(n) } if err := daemon.ContainerGraph().Delete(name); err != nil { return job.Error(err) } return engine.StatusOK } if container != nil { if container.State.IsRunning() { if forceRemove { if err := container.Kill(); err != nil { return job.Errorf("Could not kill running container, cannot remove - %v", err) } } else { return job.Errorf("You cannot remove a running container. Stop the container before attempting removal or use -f") } } if err := daemon.Destroy(container); err != nil { return job.Errorf("Cannot destroy container %s: %s", name, err) } container.LogEvent("destroy") if removeVolume { var ( volumes = make(map[string]struct{}) binds = make(map[string]struct{}) usedVolumes = make(map[string]*Container) ) // the volume id is always the base of the path getVolumeId := func(p string) string { return filepath.Base(strings.TrimSuffix(p, "/layer")) } // populate bind map so that they can be skipped and not removed for _, bind := range container.HostConfig().Binds { source := strings.Split(bind, ":")[0] // TODO: refactor all volume stuff, all of it // it is very important that we eval the link or comparing the keys to container.Volumes will not work // // eval symlink can fail, ref #5244 if we receive an is not exist error we can ignore it p, err := filepath.EvalSymlinks(source) if err != nil && !os.IsNotExist(err) { return job.Error(err) } if p != "" { source = p } binds[source] = struct{}{} } // Store all the deleted containers volumes for _, volumeId := range container.Volumes { // Skip the volumes mounted from external // bind mounts here will will be evaluated for a symlink if _, exists := binds[volumeId]; exists { continue } volumeId = getVolumeId(volumeId) volumes[volumeId] = struct{}{} } // Retrieve all volumes from all remaining containers for _, container := range daemon.List() { for _, containerVolumeId := range container.Volumes { containerVolumeId = getVolumeId(containerVolumeId) usedVolumes[containerVolumeId] = container } } for volumeId := range volumes { // If the requested volu if c, exists := usedVolumes[volumeId]; exists { log.Infof("The volume %s is used by the container %s. Impossible to remove it. Skipping.", volumeId, c.ID) continue } if err := daemon.Volumes().Delete(volumeId); err != nil { return job.Errorf("Error calling volumes.Delete(%q): %v", volumeId, err) } } } } else { return job.Errorf("No such container: %s", name) } return engine.StatusOK }
// ListenAndServe sets up the required http.Server and gets it listening for // each addr passed in and does protocol specific checking. func ListenAndServe(proto, addr string, job *engine.Job) error { var l net.Listener r, err := createRouter(job.Eng, job.GetenvBool("Logging"), job.GetenvBool("EnableCors"), job.Getenv("Version")) if err != nil { return err } if proto == "fd" { return ServeFd(addr, r) } if proto == "unix" { if err := syscall.Unlink(addr); err != nil && !os.IsNotExist(err) { return err } } var oldmask int if proto == "unix" { oldmask = syscall.Umask(0777) } if job.GetenvBool("BufferRequests") { l, err = listenbuffer.NewListenBuffer(proto, addr, activationLock) } else { l, err = net.Listen(proto, addr) } if proto == "unix" { syscall.Umask(oldmask) } if err != nil { return err } if proto != "unix" && (job.GetenvBool("Tls") || job.GetenvBool("TlsVerify")) { tlsCert := job.Getenv("TlsCert") tlsKey := job.Getenv("TlsKey") cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey) if err != nil { return fmt.Errorf("Couldn't load X509 key pair (%s, %s): %s. Key encrypted?", tlsCert, tlsKey, err) } tlsConfig := &tls.Config{ NextProtos: []string{"http/1.1"}, Certificates: []tls.Certificate{cert}, } if job.GetenvBool("TlsVerify") { certPool := x509.NewCertPool() file, err := ioutil.ReadFile(job.Getenv("TlsCa")) if err != nil { return fmt.Errorf("Couldn't read CA certificate: %s", err) } certPool.AppendCertsFromPEM(file) tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert tlsConfig.ClientCAs = certPool } l = tls.NewListener(l, tlsConfig) } // Basic error and sanity checking switch proto { case "tcp": if !strings.HasPrefix(addr, "127.0.0.1") && !job.GetenvBool("TlsVerify") { log.Infof("/!\\ DON'T BIND ON ANOTHER IP ADDRESS THAN 127.0.0.1 IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\") } case "unix": socketGroup := job.Getenv("SocketGroup") if socketGroup != "" { if err := changeGroup(addr, socketGroup); err != nil { if socketGroup == "docker" { // if the user hasn't explicitly specified the group ownership, don't fail on errors. log.Debugf("Warning: could not chgrp %s to docker: %s", addr, err.Error()) } else { return err } } } if err := os.Chmod(addr, 0660); err != nil { return err } default: return fmt.Errorf("Invalid protocol format.") } httpSrv := http.Server{Addr: addr, Handler: r} return httpSrv.Serve(l) }
func (daemon *Daemon) restore() error { var ( debug = (os.Getenv("DEBUG") != "" || os.Getenv("TEST") != "") containers = make(map[string]*Container) currentDriver = daemon.driver.String() ) if !debug { log.Infof("Loading containers: ") } dir, err := ioutil.ReadDir(daemon.repository) if err != nil { return err } for _, v := range dir { id := v.Name() container, err := daemon.load(id) if !debug { fmt.Print(".") } if err != nil { log.Errorf("Failed to load container %v: %v", id, err) continue } // Ignore the container if it does not support the current driver being used by the graph if (container.Driver == "" && currentDriver == "aufs") || container.Driver == currentDriver { log.Debugf("Loaded container %v", container.ID) containers[container.ID] = container } else { log.Debugf("Cannot load container %s because it was created with another graph driver.", container.ID) } } registeredContainers := []*Container{} if entities := daemon.containerGraph.List("/", -1); entities != nil { for _, p := range entities.Paths() { if !debug { fmt.Print(".") } e := entities[p] if container, ok := containers[e.ID()]; ok { if err := daemon.register(container, false); err != nil { log.Debugf("Failed to register container %s: %s", container.ID, err) } registeredContainers = append(registeredContainers, container) // delete from the map so that a new name is not automatically generated delete(containers, e.ID()) } } } // Any containers that are left over do not exist in the graph for _, container := range containers { // Try to set the default name for a container if it exists prior to links container.Name, err = daemon.generateNewName(container.ID) if err != nil { log.Debugf("Setting default id - %s", err) } if err := daemon.register(container, false); err != nil { log.Debugf("Failed to register container %s: %s", container.ID, err) } registeredContainers = append(registeredContainers, container) } // check the restart policy on the containers and restart any container with // the restart policy of "always" if daemon.config.AutoRestart { log.Debugf("Restarting containers...") for _, container := range registeredContainers { if container.hostConfig.RestartPolicy.Name == "always" || (container.hostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0) { log.Debugf("Starting container %s", container.ID) if err := container.Start(); err != nil { log.Debugf("Failed to start container %s: %s", container.ID, err) } } } } for _, c := range registeredContainers { c.registerVolumes() } if !debug { log.Infof(": done.") } return nil }
func (s *TagStore) pullV2Tag(eng *engine.Engine, r *registry.Session, out io.Writer, localName, remoteName, tag string, sf *utils.StreamFormatter, parallel bool) error { log.Debugf("Pulling tag from V2 registry: %q", tag) manifestBytes, err := r.GetV2ImageManifest(remoteName, tag, nil) if err != nil { return err } manifest, verified, err := s.verifyManifest(eng, manifestBytes) if err != nil { return fmt.Errorf("error verifying manifest: %s", err) } if len(manifest.BlobSums) != len(manifest.History) { return fmt.Errorf("length of history not equal to number of layers") } if verified { out.Write(sf.FormatStatus("", "The image you are pulling has been digitally signed by Docker, Inc.")) } out.Write(sf.FormatStatus(tag, "Pulling from %s", localName)) downloads := make([]downloadInfo, len(manifest.BlobSums)) for i := len(manifest.BlobSums) - 1; i >= 0; i-- { var ( sumStr = manifest.BlobSums[i] imgJSON = []byte(manifest.History[i]) ) img, err := image.NewImgJSON(imgJSON) if err != nil { return fmt.Errorf("failed to parse json: %s", err) } downloads[i].img = img // Check if exists if s.graph.Exists(img.ID) { log.Debugf("Image already exists: %s", img.ID) continue } chunks := strings.SplitN(sumStr, ":", 2) if len(chunks) < 2 { return fmt.Errorf("expected 2 parts in the sumStr, got %#v", chunks) } sumType, checksum := chunks[0], chunks[1] out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Pulling fs layer", nil)) downloadFunc := func(di *downloadInfo) error { log.Infof("pulling blob %q to V1 img %s", sumStr, img.ID) if c, err := s.poolAdd("pull", "img:"+img.ID); err != nil { if c != nil { out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Layer already being pulled by another client. Waiting.", nil)) <-c out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Download complete", nil)) } else { log.Debugf("Image (id: %s) pull is already running, skipping: %v", img.ID, err) } } else { tmpFile, err := ioutil.TempFile("", "GetV2ImageBlob") if err != nil { return err } r, l, err := r.GetV2ImageBlobReader(remoteName, sumType, checksum, nil) if err != nil { return err } defer r.Close() io.Copy(tmpFile, utils.ProgressReader(r, int(l), out, sf, false, utils.TruncateID(img.ID), "Downloading")) out.Write(sf.FormatProgress(utils.TruncateID(img.ID), "Download complete", nil)) log.Debugf("Downloaded %s to tempfile %s", img.ID, tmpFile.Name()) di.tmpFile = tmpFile di.length = l di.downloaded = true } di.imgJSON = imgJSON defer s.poolRemove("pull", "img:"+img.ID) return nil } if parallel { downloads[i].err = make(chan error) go func(di *downloadInfo) { di.err <- downloadFunc(di) }(&downloads[i]) } else { err := downloadFunc(&downloads[i]) if err != nil { return err } } } for i := len(downloads) - 1; i >= 0; i-- { d := &downloads[i] if d.err != nil { err := <-d.err if err != nil { return err } } if d.downloaded { // if tmpFile is empty assume download and extracted elsewhere defer os.Remove(d.tmpFile.Name()) defer d.tmpFile.Close() d.tmpFile.Seek(0, 0) if d.tmpFile != nil { err = s.graph.Register(d.img, d.imgJSON, utils.ProgressReader(d.tmpFile, int(d.length), out, sf, false, utils.TruncateID(d.img.ID), "Extracting")) if err != nil { return err } // FIXME: Pool release here for parallel tag pull (ensures any downloads block until fully extracted) } out.Write(sf.FormatProgress(utils.TruncateID(d.img.ID), "Pull complete", nil)) } else { out.Write(sf.FormatProgress(utils.TruncateID(d.img.ID), "Already exists", nil)) } } if err = s.Set(localName, tag, downloads[0].img.ID, true); err != nil { return err } return nil }