Browse code

Add clean if start fail

Victor Vieux authored on 2013/10/17 04:01:55
Showing 1 changed files
... ...
@@ -567,262 +567,270 @@ func (container *Container) Start(hostConfig *HostConfig) error {
567 567
 	container.State.Lock()
568 568
 	defer container.State.Unlock()
569 569
 
570
-	if hostConfig == nil { // in docker start of docker restart we want to reuse previous HostConfigFile
571
-		hostConfig, _ = container.ReadHostConfig()
572
-	}
570
+	startFct := func(hostConfig *HostConfig) error {
573 571
 
574
-	if container.State.Running {
575
-		return fmt.Errorf("The container %s is already running.", container.ID)
576
-	}
577
-	if err := container.EnsureMounted(); err != nil {
578
-		return err
579
-	}
580
-	if container.runtime.networkManager.disabled {
581
-		container.Config.NetworkDisabled = true
582
-	} else {
583
-		if err := container.allocateNetwork(); err != nil {
572
+		if hostConfig == nil { // in docker start of docker restart we want to reuse previous HostConfigFile
573
+			hostConfig, _ = container.ReadHostConfig()
574
+		}
575
+
576
+		if container.State.Running {
577
+			return fmt.Errorf("The container %s is already running.", container.ID)
578
+		}
579
+		if err := container.EnsureMounted(); err != nil {
584 580
 			return err
585 581
 		}
586
-	}
582
+		if container.runtime.networkManager.disabled {
583
+			container.Config.NetworkDisabled = true
584
+		} else {
585
+			if err := container.allocateNetwork(); err != nil {
586
+				return err
587
+			}
588
+		}
587 589
 
588
-	// Make sure the config is compatible with the current kernel
589
-	if container.Config.Memory > 0 && !container.runtime.capabilities.MemoryLimit {
590
-		log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
591
-		container.Config.Memory = 0
592
-	}
593
-	if container.Config.Memory > 0 && !container.runtime.capabilities.SwapLimit {
594
-		log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
595
-		container.Config.MemorySwap = -1
596
-	}
590
+		// Make sure the config is compatible with the current kernel
591
+		if container.Config.Memory > 0 && !container.runtime.capabilities.MemoryLimit {
592
+			log.Printf("WARNING: Your kernel does not support memory limit capabilities. Limitation discarded.\n")
593
+			container.Config.Memory = 0
594
+		}
595
+		if container.Config.Memory > 0 && !container.runtime.capabilities.SwapLimit {
596
+			log.Printf("WARNING: Your kernel does not support swap limit capabilities. Limitation discarded.\n")
597
+			container.Config.MemorySwap = -1
598
+		}
597 599
 
598
-	if container.runtime.capabilities.IPv4ForwardingDisabled {
599
-		log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
600
-	}
600
+		if container.runtime.capabilities.IPv4ForwardingDisabled {
601
+			log.Printf("WARNING: IPv4 forwarding is disabled. Networking will not work")
602
+		}
601 603
 
602
-	// Create the requested bind mounts
603
-	binds := make(map[string]BindMap)
604
-	// Define illegal container destinations
605
-	illegalDsts := []string{"/", "."}
604
+		// Create the requested bind mounts
605
+		binds := make(map[string]BindMap)
606
+		// Define illegal container destinations
607
+		illegalDsts := []string{"/", "."}
608
+
609
+		for _, bind := range hostConfig.Binds {
610
+			// FIXME: factorize bind parsing in parseBind
611
+			var src, dst, mode string
612
+			arr := strings.Split(bind, ":")
613
+			if len(arr) == 2 {
614
+				src = arr[0]
615
+				dst = arr[1]
616
+				mode = "rw"
617
+			} else if len(arr) == 3 {
618
+				src = arr[0]
619
+				dst = arr[1]
620
+				mode = arr[2]
621
+			} else {
622
+				return fmt.Errorf("Invalid bind specification: %s", bind)
623
+			}
606 624
 
607
-	for _, bind := range hostConfig.Binds {
608
-		// FIXME: factorize bind parsing in parseBind
609
-		var src, dst, mode string
610
-		arr := strings.Split(bind, ":")
611
-		if len(arr) == 2 {
612
-			src = arr[0]
613
-			dst = arr[1]
614
-			mode = "rw"
615
-		} else if len(arr) == 3 {
616
-			src = arr[0]
617
-			dst = arr[1]
618
-			mode = arr[2]
619
-		} else {
620
-			return fmt.Errorf("Invalid bind specification: %s", bind)
621
-		}
625
+			// Bail if trying to mount to an illegal destination
626
+			for _, illegal := range illegalDsts {
627
+				if dst == illegal {
628
+					return fmt.Errorf("Illegal bind destination: %s", dst)
629
+				}
630
+			}
622 631
 
623
-		// Bail if trying to mount to an illegal destination
624
-		for _, illegal := range illegalDsts {
625
-			if dst == illegal {
626
-				return fmt.Errorf("Illegal bind destination: %s", dst)
632
+			bindMap := BindMap{
633
+				SrcPath: src,
634
+				DstPath: dst,
635
+				Mode:    mode,
627 636
 			}
637
+			binds[path.Clean(dst)] = bindMap
628 638
 		}
629 639
 
630
-		bindMap := BindMap{
631
-			SrcPath: src,
632
-			DstPath: dst,
633
-			Mode:    mode,
640
+		if container.Volumes == nil || len(container.Volumes) == 0 {
641
+			container.Volumes = make(map[string]string)
642
+			container.VolumesRW = make(map[string]bool)
634 643
 		}
635
-		binds[path.Clean(dst)] = bindMap
636
-	}
637 644
 
638
-	if container.Volumes == nil || len(container.Volumes) == 0 {
639
-		container.Volumes = make(map[string]string)
640
-		container.VolumesRW = make(map[string]bool)
641
-	}
642
-
643
-	// Apply volumes from another container if requested
644
-	if container.Config.VolumesFrom != "" {
645
-		volumes := strings.Split(container.Config.VolumesFrom, ",")
646
-		for _, v := range volumes {
647
-			c := container.runtime.Get(v)
648
-			if c == nil {
649
-				return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
650
-			}
651
-			for volPath, id := range c.Volumes {
652
-				if _, exists := container.Volumes[volPath]; exists {
653
-					continue
654
-				}
655
-				if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
656
-					return err
645
+		// Apply volumes from another container if requested
646
+		if container.Config.VolumesFrom != "" {
647
+			volumes := strings.Split(container.Config.VolumesFrom, ",")
648
+			for _, v := range volumes {
649
+				c := container.runtime.Get(v)
650
+				if c == nil {
651
+					return fmt.Errorf("Container %s not found. Impossible to mount its volumes", container.ID)
657 652
 				}
658
-				container.Volumes[volPath] = id
659
-				if isRW, exists := c.VolumesRW[volPath]; exists {
660
-					container.VolumesRW[volPath] = isRW
653
+				for volPath, id := range c.Volumes {
654
+					if _, exists := container.Volumes[volPath]; exists {
655
+						continue
656
+					}
657
+					if err := os.MkdirAll(path.Join(container.RootfsPath(), volPath), 0755); err != nil {
658
+						return err
659
+					}
660
+					container.Volumes[volPath] = id
661
+					if isRW, exists := c.VolumesRW[volPath]; exists {
662
+						container.VolumesRW[volPath] = isRW
663
+					}
661 664
 				}
662
-			}
663 665
 
666
+			}
664 667
 		}
665
-	}
666 668
 
667
-	// Create the requested volumes if they don't exist
668
-	for volPath := range container.Config.Volumes {
669
-		volPath = path.Clean(volPath)
670
-		// Skip existing volumes
671
-		if _, exists := container.Volumes[volPath]; exists {
672
-			continue
673
-		}
674
-		var srcPath string
675
-		var isBindMount bool
676
-		srcRW := false
677
-		// If an external bind is defined for this volume, use that as a source
678
-		if bindMap, exists := binds[volPath]; exists {
679
-			isBindMount = true
680
-			srcPath = bindMap.SrcPath
681
-			if strings.ToLower(bindMap.Mode) == "rw" {
682
-				srcRW = true
669
+		// Create the requested volumes if they don't exist
670
+		for volPath := range container.Config.Volumes {
671
+			volPath = path.Clean(volPath)
672
+			// Skip existing volumes
673
+			if _, exists := container.Volumes[volPath]; exists {
674
+				continue
683 675
 			}
684
-			// Otherwise create an directory in $ROOT/volumes/ and use that
685
-		} else {
686
-			c, err := container.runtime.volumes.Create(nil, container, "", "", nil)
687
-			if err != nil {
688
-				return err
676
+			var srcPath string
677
+			var isBindMount bool
678
+			srcRW := false
679
+			// If an external bind is defined for this volume, use that as a source
680
+			if bindMap, exists := binds[volPath]; exists {
681
+				isBindMount = true
682
+				srcPath = bindMap.SrcPath
683
+				if strings.ToLower(bindMap.Mode) == "rw" {
684
+					srcRW = true
685
+				}
686
+				// Otherwise create an directory in $ROOT/volumes/ and use that
687
+			} else {
688
+				c, err := container.runtime.volumes.Create(nil, container, "", "", nil)
689
+				if err != nil {
690
+					return err
691
+				}
692
+				srcPath, err = c.layer()
693
+				if err != nil {
694
+					return err
695
+				}
696
+				srcRW = true // RW by default
689 697
 			}
690
-			srcPath, err = c.layer()
691
-			if err != nil {
692
-				return err
698
+			container.Volumes[volPath] = srcPath
699
+			container.VolumesRW[volPath] = srcRW
700
+			// Create the mountpoint
701
+			rootVolPath := path.Join(container.RootfsPath(), volPath)
702
+			if err := os.MkdirAll(rootVolPath, 0755); err != nil {
703
+				return nil
693 704
 			}
694
-			srcRW = true // RW by default
695
-		}
696
-		container.Volumes[volPath] = srcPath
697
-		container.VolumesRW[volPath] = srcRW
698
-		// Create the mountpoint
699
-		rootVolPath := path.Join(container.RootfsPath(), volPath)
700
-		if err := os.MkdirAll(rootVolPath, 0755); err != nil {
701
-			return nil
702
-		}
703 705
 
704
-		// Do not copy or change permissions if we are mounting from the host
705
-		if srcRW && !isBindMount {
706
-			volList, err := ioutil.ReadDir(rootVolPath)
707
-			if err != nil {
708
-				return err
709
-			}
710
-			if len(volList) > 0 {
711
-				srcList, err := ioutil.ReadDir(srcPath)
706
+			// Do not copy or change permissions if we are mounting from the host
707
+			if srcRW && !isBindMount {
708
+				volList, err := ioutil.ReadDir(rootVolPath)
712 709
 				if err != nil {
713 710
 					return err
714 711
 				}
715
-				if len(srcList) == 0 {
716
-					// If the source volume is empty copy files from the root into the volume
717
-					if err := CopyWithTar(rootVolPath, srcPath); err != nil {
712
+				if len(volList) > 0 {
713
+					srcList, err := ioutil.ReadDir(srcPath)
714
+					if err != nil {
718 715
 						return err
719 716
 					}
717
+					if len(srcList) == 0 {
718
+						// If the source volume is empty copy files from the root into the volume
719
+						if err := CopyWithTar(rootVolPath, srcPath); err != nil {
720
+							return err
721
+						}
720 722
 
721
-					var stat syscall.Stat_t
722
-					if err := syscall.Stat(rootVolPath, &stat); err != nil {
723
-						return err
724
-					}
725
-					var srcStat syscall.Stat_t
726
-					if err := syscall.Stat(srcPath, &srcStat); err != nil {
727
-						return err
728
-					}
729
-					// Change the source volume's ownership if it differs from the root
730
-					// files that where just copied
731
-					if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
732
-						if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
723
+						var stat syscall.Stat_t
724
+						if err := syscall.Stat(rootVolPath, &stat); err != nil {
725
+							return err
726
+						}
727
+						var srcStat syscall.Stat_t
728
+						if err := syscall.Stat(srcPath, &srcStat); err != nil {
733 729
 							return err
734 730
 						}
731
+						// Change the source volume's ownership if it differs from the root
732
+						// files that where just copied
733
+						if stat.Uid != srcStat.Uid || stat.Gid != srcStat.Gid {
734
+							if err := os.Chown(srcPath, int(stat.Uid), int(stat.Gid)); err != nil {
735
+								return err
736
+							}
737
+						}
735 738
 					}
736 739
 				}
737 740
 			}
738 741
 		}
739
-	}
740
-
741
-	if err := container.generateLXCConfig(hostConfig); err != nil {
742
-		return err
743
-	}
744 742
 
745
-	params := []string{
746
-		"-n", container.ID,
747
-		"-f", container.lxcConfigPath(),
748
-		"--",
749
-		"/.dockerinit",
750
-	}
751
-
752
-	// Networking
753
-	if !container.Config.NetworkDisabled {
754
-		params = append(params, "-g", container.network.Gateway.String())
755
-	}
743
+		if err := container.generateLXCConfig(hostConfig); err != nil {
744
+			return err
745
+		}
756 746
 
757
-	// User
758
-	if container.Config.User != "" {
759
-		params = append(params, "-u", container.Config.User)
760
-	}
747
+		params := []string{
748
+			"-n", container.ID,
749
+			"-f", container.lxcConfigPath(),
750
+			"--",
751
+			"/.dockerinit",
752
+		}
761 753
 
762
-	if container.Config.Tty {
763
-		params = append(params, "-e", "TERM=xterm")
764
-	}
754
+		// Networking
755
+		if !container.Config.NetworkDisabled {
756
+			params = append(params, "-g", container.network.Gateway.String())
757
+		}
765 758
 
766
-	// Setup environment
767
-	params = append(params,
768
-		"-e", "HOME=/",
769
-		"-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
770
-		"-e", "container=lxc",
771
-		"-e", "HOSTNAME="+container.Config.Hostname,
772
-	)
773
-	if container.Config.WorkingDir != "" {
774
-		workingDir := path.Clean(container.Config.WorkingDir)
775
-		utils.Debugf("[working dir] working dir is %s", workingDir)
759
+		// User
760
+		if container.Config.User != "" {
761
+			params = append(params, "-u", container.Config.User)
762
+		}
776 763
 
777
-		if err := os.MkdirAll(path.Join(container.RootfsPath(), workingDir), 0755); err != nil {
778
-			return nil
764
+		if container.Config.Tty {
765
+			params = append(params, "-e", "TERM=xterm")
779 766
 		}
780 767
 
768
+		// Setup environment
781 769
 		params = append(params,
782
-			"-w", workingDir,
770
+			"-e", "HOME=/",
771
+			"-e", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
772
+			"-e", "container=lxc",
773
+			"-e", "HOSTNAME="+container.Config.Hostname,
783 774
 		)
784
-	}
775
+		if container.Config.WorkingDir != "" {
776
+			workingDir := path.Clean(container.Config.WorkingDir)
777
+			utils.Debugf("[working dir] working dir is %s", workingDir)
785 778
 
786
-	for _, elem := range container.Config.Env {
787
-		params = append(params, "-e", elem)
788
-	}
779
+			if err := os.MkdirAll(path.Join(container.RootfsPath(), workingDir), 0755); err != nil {
780
+				return nil
781
+			}
789 782
 
790
-	// Program
791
-	params = append(params, "--", container.Path)
792
-	params = append(params, container.Args...)
783
+			params = append(params,
784
+				"-w", workingDir,
785
+			)
786
+		}
793 787
 
794
-	container.cmd = exec.Command("lxc-start", params...)
788
+		for _, elem := range container.Config.Env {
789
+			params = append(params, "-e", elem)
790
+		}
795 791
 
796
-	// Setup logging of stdout and stderr to disk
797
-	if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
798
-		return err
799
-	}
800
-	if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
801
-		return err
802
-	}
792
+		// Program
793
+		params = append(params, "--", container.Path)
794
+		params = append(params, container.Args...)
803 795
 
804
-	container.cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
796
+		container.cmd = exec.Command("lxc-start", params...)
805 797
 
806
-	var err error
807
-	if container.Config.Tty {
808
-		err = container.startPty()
809
-	} else {
810
-		err = container.start()
798
+		// Setup logging of stdout and stderr to disk
799
+		if err := container.runtime.LogToDisk(container.stdout, container.logPath("json"), "stdout"); err != nil {
800
+			return err
801
+		}
802
+		if err := container.runtime.LogToDisk(container.stderr, container.logPath("json"), "stderr"); err != nil {
803
+			return err
804
+		}
805
+
806
+		container.cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
807
+
808
+		var err error
809
+		if container.Config.Tty {
810
+			err = container.startPty()
811
+		} else {
812
+			err = container.start()
813
+		}
814
+		if err != nil {
815
+			return err
816
+		}
817
+		// FIXME: save state on disk *first*, then converge
818
+		// this way disk state is used as a journal, eg. we can restore after crash etc.
819
+		container.State.setRunning(container.cmd.Process.Pid)
820
+
821
+		// Init the lock
822
+		container.waitLock = make(chan struct{})
823
+
824
+		container.ToDisk()
825
+		container.SaveHostConfig(hostConfig)
826
+		go container.monitor(hostConfig)
827
+		return nil
811 828
 	}
829
+	err := startFct(hostConfig)
812 830
 	if err != nil {
813
-		return err
831
+		container.cleanup()
814 832
 	}
815
-	// FIXME: save state on disk *first*, then converge
816
-	// this way disk state is used as a journal, eg. we can restore after crash etc.
817
-	container.State.setRunning(container.cmd.Process.Pid)
818
-
819
-	// Init the lock
820
-	container.waitLock = make(chan struct{})
821
-
822
-	container.ToDisk()
823
-	container.SaveHostConfig(hostConfig)
824
-	go container.monitor(hostConfig)
825
-	return nil
833
+	return err
826 834
 }
827 835
 
828 836
 func (container *Container) Run() error {
... ...
@@ -981,6 +989,28 @@ func (container *Container) monitor(hostConfig *HostConfig) {
981 981
 	}
982 982
 
983 983
 	// Cleanup
984
+	container.cleanup()
985
+
986
+	// Re-create a brand new stdin pipe once the container exited
987
+	if container.Config.OpenStdin {
988
+		container.stdin, container.stdinPipe = io.Pipe()
989
+	}
990
+
991
+	// Release the lock
992
+	close(container.waitLock)
993
+
994
+	if err := container.ToDisk(); err != nil {
995
+		// FIXME: there is a race condition here which causes this to fail during the unit tests.
996
+		// If another goroutine was waiting for Wait() to return before removing the container's root
997
+		// from the filesystem... At this point it may already have done so.
998
+		// This is because State.setStopped() has already been called, and has caused Wait()
999
+		// to return.
1000
+		// FIXME: why are we serializing running state to disk in the first place?
1001
+		//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
1002
+	}
1003
+}
1004
+
1005
+func (container *Container) cleanup() {
984 1006
 	container.releaseNetwork()
985 1007
 	if container.Config.OpenStdin {
986 1008
 		if err := container.stdin.Close(); err != nil {
... ...
@@ -1003,24 +1033,6 @@ func (container *Container) monitor(hostConfig *HostConfig) {
1003 1003
 	if err := container.Unmount(); err != nil {
1004 1004
 		log.Printf("%v: Failed to umount filesystem: %v", container.ID, err)
1005 1005
 	}
1006
-
1007
-	// Re-create a brand new stdin pipe once the container exited
1008
-	if container.Config.OpenStdin {
1009
-		container.stdin, container.stdinPipe = io.Pipe()
1010
-	}
1011
-
1012
-	// Release the lock
1013
-	close(container.waitLock)
1014
-
1015
-	if err := container.ToDisk(); err != nil {
1016
-		// FIXME: there is a race condition here which causes this to fail during the unit tests.
1017
-		// If another goroutine was waiting for Wait() to return before removing the container's root
1018
-		// from the filesystem... At this point it may already have done so.
1019
-		// This is because State.setStopped() has already been called, and has caused Wait()
1020
-		// to return.
1021
-		// FIXME: why are we serializing running state to disk in the first place?
1022
-		//log.Printf("%s: Failed to dump configuration to the disk: %s", container.ID, err)
1023
-	}
1024 1006
 }
1025 1007
 
1026 1008
 func (container *Container) kill() error {