Beispiel #1
0
// DiffGetter returns a FileGetCloser that can read files from the directory that
// contains files for the layer differences. Used for direct access for tar-split.
func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
	id, err := d.resolveID(id)
	if err != nil {
		return nil, err
	}

	if hcsshim.IsTP4() {
		// The export format for TP4 is different from the contents of the layer, so
		// fall back to exporting the layer and getting file contents from there.
		layerChain, err := d.getLayerChain(id)
		if err != nil {
			return nil, err
		}

		var tempFolder string
		tempFolder, err = ioutil.TempDir("", "hcs")
		if err != nil {
			return nil, err
		}
		defer func() {
			if err != nil {
				os.RemoveAll(tempFolder)
			}
		}()

		if err = hcsshim.ExportLayer(d.info, id, tempFolder, layerChain); err != nil {
			return nil, err
		}

		return &fileGetDestroyCloser{storage.NewPathFileGetter(tempFolder), tempFolder}, nil
	}

	return &fileGetCloserWithBackupPrivileges{d.dir(id)}, nil
}
Beispiel #2
0
// NewDriver returns a new windows driver, called from NewDriver of execdriver.
func NewDriver(root string, options []string) (*Driver, error) {

	for _, option := range options {
		key, val, err := parsers.ParseKeyValueOpt(option)
		if err != nil {
			return nil, err
		}
		key = strings.ToLower(key)
		switch key {

		case "dummy":
			switch val {
			case "1":
				dummyMode = true
				logrus.Warn("Using dummy mode in Windows exec driver. This is for development use only!")
			}

		case "forcekill":
			switch val {
			case "1":
				forceKill = true
				logrus.Warn("Using force kill mode in Windows exec driver. This is for testing purposes only.")
			}

		case "isolation":
			if !container.Isolation(val).IsValid() {
				return nil, fmt.Errorf("Unrecognised exec driver option 'isolation':'%s'", val)
			}
			if container.Isolation(val).IsHyperV() {
				DefaultIsolation = "hyperv"
			}
			logrus.Infof("Windows default isolation: '%s'", val)
		default:
			return nil, fmt.Errorf("Unrecognised exec driver option %s\n", key)
		}
	}

	// TODO Windows TP5 timeframe. Remove this next block of code once TP4
	// is no longer supported. Also remove the workaround in run.go.
	//
	// Hack for TP4.
	// This overcomes an issue on TP4 which causes CreateComputeSystem to
	// intermittently fail. It's predominantly here to make Windows to Windows
	// CI more reliable.
	TP4RetryHack = hcsshim.IsTP4()

	return &Driver{
		root:             root,
		activeContainers: make(map[string]*activeContainer),
	}, nil
}
Beispiel #3
0
// exportLayer generates an archive from a layer based on the given ID.
func (d *Driver) exportLayer(id string, parentLayerPaths []string) (archive.Archive, error) {
	if hcsshim.IsTP4() {
		// Export in TP4 format to maintain compatibility with existing images and
		// because ExportLayer is somewhat broken on TP4 and can't work with the new
		// scheme.
		tempFolder, err := ioutil.TempDir("", "hcs")
		if err != nil {
			return nil, err
		}
		defer func() {
			if err != nil {
				os.RemoveAll(tempFolder)
			}
		}()

		if err = hcsshim.ExportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
			return nil, err
		}
		archive, err := archive.Tar(tempFolder, archive.Uncompressed)
		if err != nil {
			return nil, err
		}
		return ioutils.NewReadCloserWrapper(archive, func() error {
			err := archive.Close()
			os.RemoveAll(tempFolder)
			return err
		}), nil
	}

	var r hcsshim.LayerReader
	r, err := hcsshim.NewLayerReader(d.info, id, parentLayerPaths)
	if err != nil {
		return nil, err
	}

	archive, w := io.Pipe()
	go func() {
		err := writeTarFromLayer(r, w)
		cerr := r.Close()
		if err == nil {
			err = cerr
		}
		w.CloseWithError(err)
	}()

	return archive, nil
}
Beispiel #4
0
// importLayer adds a new layer to the tag and graph store based on the given data.
func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) {
	if hcsshim.IsTP4() {
		// Import from TP4 format to maintain compatibility with existing images.
		var tempFolder string
		tempFolder, err = ioutil.TempDir("", "hcs")
		if err != nil {
			return
		}
		defer os.RemoveAll(tempFolder)

		if size, err = chrootarchive.ApplyLayer(tempFolder, layerData); err != nil {
			return
		}
		if err = hcsshim.ImportLayer(d.info, id, tempFolder, parentLayerPaths); err != nil {
			return
		}
		return
	}

	var w hcsshim.LayerWriter
	w, err = hcsshim.NewLayerWriter(d.info, id, parentLayerPaths)
	if err != nil {
		return
	}

	size, err = writeLayerFromTar(layerData, w)
	if err != nil {
		w.Close()
		return
	}
	err = w.Close()
	if err != nil {
		return
	}
	return
}
Beispiel #5
0
// Create is the entrypoint to create a container from a spec, and if successfully
// created, start it too.
func (clnt *client) Create(containerID string, spec Spec, options ...CreateOption) error {
	logrus.Debugln("LCD client.Create() with spec", spec)

	cu := &containerInit{
		SystemType: "Container",
		Name:       containerID,
		Owner:      defaultOwner,

		VolumePath:              spec.Root.Path,
		IgnoreFlushesDuringBoot: spec.Windows.FirstStart,
		LayerFolderPath:         spec.Windows.LayerFolder,
		HostName:                spec.Hostname,
	}

	if spec.Windows.Networking != nil {
		cu.EndpointList = spec.Windows.Networking.EndpointList
	}

	if spec.Windows.Resources != nil {
		if spec.Windows.Resources.CPU != nil {
			if spec.Windows.Resources.CPU.Shares != nil {
				cu.ProcessorWeight = *spec.Windows.Resources.CPU.Shares
			}
			if spec.Windows.Resources.CPU.Percent != nil {
				cu.ProcessorMaximum = *spec.Windows.Resources.CPU.Percent * 100 // ProcessorMaximum is a value between 1 and 10000
			}
		}
		if spec.Windows.Resources.Memory != nil {
			if spec.Windows.Resources.Memory.Limit != nil {
				cu.MemoryMaximumInMB = *spec.Windows.Resources.Memory.Limit / 1024 / 1024
			}
		}
		if spec.Windows.Resources.Storage != nil {
			if spec.Windows.Resources.Storage.Bps != nil {
				cu.StorageBandwidthMaximum = *spec.Windows.Resources.Storage.Bps
			}
			if spec.Windows.Resources.Storage.Iops != nil {
				cu.StorageIOPSMaximum = *spec.Windows.Resources.Storage.Iops
			}
			if spec.Windows.Resources.Storage.SandboxSize != nil {
				cu.StorageSandboxSize = *spec.Windows.Resources.Storage.SandboxSize
			}
		}
	}

	cu.HvPartition = (spec.Windows.HvRuntime != nil)

	// TODO Windows @jhowardmsft. FIXME post TP5.
	//	if spec.Windows.HvRuntime != nil {
	//		if spec.WIndows.HVRuntime.ImagePath != "" {
	//			cu.TBD = spec.Windows.HvRuntime.ImagePath
	//		}
	//	}

	if cu.HvPartition {
		cu.SandboxPath = filepath.Dir(spec.Windows.LayerFolder)
	} else {
		cu.VolumePath = spec.Root.Path
		cu.LayerFolderPath = spec.Windows.LayerFolder
	}

	for _, layerPath := range spec.Windows.LayerPaths {
		_, filename := filepath.Split(layerPath)
		g, err := hcsshim.NameToGuid(filename)
		if err != nil {
			return err
		}
		cu.Layers = append(cu.Layers, layer{
			ID:   g.ToString(),
			Path: layerPath,
		})
	}

	// Add the mounts (volumes, bind mounts etc) to the structure
	mds := make([]mappedDir, len(spec.Mounts))
	for i, mount := range spec.Mounts {
		mds[i] = mappedDir{
			HostPath:      mount.Source,
			ContainerPath: mount.Destination,
			ReadOnly:      mount.Readonly}
	}
	cu.MappedDirectories = mds

	// TODO Windows: vv START OF TP4 BLOCK OF CODE. REMOVE ONCE TP4 IS NO LONGER SUPPORTED
	if hcsshim.IsTP4() &&
		spec.Windows.Networking != nil &&
		spec.Windows.Networking.Bridge != "" {
		// Enumerate through the port bindings specified by the user and convert
		// them into the internal structure matching the JSON blob that can be
		// understood by the HCS.
		var pbs []portBinding
		for i, v := range spec.Windows.Networking.PortBindings {
			proto := strings.ToUpper(i.Proto())
			if proto != "TCP" && proto != "UDP" {
				return fmt.Errorf("invalid protocol %s", i.Proto())
			}

			if len(v) > 1 {
				return fmt.Errorf("Windows does not support more than one host port in NAT settings")
			}

			for _, v2 := range v {
				var (
					iPort, ePort int
					err          error
				)
				if len(v2.HostIP) != 0 {
					return fmt.Errorf("Windows does not support host IP addresses in NAT settings")
				}
				if ePort, err = strconv.Atoi(v2.HostPort); err != nil {
					return fmt.Errorf("invalid container port %s: %s", v2.HostPort, err)
				}
				if iPort, err = strconv.Atoi(i.Port()); err != nil {
					return fmt.Errorf("invalid internal port %s: %s", i.Port(), err)
				}
				if iPort < 0 || iPort > 65535 || ePort < 0 || ePort > 65535 {
					return fmt.Errorf("specified NAT port is not in allowed range")
				}
				pbs = append(pbs,
					portBinding{ExternalPort: ePort,
						InternalPort: iPort,
						Protocol:     proto})
			}
		}

		dev := device{
			DeviceType: "Network",
			Connection: &networkConnection{
				NetworkName: spec.Windows.Networking.Bridge,
				Nat: natSettings{
					Name:         defaultContainerNAT,
					PortBindings: pbs,
				},
			},
		}

		if spec.Windows.Networking.MacAddress != "" {
			windowsStyleMAC := strings.Replace(
				spec.Windows.Networking.MacAddress, ":", "-", -1)
			dev.Settings = networkSettings{
				MacAddress: windowsStyleMAC,
			}
		}
		cu.Devices = append(cu.Devices, dev)
	} else {
		logrus.Debugln("No network interface")
	}
	// TODO Windows: ^^ END OF TP4 BLOCK OF CODE. REMOVE ONCE TP4 IS NO LONGER SUPPORTED

	configurationb, err := json.Marshal(cu)
	if err != nil {
		return err
	}

	configuration := string(configurationb)

	// TODO Windows TP5 timeframe. Remove when TP4 is no longer supported.
	// The following a workaround for Windows TP4 which has a networking
	// bug which fairly frequently returns an error. Back off and retry.
	if !hcsshim.IsTP4() {
		if err := hcsshim.CreateComputeSystem(containerID, configuration); err != nil {
			return err
		}
	} else {
		maxAttempts := 5
		for i := 1; i <= maxAttempts; i++ {
			err = hcsshim.CreateComputeSystem(containerID, configuration)
			if err == nil {
				break
			}

			if herr, ok := err.(*hcsshim.HcsError); ok {
				if herr.Err != syscall.ERROR_NOT_FOUND && // Element not found
					herr.Err != syscall.ERROR_FILE_NOT_FOUND && // The system cannot find the file specified
					herr.Err != ErrorNoNetwork && // The network is not present or not started
					herr.Err != ErrorBadPathname && // The specified path is invalid
					herr.Err != CoEClassstring && // Invalid class string
					herr.Err != ErrorInvalidObject { // The object identifier does not represent a valid object
					logrus.Debugln("Failed to create temporary container ", err)
					return err
				}
				logrus.Warnf("Invoking Windows TP4 retry hack (%d of %d)", i, maxAttempts-1)
				time.Sleep(50 * time.Millisecond)
			}
		}
	}

	// Construct a container object for calling start on it.
	container := &container{
		containerCommon: containerCommon{
			process: process{
				processCommon: processCommon{
					containerID:  containerID,
					client:       clnt,
					friendlyName: InitFriendlyName,
				},
			},
			processes: make(map[string]*process),
		},
		ociSpec: spec,
	}

	container.options = options
	for _, option := range options {
		if err := option.Apply(container); err != nil {
			logrus.Error(err)
		}
	}

	// Call start, and if it fails, delete the container from our
	// internal structure, and also keep HCS in sync by deleting the
	// container there.
	logrus.Debugf("Create() id=%s, Calling start()", containerID)
	if err := container.start(); err != nil {
		clnt.deleteContainer(containerID)
		return err
	}

	logrus.Debugf("Create() id=%s completed successfully", containerID)
	return nil

}