func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, endpointConfig *networktypes.EndpointSettings, updateSettings bool) (err error) { if container.HostConfig.NetworkMode.IsContainer() { return runconfig.ErrConflictSharedNetwork } if !containertypes.NetworkMode(idOrName).IsUserDefined() && hasUserDefinedIPAddress(endpointConfig) { return runconfig.ErrUnsupportedNetworkAndIP } if containertypes.NetworkMode(idOrName).IsBridge() && daemon.configStore.DisableBridge { container.Config.NetworkDisabled = true return nil } controller := daemon.netController n, err := daemon.FindNetwork(idOrName) if err != nil { return err } if err := validateNetworkingConfig(n, endpointConfig); err != nil { return err } if updateSettings { if err := daemon.updateNetworkSettings(container, n); err != nil { return err } } if endpointConfig != nil { container.NetworkSettings.Networks[n.Name()] = endpointConfig } ep, err := container.GetEndpointInNetwork(n) if err == nil { return fmt.Errorf("Conflict. A container with name %q is already connected to network %s.", strings.TrimPrefix(container.Name, "/"), idOrName) } if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok { return err } createOptions, err := container.BuildCreateEndpointOptions(n) if err != nil { return err } endpointName := strings.TrimPrefix(container.Name, "/") ep, err = n.CreateEndpoint(endpointName, createOptions...) if err != nil { return err } defer func() { if err != nil { if e := ep.Delete(); e != nil { logrus.Warnf("Could not rollback container connection to network %s", idOrName) } } }() if err := daemon.updateEndpointNetworkSettings(container, n, ep); err != nil { return err } sb := daemon.getNetworkSandbox(container) if sb == nil { options, err := daemon.buildSandboxOptions(container, n) if err != nil { return err } sb, err = controller.NewSandbox(container.ID, options...) if err != nil { return err } container.UpdateSandboxNetworkSettings(sb) } if err := ep.Join(sb); err != nil { return err } if err := container.UpdateJoinInfo(n, ep); err != nil { return derr.ErrorCodeJoinInfo.WithArgs(err) } daemon.LogNetworkEventWithAttributes(n, "connect", map[string]string{"container": container.ID}) return nil }
func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, error) { s := oci.DefaultSpec() linkedEnv, err := daemon.setupLinkedContainers(c) if err != nil { return nil, err } // TODO Windows - this can be removed. Not used (UID/GID) rootUID, rootGID := daemon.GetRemappedUIDGID() if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil { return nil, err } img, err := daemon.imageStore.Get(c.ImageID) if err != nil { return nil, fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err) } // In base spec s.Hostname = c.FullHostname() // In s.Mounts mounts, err := daemon.setupMounts(c) if err != nil { return nil, err } for _, mount := range mounts { s.Mounts = append(s.Mounts, windowsoci.Mount{ Source: mount.Source, Destination: mount.Destination, Readonly: !mount.Writable, }) } // In s.Process s.Process.Args = append([]string{c.Path}, c.Args...) if !c.Config.ArgsEscaped { s.Process.Args = escapeArgs(s.Process.Args) } s.Process.Cwd = c.Config.WorkingDir s.Process.Env = c.CreateDaemonEnvironment(linkedEnv) s.Process.InitialConsoleSize = c.HostConfig.ConsoleSize s.Process.Terminal = c.Config.Tty s.Process.User.User = c.Config.User // In spec.Root s.Root.Path = c.BaseFS s.Root.Readonly = c.HostConfig.ReadonlyRootfs // In s.Windows s.Windows.FirstStart = !c.HasBeenStartedBefore // s.Windows.LayerFolder. m, err := c.RWLayer.Metadata() if err != nil { return nil, fmt.Errorf("Failed to get layer metadata - %s", err) } s.Windows.LayerFolder = m["dir"] // s.Windows.LayerPaths var layerPaths []string if img.RootFS != nil && (img.RootFS.Type == image.TypeLayers || img.RootFS.Type == image.TypeLayersWithBase) { // Get the layer path for each layer. start := 1 if img.RootFS.Type == image.TypeLayersWithBase { // Include an empty slice to get the base layer ID. start = 0 } max := len(img.RootFS.DiffIDs) for i := start; i <= max; i++ { img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i] path, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID()) if err != nil { return nil, fmt.Errorf("Failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err) } // Reverse order, expecting parent most first layerPaths = append([]string{path}, layerPaths...) } } s.Windows.LayerPaths = layerPaths // Are we going to run as a Hyper-V container? hv := false if c.HostConfig.Isolation.IsDefault() { // Container is set to use the default, so take the default from the daemon configuration hv = daemon.defaultIsolation.IsHyperV() } else { // Container is requesting an isolation mode. Honour it. hv = c.HostConfig.Isolation.IsHyperV() } if hv { hvr := &windowsoci.HvRuntime{} if img.RootFS != nil && img.RootFS.Type == image.TypeLayers { // For TP5, the utility VM is part of the base layer. // TODO-jstarks: Add support for separate utility VM images // once it is decided how they can be stored. uvmpath := filepath.Join(layerPaths[len(layerPaths)-1], "UtilityVM") _, err = os.Stat(uvmpath) if err != nil { if os.IsNotExist(err) { err = errors.New("container image does not contain a utility VM") } return nil, err } hvr.ImagePath = uvmpath } s.Windows.HvRuntime = hvr } // In s.Windows.Networking // Connect all the libnetwork allocated networks to the container var epList []string if c.NetworkSettings != nil { for n := range c.NetworkSettings.Networks { sn, err := daemon.FindNetwork(n) if err != nil { continue } ep, err := c.GetEndpointInNetwork(sn) if err != nil { continue } data, err := ep.DriverInfo() if err != nil { continue } if data["hnsid"] != nil { epList = append(epList, data["hnsid"].(string)) } } } s.Windows.Networking = &windowsoci.Networking{ EndpointList: epList, } // In s.Windows.Resources // @darrenstahlmsft implement these resources cpuShares := uint64(c.HostConfig.CPUShares) s.Windows.Resources = &windowsoci.Resources{ CPU: &windowsoci.CPU{ //TODO Count: ..., //TODO Percent: ..., Shares: &cpuShares, }, Memory: &windowsoci.Memory{ //TODO Limit: ..., //TODO Reservation: ..., }, Network: &windowsoci.Network{ //TODO Bandwidth: ..., }, Storage: &windowsoci.Storage{ //TODO Bps: ..., //TODO Iops: ..., //TODO SandboxSize: ..., }, } return (*libcontainerd.Spec)(&s), nil }
func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libnetwork.Network) ([]libnetwork.SandboxOption, error) { var ( sboxOptions []libnetwork.SandboxOption err error dns []string dnsSearch []string dnsOptions []string ) sboxOptions = append(sboxOptions, libnetwork.OptionHostname(container.Config.Hostname), libnetwork.OptionDomainname(container.Config.Domainname)) if container.HostConfig.NetworkMode.IsHost() { sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox()) sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) } else if daemon.execDriver.SupportsHooks() { // OptionUseExternalKey is mandatory for userns support. // But optional for non-userns support sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey()) } container.HostsPath, err = container.GetRootResourcePath("hosts") if err != nil { return nil, err } sboxOptions = append(sboxOptions, libnetwork.OptionHostsPath(container.HostsPath)) container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf") if err != nil { return nil, err } sboxOptions = append(sboxOptions, libnetwork.OptionResolvConfPath(container.ResolvConfPath)) if len(container.HostConfig.DNS) > 0 { dns = container.HostConfig.DNS } else if len(daemon.configStore.DNS) > 0 { dns = daemon.configStore.DNS } for _, d := range dns { sboxOptions = append(sboxOptions, libnetwork.OptionDNS(d)) } if len(container.HostConfig.DNSSearch) > 0 { dnsSearch = container.HostConfig.DNSSearch } else if len(daemon.configStore.DNSSearch) > 0 { dnsSearch = daemon.configStore.DNSSearch } for _, ds := range dnsSearch { sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(ds)) } if len(container.HostConfig.DNSOptions) > 0 { dnsOptions = container.HostConfig.DNSOptions } else if len(daemon.configStore.DNSOptions) > 0 { dnsOptions = daemon.configStore.DNSOptions } for _, ds := range dnsOptions { sboxOptions = append(sboxOptions, libnetwork.OptionDNSOptions(ds)) } if container.NetworkSettings.SecondaryIPAddresses != nil { name := container.Config.Hostname if container.Config.Domainname != "" { name = name + "." + container.Config.Domainname } for _, a := range container.NetworkSettings.SecondaryIPAddresses { sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(name, a.Addr)) } } for _, extraHost := range container.HostConfig.ExtraHosts { // allow IPv6 addresses in extra hosts; only split on first ":" parts := strings.SplitN(extraHost, ":", 2) sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1])) } // Link feature is supported only for the default bridge network. // return if this call to build join options is not for default bridge network if n.Name() != "bridge" { return sboxOptions, nil } ep, _ := container.GetEndpointInNetwork(n) if ep == nil { return sboxOptions, nil } var childEndpoints, parentEndpoints []string children, err := daemon.children(container.Name) if err != nil { return nil, err } for linkAlias, child := range children { if !isLinkable(child) { return nil, fmt.Errorf("Cannot link to %s, as it does not belong to the default network", child.Name) } _, alias := path.Split(linkAlias) // allow access to the linked container via the alias, real name, and container hostname aliasList := alias + " " + child.Config.Hostname // only add the name if alias isn't equal to the name if alias != child.Name[1:] { aliasList = aliasList + " " + child.Name[1:] } sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.Networks["bridge"].IPAddress)) cEndpoint, _ := child.GetEndpointInNetwork(n) if cEndpoint != nil && cEndpoint.ID() != "" { childEndpoints = append(childEndpoints, cEndpoint.ID()) } } bridgeSettings := container.NetworkSettings.Networks["bridge"] refs := daemon.containerGraph().RefPaths(container.ID) for _, ref := range refs { if ref.ParentID == "0" { continue } c, err := daemon.GetContainer(ref.ParentID) if err != nil { logrus.Error(err) } if c != nil && !daemon.configStore.DisableBridge && container.HostConfig.NetworkMode.IsPrivate() { logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", c.ID, ref.Name, bridgeSettings.IPAddress) sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate(c.ID, ref.Name, bridgeSettings.IPAddress)) if ep.ID() != "" { parentEndpoints = append(parentEndpoints, ep.ID()) } } } linkOptions := options.Generic{ netlabel.GenericData: options.Generic{ "ParentEndpoints": parentEndpoints, "ChildEndpoints": childEndpoints, }, } sboxOptions = append(sboxOptions, libnetwork.OptionGeneric(linkOptions)) return sboxOptions, nil }
func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) (*[]libcontainerd.CreateOption, error) { createOptions := []libcontainerd.CreateOption{} // Are we going to run as a Hyper-V container? hvOpts := &libcontainerd.HyperVIsolationOption{} if container.HostConfig.Isolation.IsDefault() { // Container is set to use the default, so take the default from the daemon configuration hvOpts.IsHyperV = daemon.defaultIsolation.IsHyperV() } else { // Container is requesting an isolation mode. Honour it. hvOpts.IsHyperV = container.HostConfig.Isolation.IsHyperV() } // Generate the layer folder of the layer options layerOpts := &libcontainerd.LayerOption{} m, err := container.RWLayer.Metadata() if err != nil { return nil, fmt.Errorf("failed to get layer metadata - %s", err) } if hvOpts.IsHyperV { hvOpts.SandboxPath = filepath.Dir(m["dir"]) } layerOpts.LayerFolderPath = m["dir"] // Generate the layer paths of the layer options img, err := daemon.imageStore.Get(container.ImageID) if err != nil { return nil, fmt.Errorf("failed to graph.Get on ImageID %s - %s", container.ImageID, err) } // Get the layer path for each layer. max := len(img.RootFS.DiffIDs) for i := 1; i <= max; i++ { img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i] layerPath, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID()) if err != nil { return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err) } // Reverse order, expecting parent most first layerOpts.LayerPaths = append([]string{layerPath}, layerOpts.LayerPaths...) } // Get endpoints for the libnetwork allocated networks to the container var epList []string AllowUnqualifiedDNSQuery := false if container.NetworkSettings != nil { for n := range container.NetworkSettings.Networks { sn, err := daemon.FindNetwork(n) if err != nil { continue } ep, err := container.GetEndpointInNetwork(sn) if err != nil { continue } data, err := ep.DriverInfo() if err != nil { continue } if data["hnsid"] != nil { epList = append(epList, data["hnsid"].(string)) } if data["AllowUnqualifiedDNSQuery"] != nil { AllowUnqualifiedDNSQuery = true } } } // Now build the full set of options createOptions = append(createOptions, &libcontainerd.FlushOption{IgnoreFlushesDuringBoot: !container.HasBeenStartedBefore}) createOptions = append(createOptions, hvOpts) createOptions = append(createOptions, layerOpts) if epList != nil { createOptions = append(createOptions, &libcontainerd.NetworkEndpointsOption{Endpoints: epList, AllowUnqualifiedDNSQuery: AllowUnqualifiedDNSQuery}) } return &createOptions, nil }
func (daemon *Daemon) buildSandboxOptions(container *container.Container, n libnetwork.Network) ([]libnetwork.SandboxOption, error) { var ( sboxOptions []libnetwork.SandboxOption err error dns []string dnsSearch []string dnsOptions []string bindings = make(nat.PortMap) pbList []types.PortBinding exposeList []types.TransportPort ) defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName() sboxOptions = append(sboxOptions, libnetwork.OptionHostname(container.Config.Hostname), libnetwork.OptionDomainname(container.Config.Domainname)) if container.HostConfig.NetworkMode.IsHost() { sboxOptions = append(sboxOptions, libnetwork.OptionUseDefaultSandbox()) sboxOptions = append(sboxOptions, libnetwork.OptionOriginHostsPath("/etc/hosts")) sboxOptions = append(sboxOptions, libnetwork.OptionOriginResolvConfPath("/etc/resolv.conf")) } else if daemon.execDriver.SupportsHooks() { // OptionUseExternalKey is mandatory for userns support. // But optional for non-userns support sboxOptions = append(sboxOptions, libnetwork.OptionUseExternalKey()) } container.HostsPath, err = container.GetRootResourcePath("hosts") if err != nil { return nil, err } sboxOptions = append(sboxOptions, libnetwork.OptionHostsPath(container.HostsPath)) container.ResolvConfPath, err = container.GetRootResourcePath("resolv.conf") if err != nil { return nil, err } sboxOptions = append(sboxOptions, libnetwork.OptionResolvConfPath(container.ResolvConfPath)) if len(container.HostConfig.DNS) > 0 { dns = container.HostConfig.DNS } else if len(daemon.configStore.DNS) > 0 { dns = daemon.configStore.DNS } for _, d := range dns { sboxOptions = append(sboxOptions, libnetwork.OptionDNS(d)) } if len(container.HostConfig.DNSSearch) > 0 { dnsSearch = container.HostConfig.DNSSearch } else if len(daemon.configStore.DNSSearch) > 0 { dnsSearch = daemon.configStore.DNSSearch } for _, ds := range dnsSearch { sboxOptions = append(sboxOptions, libnetwork.OptionDNSSearch(ds)) } if len(container.HostConfig.DNSOptions) > 0 { dnsOptions = container.HostConfig.DNSOptions } else if len(daemon.configStore.DNSOptions) > 0 { dnsOptions = daemon.configStore.DNSOptions } for _, ds := range dnsOptions { sboxOptions = append(sboxOptions, libnetwork.OptionDNSOptions(ds)) } if container.NetworkSettings.SecondaryIPAddresses != nil { name := container.Config.Hostname if container.Config.Domainname != "" { name = name + "." + container.Config.Domainname } for _, a := range container.NetworkSettings.SecondaryIPAddresses { sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(name, a.Addr)) } } for _, extraHost := range container.HostConfig.ExtraHosts { // allow IPv6 addresses in extra hosts; only split on first ":" parts := strings.SplitN(extraHost, ":", 2) sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(parts[0], parts[1])) } if container.HostConfig.PortBindings != nil { for p, b := range container.HostConfig.PortBindings { bindings[p] = []nat.PortBinding{} for _, bb := range b { bindings[p] = append(bindings[p], nat.PortBinding{ HostIP: bb.HostIP, HostPort: bb.HostPort, }) } } } portSpecs := container.Config.ExposedPorts ports := make([]nat.Port, len(portSpecs)) var i int for p := range portSpecs { ports[i] = p i++ } nat.SortPortMap(ports, bindings) for _, port := range ports { expose := types.TransportPort{} expose.Proto = types.ParseProtocol(port.Proto()) expose.Port = uint16(port.Int()) exposeList = append(exposeList, expose) pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto} binding := bindings[port] for i := 0; i < len(binding); i++ { pbCopy := pb.GetCopy() newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort)) var portStart, portEnd int if err == nil { portStart, portEnd, err = newP.Range() } if err != nil { return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding[i].HostPort, err) } pbCopy.HostPort = uint16(portStart) pbCopy.HostPortEnd = uint16(portEnd) pbCopy.HostIP = net.ParseIP(binding[i].HostIP) pbList = append(pbList, pbCopy) } if container.HostConfig.PublishAllPorts && len(binding) == 0 { pbList = append(pbList, pb) } } sboxOptions = append(sboxOptions, libnetwork.OptionPortMapping(pbList), libnetwork.OptionExposedPorts(exposeList)) // Link feature is supported only for the default bridge network. // return if this call to build join options is not for default bridge network if n.Name() != defaultNetName { return sboxOptions, nil } ep, _ := container.GetEndpointInNetwork(n) if ep == nil { return sboxOptions, nil } var childEndpoints, parentEndpoints []string children := daemon.children(container) for linkAlias, child := range children { if !isLinkable(child) { return nil, fmt.Errorf("Cannot link to %s, as it does not belong to the default network", child.Name) } _, alias := path.Split(linkAlias) // allow access to the linked container via the alias, real name, and container hostname aliasList := alias + " " + child.Config.Hostname // only add the name if alias isn't equal to the name if alias != child.Name[1:] { aliasList = aliasList + " " + child.Name[1:] } sboxOptions = append(sboxOptions, libnetwork.OptionExtraHost(aliasList, child.NetworkSettings.Networks[defaultNetName].IPAddress)) cEndpoint, _ := child.GetEndpointInNetwork(n) if cEndpoint != nil && cEndpoint.ID() != "" { childEndpoints = append(childEndpoints, cEndpoint.ID()) } } bridgeSettings := container.NetworkSettings.Networks[defaultNetName] for alias, parent := range daemon.parents(container) { if daemon.configStore.DisableBridge || !container.HostConfig.NetworkMode.IsPrivate() { continue } _, alias = path.Split(alias) logrus.Debugf("Update /etc/hosts of %s for alias %s with ip %s", parent.ID, alias, bridgeSettings.IPAddress) sboxOptions = append(sboxOptions, libnetwork.OptionParentUpdate( parent.ID, alias, bridgeSettings.IPAddress, )) if ep.ID() != "" { parentEndpoints = append(parentEndpoints, ep.ID()) } } linkOptions := options.Generic{ netlabel.GenericData: options.Generic{ "ParentEndpoints": parentEndpoints, "ChildEndpoints": childEndpoints, }, } sboxOptions = append(sboxOptions, libnetwork.OptionGeneric(linkOptions)) return sboxOptions, nil }
func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName string, updateSettings bool) (err error) { if container.HostConfig.NetworkMode.IsContainer() { return runconfig.ErrConflictSharedNetwork } if runconfig.NetworkMode(idOrName).IsBridge() && daemon.configStore.DisableBridge { container.Config.NetworkDisabled = true return nil } controller := daemon.netController n, err := daemon.FindNetwork(idOrName) if err != nil { return err } if updateSettings { if err := daemon.updateNetworkSettings(container, n); err != nil { return err } } ep, err := container.GetEndpointInNetwork(n) if err == nil { return fmt.Errorf("Conflict. A container with name %q is already connected to network %s.", strings.TrimPrefix(container.Name, "/"), idOrName) } if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok { return err } createOptions, err := container.BuildCreateEndpointOptions(n) if err != nil { return err } endpointName := strings.TrimPrefix(container.Name, "/") ep, err = n.CreateEndpoint(endpointName, createOptions...) if err != nil { return err } defer func() { if err != nil { if e := ep.Delete(); e != nil { logrus.Warnf("Could not rollback container connection to network %s", idOrName) } } }() if err := daemon.updateEndpointNetworkSettings(container, n, ep); err != nil { return err } sb := daemon.getNetworkSandbox(container) if sb == nil { options, err := daemon.buildSandboxOptions(container, n) if err != nil { return err } sb, err = controller.NewSandbox(container.ID, options...) if err != nil { return err } container.UpdateSandboxNetworkSettings(sb) } if err := ep.Join(sb); err != nil { return err } if err := container.UpdateJoinInfo(n, ep); err != nil { return derr.ErrorCodeJoinInfo.WithArgs(err) } return nil }
func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Container) ([]libcontainerd.CreateOption, error) { createOptions := []libcontainerd.CreateOption{} // Are we going to run as a Hyper-V container? hvOpts := &libcontainerd.HyperVIsolationOption{} if container.HostConfig.Isolation.IsDefault() { // Container is set to use the default, so take the default from the daemon configuration hvOpts.IsHyperV = daemon.defaultIsolation.IsHyperV() } else { // Container is requesting an isolation mode. Honour it. hvOpts.IsHyperV = container.HostConfig.Isolation.IsHyperV() } // Generate the layer folder of the layer options layerOpts := &libcontainerd.LayerOption{} m, err := container.RWLayer.Metadata() if err != nil { return nil, fmt.Errorf("failed to get layer metadata - %s", err) } if hvOpts.IsHyperV { hvOpts.SandboxPath = filepath.Dir(m["dir"]) } layerOpts.LayerFolderPath = m["dir"] // Generate the layer paths of the layer options img, err := daemon.imageStore.Get(container.ImageID) if err != nil { return nil, fmt.Errorf("failed to graph.Get on ImageID %s - %s", container.ImageID, err) } // Get the layer path for each layer. max := len(img.RootFS.DiffIDs) for i := 1; i <= max; i++ { img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i] layerPath, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID()) if err != nil { return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err) } // Reverse order, expecting parent most first layerOpts.LayerPaths = append([]string{layerPath}, layerOpts.LayerPaths...) } // Get endpoints for the libnetwork allocated networks to the container var epList []string AllowUnqualifiedDNSQuery := false gwHNSID := "" if container.NetworkSettings != nil { for n := range container.NetworkSettings.Networks { sn, err := daemon.FindNetwork(n) if err != nil { continue } ep, err := container.GetEndpointInNetwork(sn) if err != nil { continue } data, err := ep.DriverInfo() if err != nil { continue } if data["GW_INFO"] != nil { gwInfo := data["GW_INFO"].(map[string]interface{}) if gwInfo["hnsid"] != nil { gwHNSID = gwInfo["hnsid"].(string) } } if data["hnsid"] != nil { epList = append(epList, data["hnsid"].(string)) } if data["AllowUnqualifiedDNSQuery"] != nil { AllowUnqualifiedDNSQuery = true } } } if gwHNSID != "" { epList = append(epList, gwHNSID) } // Read and add credentials from the security options if a credential spec has been provided. if container.HostConfig.SecurityOpt != nil { for _, sOpt := range container.HostConfig.SecurityOpt { sOpt = strings.ToLower(sOpt) if !strings.Contains(sOpt, "=") { return nil, fmt.Errorf("invalid security option: no equals sign in supplied value %s", sOpt) } var splitsOpt []string splitsOpt = strings.SplitN(sOpt, "=", 2) if len(splitsOpt) != 2 { return nil, fmt.Errorf("invalid security option: %s", sOpt) } if splitsOpt[0] != "credentialspec" { return nil, fmt.Errorf("security option not supported: %s", splitsOpt[0]) } credentialsOpts := &libcontainerd.CredentialsOption{} var ( match bool csValue string err error ) if match, csValue = getCredentialSpec("file://", splitsOpt[1]); match { if csValue == "" { return nil, fmt.Errorf("no value supplied for file:// credential spec security option") } if credentialsOpts.Credentials, err = readCredentialSpecFile(container.ID, daemon.root, filepath.Clean(csValue)); err != nil { return nil, err } } else if match, csValue = getCredentialSpec("registry://", splitsOpt[1]); match { if csValue == "" { return nil, fmt.Errorf("no value supplied for registry:// credential spec security option") } if credentialsOpts.Credentials, err = readCredentialSpecRegistry(container.ID, csValue); err != nil { return nil, err } } else { return nil, fmt.Errorf("invalid credential spec security option - value must be prefixed file:// or registry:// followed by a value") } createOptions = append(createOptions, credentialsOpts) } } // Now add the remaining options. createOptions = append(createOptions, &libcontainerd.FlushOption{IgnoreFlushesDuringBoot: !container.HasBeenStartedBefore}) createOptions = append(createOptions, hvOpts) createOptions = append(createOptions, layerOpts) if epList != nil { createOptions = append(createOptions, &libcontainerd.NetworkEndpointsOption{Endpoints: epList, AllowUnqualifiedDNSQuery: AllowUnqualifiedDNSQuery}) } return createOptions, nil }
func (daemon *Daemon) populateCommand(c *container.Container, env []string) error { en := &execdriver.Network{ Interface: nil, } var epList []string // Connect all the libnetwork allocated networks to the container if c.NetworkSettings != nil { for n := range c.NetworkSettings.Networks { sn, err := daemon.FindNetwork(n) if err != nil { continue } ep, err := c.GetEndpointInNetwork(sn) if err != nil { continue } data, err := ep.DriverInfo() if err != nil { continue } if data["hnsid"] != nil { epList = append(epList, data["hnsid"].(string)) } } } if daemon.netController == nil { parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2) switch parts[0] { case "none": case "default", "": // empty string to support existing containers if !c.Config.NetworkDisabled { en.Interface = &execdriver.NetworkInterface{ MacAddress: c.Config.MacAddress, Bridge: daemon.configStore.bridgeConfig.Iface, PortBindings: c.HostConfig.PortBindings, // TODO Windows. Include IPAddress. There already is a // property IPAddress on execDrive.CommonNetworkInterface, // but there is no CLI option in docker to pass through // an IPAddress on docker run. } } default: return fmt.Errorf("invalid network mode: %s", c.HostConfig.NetworkMode) } } // TODO Windows. More resource controls to be implemented later. resources := &execdriver.Resources{ CommonResources: execdriver.CommonResources{ CPUShares: c.HostConfig.CPUShares, }, } processConfig := execdriver.ProcessConfig{ CommonProcessConfig: execdriver.CommonProcessConfig{ Entrypoint: c.Path, Arguments: c.Args, Tty: c.Config.Tty, }, ConsoleSize: c.HostConfig.ConsoleSize, } processConfig.Env = env var layerPaths []string img, err := daemon.imageStore.Get(c.ImageID) if err != nil { return fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err) } if img.RootFS != nil && img.RootFS.Type == "layers+base" { max := len(img.RootFS.DiffIDs) for i := 0; i <= max; i++ { img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i] path, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID()) if err != nil { return fmt.Errorf("Failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err) } // Reverse order, expecting parent most first layerPaths = append([]string{path}, layerPaths...) } } m, err := c.RWLayer.Metadata() if err != nil { return fmt.Errorf("Failed to get layer metadata - %s", err) } layerFolder := m["dir"] var hvPartition bool // Work out the isolation (whether it is a hypervisor partition) if c.HostConfig.Isolation.IsDefault() { // Not specified by caller. Take daemon default hvPartition = windows.DefaultIsolation.IsHyperV() } else { // Take value specified by caller hvPartition = c.HostConfig.Isolation.IsHyperV() } c.Command = &execdriver.Command{ CommonCommand: execdriver.CommonCommand{ ID: c.ID, Rootfs: c.BaseFS, WorkingDir: c.Config.WorkingDir, Network: en, MountLabel: c.GetMountLabel(), Resources: resources, ProcessConfig: processConfig, ProcessLabel: c.GetProcessLabel(), }, FirstStart: !c.HasBeenStartedBefore, LayerFolder: layerFolder, LayerPaths: layerPaths, Hostname: c.Config.Hostname, Isolation: string(c.HostConfig.Isolation), ArgsEscaped: c.Config.ArgsEscaped, HvPartition: hvPartition, EpList: epList, } return nil }
func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, error) { s := oci.DefaultSpec() linkedEnv, err := daemon.setupLinkedContainers(c) if err != nil { return nil, err } // TODO Windows - this can be removed. Not used (UID/GID) rootUID, rootGID := daemon.GetRemappedUIDGID() if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil { return nil, err } img, err := daemon.imageStore.Get(c.ImageID) if err != nil { return nil, fmt.Errorf("Failed to graph.Get on ImageID %s - %s", c.ImageID, err) } // In base spec s.Hostname = c.FullHostname() // In s.Mounts mounts, err := daemon.setupMounts(c) if err != nil { return nil, err } for _, mount := range mounts { s.Mounts = append(s.Mounts, windowsoci.Mount{ Source: mount.Source, Destination: mount.Destination, Readonly: !mount.Writable, }) } // Are we going to run as a Hyper-V container? hv := false if c.HostConfig.Isolation.IsDefault() { // Container is set to use the default, so take the default from the daemon configuration hv = daemon.defaultIsolation.IsHyperV() } else { // Container is requesting an isolation mode. Honour it. hv = c.HostConfig.Isolation.IsHyperV() } if hv { // TODO We don't yet have the ImagePath hooked up. But set to // something non-nil to pickup in libcontainerd. s.Windows.HvRuntime = &windowsoci.HvRuntime{} } // In s.Process if c.Config.ArgsEscaped { s.Process.Args = append([]string{c.Path}, c.Args...) } else { // TODO (jstarks): escape the entrypoint too once the tests are fixed to not rely on this behavior s.Process.Args = append([]string{c.Path}, escapeArgs(c.Args)...) } s.Process.Cwd = c.Config.WorkingDir s.Process.Env = c.CreateDaemonEnvironment(linkedEnv) s.Process.InitialConsoleSize = c.HostConfig.ConsoleSize s.Process.Terminal = c.Config.Tty s.Process.User.User = c.Config.User // In spec.Root s.Root.Path = c.BaseFS s.Root.Readonly = c.HostConfig.ReadonlyRootfs // In s.Windows s.Windows.FirstStart = !c.HasBeenStartedBefore // s.Windows.LayerFolder. m, err := c.RWLayer.Metadata() if err != nil { return nil, fmt.Errorf("Failed to get layer metadata - %s", err) } s.Windows.LayerFolder = m["dir"] // s.Windows.LayerPaths var layerPaths []string if img.RootFS != nil && img.RootFS.Type == "layers+base" { max := len(img.RootFS.DiffIDs) for i := 0; i <= max; i++ { img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i] path, err := layer.GetLayerPath(daemon.layerStore, img.RootFS.ChainID()) if err != nil { return nil, fmt.Errorf("Failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.layerStore, img.RootFS.ChainID(), err) } // Reverse order, expecting parent most first layerPaths = append([]string{path}, layerPaths...) } } s.Windows.LayerPaths = layerPaths // In s.Windows.Networking (TP5+ libnetwork way of doing things) // Connect all the libnetwork allocated networks to the container var epList []string if c.NetworkSettings != nil { for n := range c.NetworkSettings.Networks { sn, err := daemon.FindNetwork(n) if err != nil { continue } ep, err := c.GetEndpointInNetwork(sn) if err != nil { continue } data, err := ep.DriverInfo() if err != nil { continue } if data["hnsid"] != nil { epList = append(epList, data["hnsid"].(string)) } } } s.Windows.Networking = &windowsoci.Networking{ EndpointList: epList, } // In s.Windows.Networking (TP4 back compat) // TODO Windows: Post TP4 - Remove this along with definitions from spec // and changes to libcontainerd to not read these fields. if daemon.netController == nil { parts := strings.SplitN(string(c.HostConfig.NetworkMode), ":", 2) switch parts[0] { case "none": case "default", "": // empty string to support existing containers if !c.Config.NetworkDisabled { s.Windows.Networking = &windowsoci.Networking{ MacAddress: c.Config.MacAddress, Bridge: daemon.configStore.bridgeConfig.Iface, PortBindings: c.HostConfig.PortBindings, } } default: return nil, fmt.Errorf("invalid network mode: %s", c.HostConfig.NetworkMode) } } // In s.Windows.Resources // @darrenstahlmsft implement these resources cpuShares := uint64(c.HostConfig.CPUShares) s.Windows.Resources = &windowsoci.Resources{ CPU: &windowsoci.CPU{ //TODO Count: ..., //TODO Percent: ..., Shares: &cpuShares, }, Memory: &windowsoci.Memory{ //TODO Limit: ..., //TODO Reservation: ..., }, Network: &windowsoci.Network{ //TODO Bandwidth: ..., }, Storage: &windowsoci.Storage{ //TODO Bps: ..., //TODO Iops: ..., //TODO SandboxSize: ..., }, } return (*libcontainerd.Spec)(&s), nil }
func (daemon *Daemon) createSpec(c *container.Container) (*libcontainerd.Spec, error) { s := oci.DefaultSpec() linkedEnv, err := daemon.setupLinkedContainers(c) if err != nil { return nil, err } // TODO Windows - this can be removed. Not used (UID/GID) rootUID, rootGID := daemon.GetRemappedUIDGID() if err := c.SetupWorkingDirectory(rootUID, rootGID); err != nil { return nil, err } // In base spec s.Hostname = c.FullHostname() // In s.Mounts mounts, err := daemon.setupMounts(c) if err != nil { return nil, err } for _, mount := range mounts { m := windowsoci.Mount{ Source: mount.Source, Destination: mount.Destination, } if !mount.Writable { m.Options = append(m.Options, "ro") } s.Mounts = append(s.Mounts, m) } // In s.Process s.Process.Args = append([]string{c.Path}, c.Args...) if !c.Config.ArgsEscaped { s.Process.Args = escapeArgs(s.Process.Args) } s.Process.Cwd = c.Config.WorkingDir if len(s.Process.Cwd) == 0 { // We default to C:\ to workaround the oddity of the case that the // default directory for cmd running as LocalSystem (or // ContainerAdministrator) is c:\windows\system32. Hence docker run // <image> cmd will by default end in c:\windows\system32, rather // than 'root' (/) on Linux. The oddity is that if you have a dockerfile // which has no WORKDIR and has a COPY file ., . will be interpreted // as c:\. Hence, setting it to default of c:\ makes for consistency. s.Process.Cwd = `C:\` } s.Process.Env = c.CreateDaemonEnvironment(linkedEnv) s.Process.ConsoleSize.Height = c.HostConfig.ConsoleSize[0] s.Process.ConsoleSize.Width = c.HostConfig.ConsoleSize[1] s.Process.Terminal = c.Config.Tty s.Process.User.Username = c.Config.User // In spec.Root s.Root.Path = c.BaseFS s.Root.Readonly = c.HostConfig.ReadonlyRootfs // In s.Windows.Networking // Connect all the libnetwork allocated networks to the container var epList []string if c.NetworkSettings != nil { for n := range c.NetworkSettings.Networks { sn, err := daemon.FindNetwork(n) if err != nil { continue } ep, err := c.GetEndpointInNetwork(sn) if err != nil { continue } data, err := ep.DriverInfo() if err != nil { continue } if data["hnsid"] != nil { epList = append(epList, data["hnsid"].(string)) } } } s.Windows.Networking = &windowsoci.WindowsNetworking{ EndpointList: epList, } // In s.Windows.Resources // @darrenstahlmsft implement these resources cpuShares := uint64(c.HostConfig.CPUShares) s.Windows.Resources = &windowsoci.WindowsResources{ CPU: &windowsoci.WindowsCPU{ Percent: &c.HostConfig.CPUPercent, Shares: &cpuShares, }, Memory: &windowsoci.WindowsMemory{ Limit: &c.HostConfig.Memory, //TODO Reservation: ..., }, Network: &windowsoci.WindowsNetwork{ //TODO Bandwidth: ..., }, Storage: &windowsoci.WindowsStorage{ Bps: &c.HostConfig.IOMaximumBandwidth, Iops: &c.HostConfig.IOMaximumIOps, }, } return (*libcontainerd.Spec)(&s), nil }