in cmd/runhcs/container.go [256:457]
func createContainer(cfg *containerConfig) (_ *container, err error) {
// Store the container information in a volatile registry key.
cwd, err := os.Getwd()
if err != nil {
return nil, err
}
vmisolated := cfg.Spec.Linux != nil || (cfg.Spec.Windows != nil && cfg.Spec.Windows.HyperV != nil)
sandboxID, isSandbox := parseSandboxAnnotations(cfg.Spec.Annotations)
hostID := cfg.HostID
if isSandbox {
if sandboxID != cfg.ID {
return nil, errors.New("sandbox ID must match ID")
}
} else if sandboxID != "" {
// Validate that the sandbox container exists.
sandbox, err := getContainer(sandboxID, false)
if err != nil {
return nil, err
}
defer sandbox.Close()
if sandbox.SandboxID != sandboxID {
return nil, fmt.Errorf("container %s is not a sandbox", sandboxID)
}
if hostID == "" {
// Use the sandbox's host.
hostID = sandbox.HostID
} else if sandbox.HostID == "" {
return nil, fmt.Errorf("sandbox container %s is not running in a VM host, but host %s was specified", sandboxID, hostID)
} else if hostID != sandbox.HostID {
return nil, fmt.Errorf("sandbox container %s has a different host %s from the requested host %s", sandboxID, sandbox.HostID, hostID)
}
if vmisolated && hostID == "" {
return nil, fmt.Errorf("container %s is not a VM isolated sandbox", sandboxID)
}
}
uniqueID, err := guid.NewV4()
if err != nil {
return nil, err
}
newvm := false
var hostUniqueID guid.GUID
if hostID != "" {
host, err := getContainer(hostID, false)
if err != nil {
return nil, err
}
defer host.Close()
if !host.IsHost {
return nil, fmt.Errorf("host container %s is not a VM host", hostID)
}
hostUniqueID = host.UniqueID
} else if vmisolated && (isSandbox || cfg.Spec.Linux != nil || osversion.Build() >= osversion.RS5) {
// This handles all LCOW, Pod Sandbox, and (Windows Xenon V2 for RS5+)
hostID = cfg.ID
newvm = true
hostUniqueID = uniqueID
}
// Make absolute the paths in Root.Path and Windows.LayerFolders.
rootfs := ""
if cfg.Spec.Root != nil {
rootfs = cfg.Spec.Root.Path
if rootfs != "" && !filepath.IsAbs(rootfs) && !strings.HasPrefix(rootfs, `\\?\`) {
rootfs = filepath.Join(cwd, rootfs)
cfg.Spec.Root.Path = rootfs
}
}
netNS := ""
if cfg.Spec.Windows != nil {
for i, f := range cfg.Spec.Windows.LayerFolders {
if !filepath.IsAbs(f) && !strings.HasPrefix(rootfs, `\\?\`) {
cfg.Spec.Windows.LayerFolders[i] = filepath.Join(cwd, f)
}
}
// Determine the network namespace to use.
if cfg.Spec.Windows.Network != nil {
if cfg.Spec.Windows.Network.NetworkSharedContainerName != "" {
// RS4 case
err = stateKey.Get(cfg.Spec.Windows.Network.NetworkSharedContainerName, keyNetNS, &netNS)
if err != nil {
if _, ok := err.(*regstate.NoStateError); !ok {
return nil, err
}
}
} else if cfg.Spec.Windows.Network.NetworkNamespace != "" {
// RS5 case
netNS = cfg.Spec.Windows.Network.NetworkNamespace
}
}
}
// Store the initial container state in the registry so that the delete
// command can clean everything up if something goes wrong.
c := &container{
persistedState: persistedState{
ID: cfg.ID,
Owner: cfg.Owner,
Bundle: cwd,
Rootfs: rootfs,
Created: time.Now(),
Spec: cfg.Spec,
SandboxID: sandboxID,
HostID: hostID,
IsHost: newvm,
RequestedNetNS: netNS,
UniqueID: uniqueID,
HostUniqueID: hostUniqueID,
},
}
err = stateKey.Create(cfg.ID, keyState, &c.persistedState)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
_ = c.Remove()
}
}()
if isSandbox && vmisolated {
cnicfg := cni.NewPersistedNamespaceConfig(netNS, cfg.ID, hostUniqueID)
err = cnicfg.Store()
if err != nil {
return nil, err
}
defer func() {
if err != nil {
_ = cnicfg.Remove()
}
}()
}
// Start a VM if necessary.
if newvm {
opts, err := oci.SpecToUVMCreateOpts(context.Background(), cfg.Spec, vmID(c.ID), cfg.Owner)
if err != nil {
return nil, err
}
switch opts := opts.(type) {
case *uvm.OptionsLCOW:
opts.ConsolePipe = cfg.VMConsolePipe
case *uvm.OptionsWCOW:
// In order for the UVM sandbox.vhdx not to collide with the actual
// nested Argon sandbox.vhdx we append the \vm folder to the last entry
// in the list.
layersLen := len(cfg.Spec.Windows.LayerFolders)
layers := make([]string, layersLen)
copy(layers, cfg.Spec.Windows.LayerFolders)
vmPath := filepath.Join(layers[layersLen-1], "vm")
err := os.MkdirAll(vmPath, 0)
if err != nil {
return nil, err
}
layers[layersLen-1] = vmPath
opts.LayerFolders = layers
}
shim, err := c.startVMShim(cfg.VMLogFile, opts)
if err != nil {
return nil, err
}
_ = shim.Release()
}
if c.HostID != "" {
// Call to the VM shim process to create the container. This is done so
// that the VM process can keep track of the VM's virtual hardware
// resource use.
err = c.issueVMRequest(runhcs.OpCreateContainer)
if err != nil {
return nil, err
}
c.hc, err = hcs.OpenComputeSystem(context.Background(), cfg.ID)
if err != nil {
return nil, err
}
} else {
// Create the container directly from this process.
err = createContainerInHost(c, nil)
if err != nil {
return nil, err
}
}
// Create the shim process for the container.
err = startContainerShim(c, cfg.PidFile, cfg.ShimLogFile)
if err != nil {
if e := c.Kill(); e == nil {
_ = c.Remove()
}
return nil, err
}
return c, nil
}