From 0bd1cf25175c19e7fffc70b31cd7565bc432df19 Mon Sep 17 00:00:00 2001 From: John Howard Date: Tue, 18 Jul 2017 18:58:39 -0700 Subject: [PATCH] Revendor jhowardmsft/opengcs @ v0.0.12 Signed-off-by: John Howard --- vendor.conf | 2 +- .../opengcs/gogcs/client/config.go | 200 ++++++++++-------- .../opengcs/gogcs/client/createext4vhdx.go | 165 +++++++++++++++ .../opengcs/gogcs/client/createsandbox.go | 67 ------ .../opengcs/gogcs/client/hotaddvhd.go | 9 +- .../opengcs/gogcs/client/hotremovevhd.go | 2 +- .../opengcs/gogcs/client/process.go | 40 ++-- .../opengcs/gogcs/client/tartovhd.go | 8 +- 8 files changed, 314 insertions(+), 179 deletions(-) create mode 100644 vendor/github.com/jhowardmsft/opengcs/gogcs/client/createext4vhdx.go delete mode 100644 vendor/github.com/jhowardmsft/opengcs/gogcs/client/createsandbox.go diff --git a/vendor.conf b/vendor.conf index 763ef84b51..ac33fdaa45 100644 --- a/vendor.conf +++ b/vendor.conf @@ -8,7 +8,7 @@ github.com/docker/libtrust 9cbd2a1374f46905c68a4eb3694a130610adc62a github.com/go-check/check 4ed411733c5785b40214c70bce814c3a3a689609 https://github.com/cpuguy83/check.git github.com/gorilla/context v1.1 github.com/gorilla/mux v1.1 -github.com/jhowardmsft/opengcs b9d0120d36f26e981a50bf18bac1bb3f0c2b8fef https://github.com/dmcgowan/opengcs.git +github.com/jhowardmsft/opengcs v0.0.12 github.com/kr/pty 5cf931ef8f github.com/mattn/go-shellwords v1.0.3 github.com/sirupsen/logrus v1.0.1 diff --git a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/config.go b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/config.go index 1c52ce6fe4..141b77f200 100644 --- a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/config.go +++ b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/config.go @@ -39,21 +39,20 @@ const ( // defaultUvmTimeoutSeconds is the default time to wait for utility VM operations defaultUvmTimeoutSeconds = 5 * 60 - // DefaultSandboxSizeMB is the size of the default sandbox size in MB - DefaultSandboxSizeMB = 20 * 1024 * 1024 + // DefaultVhdxSizeGB is the size of the default sandbox & scratch in GB + DefaultVhdxSizeGB = 20 + + // defaultVhdxBlockSizeMB is the block-size for the sandbox/scratch VHDx's this package can create. + defaultVhdxBlockSizeMB = 1 ) -// Config is the structure used to configuring a utility VM to be used -// as a service VM. There are two ways of starting. Either supply a VHD, -// or a Kernel+Initrd. For the latter, both must be supplied, and both -// must be in the same directory. +// Config is the structure used to configuring a utility VM. There are two ways +// of starting. Either supply a VHD, or a Kernel+Initrd. For the latter, both +// must be supplied, and both must be in the same directory. // // VHD is the priority. type Config struct { - KirdPath string // Path to where kernel/initrd are found (defaults to c:\program files\Linux Containers) - KernelFile string // Kernel for Utility VM (embedded in a UEFI bootloader) - does NOT include full path, just filename - InitrdFile string // Initrd image for Utility VM - does NOT include full path, just filename - Vhdx string // VHD for booting the utility VM - is a full path + Options // Configuration options Name string // Name of the utility VM RequestedMode Mode // What mode is preferred when validating ActualMode Mode // What mode was obtained during validation @@ -62,105 +61,129 @@ type Config struct { MappedVirtualDisks []hcsshim.MappedVirtualDisk // Data-disks to be attached } -// GenerateDefault generates a default config from a set of options -// If baseDir is not supplied, defaults to $env:ProgramFiles\Linux Containers -func (config *Config) GenerateDefault(options []string) error { - if config.UvmTimeoutSeconds < 0 { - return fmt.Errorf("opengcs: cannot generate a config when supplied a negative utility VM timeout") - } - - envTimeoutSeconds := 0 - optTimeoutSeconds := 0 - - if config.UvmTimeoutSeconds != 0 { - envTimeout := os.Getenv("OPENGCS_UVM_TIMEOUT_SECONDS") - if len(envTimeout) > 0 { - var err error - if envTimeoutSeconds, err = strconv.Atoi(envTimeout); err != nil { - return fmt.Errorf("opengcs: OPENGCS_UVM_TIMEOUT_SECONDS could not be interpreted as an integer") - } - if envTimeoutSeconds < 0 { - return fmt.Errorf("opengcs: OPENGCS_UVM_TIMEOUT_SECONDS cannot be negative") - } - } - } +// Options is the structure used by a client to define configurable options for a utility VM. +type Options struct { + KirdPath string // Path to where kernel/initrd are found (defaults to %PROGRAMFILES%\Linux Containers) + KernelFile string // Kernel for Utility VM (embedded in a UEFI bootloader) - does NOT include full path, just filename + InitrdFile string // Initrd image for Utility VM - does NOT include full path, just filename + Vhdx string // VHD for booting the utility VM - is a full path + TimeoutSeconds int // Requested time for the utility VM to respond in seconds (may be over-ridden by environment) + BootParameters string // Additional boot parameters for initrd booting (not VHDx) +} +// ParseOptions parses a set of K-V pairs into options used by opengcs. Note +// for consistency with the LCOW graphdriver in docker, we keep the same +// convention of an `lcow.` prefix. +func ParseOptions(options []string) (Options, error) { + rOpts := Options{TimeoutSeconds: 0} for _, v := range options { opt := strings.SplitN(v, "=", 2) if len(opt) == 2 { switch strings.ToLower(opt[0]) { - case "opengcskirdpath": - config.KirdPath = opt[1] - case "opengcskernel": - config.KernelFile = opt[1] - case "opengcsinitrd": - config.InitrdFile = opt[1] - case "opengcsvhdx": - config.Vhdx = opt[1] - case "opengcstimeoutsecs": + case "lcow.kirdpath": + rOpts.KirdPath = opt[1] + case "lcow.kernel": + rOpts.KernelFile = opt[1] + case "lcow.initrd": + rOpts.InitrdFile = opt[1] + case "lcow.vhdx": + rOpts.Vhdx = opt[1] + case "lcow.bootparameters": + rOpts.BootParameters = opt[1] + case "lcow.timeout": var err error - if optTimeoutSeconds, err = strconv.Atoi(opt[1]); err != nil { - return fmt.Errorf("opengcs: opengcstimeoutsecs option could not be interpreted as an integer") + if rOpts.TimeoutSeconds, err = strconv.Atoi(opt[1]); err != nil { + return rOpts, fmt.Errorf("opengcstimeoutsecs option could not be interpreted as an integer") } - if optTimeoutSeconds < 0 { - return fmt.Errorf("opengcs: opengcstimeoutsecs option cannot be negative") + if rOpts.TimeoutSeconds < 0 { + return rOpts, fmt.Errorf("opengcstimeoutsecs option cannot be negative") } } } } - if config.KirdPath == "" { - config.KirdPath = filepath.Join(os.Getenv("ProgramFiles"), "Linux Containers") + // Set default values if not supplied + if rOpts.KirdPath == "" { + rOpts.KirdPath = filepath.Join(os.Getenv("ProgramFiles"), "Linux Containers") + } + if rOpts.Vhdx == "" { + rOpts.Vhdx = filepath.Join(rOpts.KirdPath, `uvm.vhdx`) + } + if rOpts.KernelFile == "" { + rOpts.KernelFile = `bootx64.efi` + } + if rOpts.InitrdFile == "" { + rOpts.InitrdFile = `initrd.img` } - if config.Vhdx == "" { - config.Vhdx = filepath.Join(config.KirdPath, `uvm.vhdx`) - } - if config.KernelFile == "" { - config.KernelFile = `bootx64.efi` - } - if config.InitrdFile == "" { - config.InitrdFile = `initrd.img` + return rOpts, nil +} + +// GenerateDefault generates a default config from a set of options +// If baseDir is not supplied, defaults to $env:ProgramFiles\Linux Containers +func (config *Config) GenerateDefault(options []string) error { + // Parse the options that the user supplied. + var err error + config.Options, err = ParseOptions(options) + if err != nil { + return err } - // Which timeout are we going to take? If not through option or environment, - // then use the default constant, otherwise the maximum of the option or - // environment supplied setting. A requested on in the config supplied - // overrides all of this. - if config.UvmTimeoutSeconds == 0 { - config.UvmTimeoutSeconds = defaultUvmTimeoutSeconds - if optTimeoutSeconds != 0 || envTimeoutSeconds != 0 { - config.UvmTimeoutSeconds = optTimeoutSeconds - if envTimeoutSeconds > optTimeoutSeconds { - config.UvmTimeoutSeconds = envTimeoutSeconds - } + // Get the timeout from the environment + envTimeoutSeconds := 0 + envTimeout := os.Getenv("OPENGCS_UVM_TIMEOUT_SECONDS") + if len(envTimeout) > 0 { + var err error + if envTimeoutSeconds, err = strconv.Atoi(envTimeout); err != nil { + return fmt.Errorf("OPENGCS_UVM_TIMEOUT_SECONDS could not be interpreted as an integer") + } + if envTimeoutSeconds < 0 { + return fmt.Errorf("OPENGCS_UVM_TIMEOUT_SECONDS cannot be negative") } } - config.MappedVirtualDisks = nil + // Priority to the requested timeout from the options. + if config.TimeoutSeconds != 0 { + config.UvmTimeoutSeconds = config.TimeoutSeconds + return nil + } + + // Next priority, the environment + if envTimeoutSeconds != 0 { + config.UvmTimeoutSeconds = envTimeoutSeconds + return nil + } + + // Last priority is the default timeout + config.UvmTimeoutSeconds = defaultUvmTimeoutSeconds return nil } -// validate validates a Config structure for starting a utility VM. -func (config *Config) validate() error { +// Validate validates a Config structure for starting a utility VM. +func (config *Config) Validate() error { config.ActualMode = ModeActualError if config.RequestedMode == ModeRequestVhdx && config.Vhdx == "" { - return fmt.Errorf("opengcs: config is invalid - request for VHDX mode did not supply a VHDX") + return fmt.Errorf("VHDx mode must supply a VHDx") } if config.RequestedMode == ModeRequestKernelInitrd && (config.KernelFile == "" || config.InitrdFile == "") { - return fmt.Errorf("opengcs: config is invalid - request for Kernel+Initrd mode must supply both kernel and initrd") + return fmt.Errorf("kernel+initrd mode must supply both kernel and initrd") } // Validate that if VHDX requested or auto, it exists. if config.RequestedMode == ModeRequestAuto || config.RequestedMode == ModeRequestVhdx { if _, err := os.Stat(config.Vhdx); os.IsNotExist(err) { if config.RequestedMode == ModeRequestVhdx { - return fmt.Errorf("opengcs: mode requested was VHDX but '%s' could not be found", config.Vhdx) + return fmt.Errorf("VHDx '%s' not found", config.Vhdx) } } else { config.ActualMode = ModeActualVhdx + + // Can't specify boot parameters with VHDx + if config.BootParameters != "" { + return fmt.Errorf("Boot parameters cannot be specified in VHDx mode") + } return nil } } @@ -168,16 +191,16 @@ func (config *Config) validate() error { // So must be kernel+initrd, or auto where we fallback as the VHDX doesn't exist if config.InitrdFile == "" || config.KernelFile == "" { if config.RequestedMode == ModeRequestKernelInitrd { - return fmt.Errorf("opengcs: both initrd and kernel options for utility VM boot must be supplied") + return fmt.Errorf("initrd and kernel options must be supplied") } return fmt.Errorf("opengcs: configuration is invalid") } if _, err := os.Stat(filepath.Join(config.KirdPath, config.KernelFile)); os.IsNotExist(err) { - return fmt.Errorf("opengcs: kernel '%s' was not found", filepath.Join(config.KirdPath, config.KernelFile)) + return fmt.Errorf("kernel '%s' not found", filepath.Join(config.KirdPath, config.KernelFile)) } if _, err := os.Stat(filepath.Join(config.KirdPath, config.InitrdFile)); os.IsNotExist(err) { - return fmt.Errorf("opengcs: initrd '%s' was not found", filepath.Join(config.KirdPath, config.InitrdFile)) + return fmt.Errorf("initrd '%s' not found", filepath.Join(config.KirdPath, config.InitrdFile)) } config.ActualMode = ModeActualKernelInitrd @@ -185,21 +208,21 @@ func (config *Config) validate() error { // Ensure all the MappedVirtualDisks exist on the host for _, mvd := range config.MappedVirtualDisks { if _, err := os.Stat(mvd.HostPath); err != nil { - return fmt.Errorf("opengcs: MappedVirtualDisk '%s' was not found", mvd.HostPath) + return fmt.Errorf("mapped virtual disk '%s' not found", mvd.HostPath) } if mvd.ContainerPath == "" { - return fmt.Errorf("opengcs: MappedVirtualDisk '%s' has no container path", mvd.HostPath) + return fmt.Errorf("mapped virtual disk '%s' requested without a container path", mvd.HostPath) } } return nil } -// Create creates a utility VM from a configuration. -func (config *Config) Create() error { - logrus.Debugf("opengcs Create: %+v", config) +// StartUtilityVM creates and starts a utility VM from a configuration. +func (config *Config) StartUtilityVM() error { + logrus.Debugf("opengcs: StartUtilityVM: %+v", config) - if err := config.validate(); err != nil { + if err := config.Validate(); err != nil { return err } @@ -218,28 +241,29 @@ func (config *Config) Create() error { } } else { configuration.HvRuntime = &hcsshim.HvRuntime{ - ImagePath: config.KirdPath, - LinuxInitrdFile: config.InitrdFile, - LinuxKernelFile: config.KernelFile, + ImagePath: config.KirdPath, + LinuxInitrdFile: config.InitrdFile, + LinuxKernelFile: config.KernelFile, + LinuxBootParameters: config.BootParameters, } } configurationS, _ := json.Marshal(configuration) - logrus.Debugf("opengcs Create: calling HCS with '%s'", string(configurationS)) + logrus.Debugf("opengcs: StartUtilityVM: calling HCS with '%s'", string(configurationS)) uvm, err := hcsshim.CreateContainer(config.Name, configuration) if err != nil { return err } - logrus.Debugf("opengcs Create: uvm created, starting...") + logrus.Debugf("opengcs: StartUtilityVM: uvm created, starting...") err = uvm.Start() if err != nil { - logrus.Debugf("opengcs Create: uvm failed to start: %s", err) + logrus.Debugf("opengcs: StartUtilityVM: uvm failed to start: %s", err) // Make sure we don't leave it laying around as it's been created in HCS uvm.Terminate() return err } config.Uvm = uvm - logrus.Debugf("opengcs Create: uvm %s is running", config.Name) + logrus.Debugf("opengcs StartUtilityVM: uvm %s is running", config.Name) return nil } diff --git a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/createext4vhdx.go b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/createext4vhdx.go new file mode 100644 index 0000000000..b53ce25149 --- /dev/null +++ b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/createext4vhdx.go @@ -0,0 +1,165 @@ +// +build windows + +package client + +import ( + "bytes" + "fmt" + "os" + "strings" + "time" + + winio "github.com/Microsoft/go-winio/vhd" + // "github.com/Microsoft/hcsshim" + "github.com/sirupsen/logrus" +) + +// dismount is a simple utility function wrapping a conditional HotRemove. It would +// have been easier if you could cancel a deferred function, but this works just +// as well. +func (config *Config) dismount(file string) error { + logrus.Debugf("opengcs: CreateExt4Vhdx: hot-remove of %s", file) + err := config.HotRemoveVhd(file) + if err != nil { + logrus.Warnf("failed to hot-remove: %s", err) + } + return err +} + +// CreateExt4Vhdx does what it says on the tin. It is the responsibility of the caller to synchronise +// simultaneous attempts to create the cache file. +func (config *Config) CreateExt4Vhdx(destFile string, sizeGB uint32, cacheFile string) error { + // Smallest we can accept is the default sandbox size as we can't size down, only expand. + if sizeGB < DefaultVhdxSizeGB { + sizeGB = DefaultVhdxSizeGB + } + + logrus.Debugf("opengcs: CreateExt4Vhdx: %s size:%dGB cache:%s", destFile, sizeGB, cacheFile) + + // Retrieve from cache if the default size and already on disk + if cacheFile != "" && sizeGB == DefaultVhdxSizeGB { + if _, err := os.Stat(cacheFile); err == nil { + if err := CopyFile(cacheFile, destFile, false); err != nil { + return fmt.Errorf("failed to copy cached file '%s' to '%s': %s", cacheFile, destFile, err) + } + logrus.Debugf("opengcs: CreateExt4Vhdx: %s fulfilled from cache", destFile) + return nil + } + } + + // Must have a utility VM to operate on + if config.Uvm == nil { + return fmt.Errorf("no utility VM") + } + + // Create the VHDX + if err := winio.CreateVhdx(destFile, sizeGB, defaultVhdxBlockSizeMB); err != nil { + return fmt.Errorf("failed to create VHDx %s: %s", destFile, err) + } + + // Attach it to the utility VM, but don't mount it (as there's no filesystem on it) + if err := config.HotAddVhd(destFile, "", false, false); err != nil { + return fmt.Errorf("opengcs: CreateExt4Vhdx: failed to hot-add %s to utility VM: %s", cacheFile, err) + } + + // Get the list of mapped virtual disks to find the controller and LUN IDs + logrus.Debugf("opengcs: CreateExt4Vhdx: %s querying mapped virtual disks", destFile) + mvdControllers, err := config.Uvm.MappedVirtualDisks() + if err != nil { + return fmt.Errorf("failed to get mapped virtual disks: %s", err) + } + + // Find our mapped disk from the list of all currently added. + controller := -1 + lun := -1 + for controllerNumber, controllerElement := range mvdControllers { + for diskNumber, diskElement := range controllerElement.MappedVirtualDisks { + if diskElement.HostPath == destFile { + controller = controllerNumber + lun = diskNumber + break + } + } + } + if controller == -1 || lun == -1 { + config.dismount(destFile) + return fmt.Errorf("failed to find %s in mapped virtual disks after hot-adding", destFile) + } + logrus.Debugf("opengcs: CreateExt4Vhdx: %s at C=%d L=%d", destFile, controller, lun) + + // Validate /sys/bus/scsi/devices/C:0:0:L exists as a directory + testdCommand := fmt.Sprintf(`test -d /sys/bus/scsi/devices/%d:0:0:%d`, controller, lun) + testdProc, err := config.RunProcess(testdCommand, nil, nil, nil) + if err != nil { + config.dismount(destFile) + return fmt.Errorf("failed to `%s` following hot-add %s to utility VM: %s", testdCommand, destFile, err) + } + defer testdProc.Close() + testdProc.WaitTimeout(time.Duration(int(time.Second) * config.UvmTimeoutSeconds)) + testdExitCode, err := testdProc.ExitCode() + if err != nil { + config.dismount(destFile) + return fmt.Errorf("failed to get exit code from `%s` following hot-add %s to utility VM: %s", testdCommand, destFile, err) + } + if testdExitCode != 0 { + config.dismount(destFile) + return fmt.Errorf("`%s` return non-zero exit code (%d) following hot-add %s to utility VM", testdCommand, testdExitCode, destFile) + } + + // Get the device from under the block subdirectory by doing a simple ls. This will come back as (eg) `sda` + lsCommand := fmt.Sprintf(`ls /sys/bus/scsi/devices/%d:0:0:%d/block`, controller, lun) + var lsOutput bytes.Buffer + lsProc, err := config.RunProcess(lsCommand, nil, &lsOutput, nil) + if err != nil { + config.dismount(destFile) + return fmt.Errorf("failed to `%s` following hot-add %s to utility VM: %s", lsCommand, destFile, err) + } + defer lsProc.Close() + lsProc.WaitTimeout(time.Duration(int(time.Second) * config.UvmTimeoutSeconds)) + lsExitCode, err := lsProc.ExitCode() + if err != nil { + config.dismount(destFile) + return fmt.Errorf("failed to get exit code from `%s` following hot-add %s to utility VM: %s", lsCommand, destFile, err) + } + if lsExitCode != 0 { + config.dismount(destFile) + return fmt.Errorf("`%s` return non-zero exit code (%d) following hot-add %s to utility VM", lsCommand, lsExitCode, destFile) + } + device := fmt.Sprintf(`/dev/%s`, strings.TrimSpace(lsOutput.String())) + logrus.Debugf("opengcs: CreateExt4Vhdx: %s: device at %s", destFile, device) + + // Format it ext4 + mkfsCommand := fmt.Sprintf(`mkfs.ext4 -q -E lazy_itable_init=1 -O ^has_journal,sparse_super2,uninit_bg,^resize_inode %s`, device) + var mkfsStderr bytes.Buffer + mkfsProc, err := config.RunProcess(mkfsCommand, nil, nil, &mkfsStderr) + if err != nil { + config.dismount(destFile) + return fmt.Errorf("failed to RunProcess %q following hot-add %s to utility VM: %s", destFile, mkfsCommand, err) + } + defer mkfsProc.Close() + mkfsProc.WaitTimeout(time.Duration(int(time.Second) * config.UvmTimeoutSeconds)) + mkfsExitCode, err := mkfsProc.ExitCode() + if err != nil { + config.dismount(destFile) + return fmt.Errorf("failed to get exit code from `%s` following hot-add %s to utility VM: %s", mkfsCommand, destFile, err) + } + if mkfsExitCode != 0 { + config.dismount(destFile) + return fmt.Errorf("`%s` return non-zero exit code (%d) following hot-add %s to utility VM: %s", mkfsCommand, mkfsExitCode, destFile, strings.TrimSpace(mkfsStderr.String())) + } + + // Dismount before we copy it + if err := config.dismount(destFile); err != nil { + return fmt.Errorf("failed to hot-remove: %s", err) + } + + // Populate the cache. + if cacheFile != "" && (sizeGB == DefaultVhdxSizeGB) { + if err := CopyFile(destFile, cacheFile, true); err != nil { + return fmt.Errorf("failed to seed cache '%s' from '%s': %s", destFile, cacheFile, err) + } + } + + logrus.Debugf("opengcs: CreateExt4Vhdx: %s created (non-cache)", destFile) + return nil +} diff --git a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/createsandbox.go b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/createsandbox.go deleted file mode 100644 index bb808b538e..0000000000 --- a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/createsandbox.go +++ /dev/null @@ -1,67 +0,0 @@ -// +build windows - -package client - -import ( - "fmt" - "os" - - "github.com/sirupsen/logrus" -) - -// CreateSandbox does what it says on the tin. This is done by copying a prebuilt-sandbox from the ServiceVM. -// It is the responsibility of the caller to synchronise simultaneous attempts to create the cache file. -// TODO: @jhowardmsft maxSizeInMB isn't hooked up in GCS. Needs a platform change which is in flight. -func (config *Config) CreateSandbox(destFile string, maxSizeInMB uint32, cacheFile string) error { - // Smallest we can accept is the default sandbox size as we can't size down, only expand. - if maxSizeInMB < DefaultSandboxSizeMB { - maxSizeInMB = DefaultSandboxSizeMB - } - - logrus.Debugf("opengcs: CreateSandbox: %s size:%dMB cache:%s", destFile, maxSizeInMB, cacheFile) - - // Retrieve from cache if the default size and already on disk - if cacheFile != "" && maxSizeInMB == DefaultSandboxSizeMB { - if _, err := os.Stat(cacheFile); err == nil { - if err := CopyFile(cacheFile, destFile, false); err != nil { - return fmt.Errorf("opengcs: CreateSandbox: Failed to copy cached sandbox '%s' to '%s': %s", cacheFile, destFile, err) - } - logrus.Debugf("opengcs: CreateSandbox: %s fulfilled from cache", destFile) - return nil - } - } - - if config.Uvm == nil { - return fmt.Errorf("opengcs: CreateSandbox: No utility VM has been created") - } - - // TODO @jhowardmsft - needs a platform change so that can specify size. eg fmt.Sprintf("createSandbox -size %d", maxSizeInMB)) - process, err := config.createUtilsProcess("createSandbox") - if err != nil { - return fmt.Errorf("opengcs: CreateSandbox: %s: failed to create utils process: %s", destFile, err) - } - - defer func() { - process.Process.Close() - }() - - logrus.Debugf("opengcs: CreateSandbox: %s: writing from stdout", destFile) - // Get back the sandbox VHDx stream from the service VM and write it to file - resultSize, err := writeFileFromReader(destFile, process.Stdout, config.UvmTimeoutSeconds, fmt.Sprintf("createSandbox %s", destFile)) - if err != nil { - return fmt.Errorf("opengcs: CreateSandbox: %s: failed writing %d bytes to target file: %s", destFile, resultSize, err) - } - - // Populate the cache - if cacheFile != "" && maxSizeInMB == DefaultSandboxSizeMB { - // It may already exist due to being created on another thread, in which case no copy back needed. - if _, err := os.Stat(cacheFile); os.IsNotExist(err) { - if err := CopyFile(destFile, cacheFile, false); err != nil { - return fmt.Errorf("opengcs: CreateSandbox: Failed to seed sandbox cache '%s' from '%s': %s", destFile, cacheFile, err) - } - } - } - - logrus.Debugf("opengcs: CreateSandbox: %s created (non-cache)", destFile) - return nil -} diff --git a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotaddvhd.go b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotaddvhd.go index 12ceab3ee0..daf7c25f04 100644 --- a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotaddvhd.go +++ b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotaddvhd.go @@ -13,7 +13,7 @@ import ( // service-VM per host scenario. In order to do a graphdriver `Diff`, we hot-add the // sandbox to /mnt/ so that we can run `exportSandbox` inside the utility VM to // get a tar-stream of the sandboxes contents back to the daemon. -func (config *Config) HotAddVhd(hostPath string, containerPath string) error { +func (config *Config) HotAddVhd(hostPath string, containerPath string, readOnly bool, mount bool) error { logrus.Debugf("opengcs: HotAddVhd: %s: %s", hostPath, containerPath) if config.Uvm == nil { @@ -26,13 +26,14 @@ func (config *Config) HotAddVhd(hostPath string, containerPath string) error { HostPath: hostPath, ContainerPath: containerPath, CreateInUtilityVM: true, - //ReadOnly: true, + ReadOnly: readOnly, + AttachOnly: !mount, }, Request: "Add", } - logrus.Debugf("opengcs: HotAddVhd: %s to %s", hostPath, containerPath) + if err := config.Uvm.Modify(modification); err != nil { - return fmt.Errorf("opengcs: HotAddVhd: failed: %s", err) + return fmt.Errorf("failed to modify utility VM configuration for hot-add: %s", err) } logrus.Debugf("opengcs: HotAddVhd: %s added successfully", hostPath) return nil diff --git a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotremovevhd.go b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotremovevhd.go index 1923c012f2..cf1971244d 100644 --- a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotremovevhd.go +++ b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotremovevhd.go @@ -27,7 +27,7 @@ func (config *Config) HotRemoveVhd(hostPath string) error { Request: "Remove", } if err := config.Uvm.Modify(modification); err != nil { - return fmt.Errorf("opengcs: HotRemoveVhd: %s failed: %s", hostPath, err) + return fmt.Errorf("failed modifying utility VM for hot-remove %s: %s", hostPath, err) } logrus.Debugf("opengcs: HotRemoveVhd: %s removed successfully", hostPath) return nil diff --git a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/process.go b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/process.go index 25299a3739..984a95a32e 100644 --- a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/process.go +++ b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/process.go @@ -15,6 +15,7 @@ type process struct { Process hcsshim.Process Stdin io.WriteCloser Stdout io.ReadCloser + Stderr io.ReadCloser } // createUtilsProcess is a convenient wrapper for hcsshim.createUtilsProcess to use when @@ -45,13 +46,13 @@ func (config *Config) createUtilsProcess(commandLine string) (process, error) { } proc.Process, err = config.Uvm.CreateProcess(processConfig) if err != nil { - return process{}, fmt.Errorf("opengcs: createUtilsProcess: CreateProcess %+v failed %s", config, err) + return process{}, fmt.Errorf("failed to create process (%+v) in utility VM: %s", config, err) } - if proc.Stdin, proc.Stdout, _, err = proc.Process.Stdio(); err != nil { + if proc.Stdin, proc.Stdout, proc.Stderr, err = proc.Process.Stdio(); err != nil { proc.Process.Kill() // Should this have a timeout? proc.Process.Close() - return process{}, fmt.Errorf("opengcs: createUtilsProcess: failed to get Stdio pipes %s", err) + return process{}, fmt.Errorf("failed to get stdio pipes for process %+v: %s", config, err) } logrus.Debugf("opengcs: createUtilsProcess success: pid %d", proc.Process.Pid()) @@ -60,41 +61,52 @@ func (config *Config) createUtilsProcess(commandLine string) (process, error) { // RunProcess runs the given command line program in the utilityVM. It takes in // an input to the reader to feed into stdin and returns stdout to output. -func (config *Config) RunProcess(commandLine string, input io.Reader, output io.Writer) error { +// IMPORTANT: It is the responsibility of the caller to call Close() on the returned process. +func (config *Config) RunProcess(commandLine string, stdin io.Reader, stdout io.Writer, stderr io.Writer) (hcsshim.Process, error) { logrus.Debugf("opengcs: RunProcess: %s", commandLine) process, err := config.createUtilsProcess(commandLine) if err != nil { - return err + return nil, err } - defer process.Process.Close() // Send the data into the process's stdin - if input != nil { + if stdin != nil { if _, err = copyWithTimeout(process.Stdin, - input, + stdin, 0, config.UvmTimeoutSeconds, fmt.Sprintf("send to stdin of %s", commandLine)); err != nil { - return err + return nil, err } // Don't need stdin now we've sent everything. This signals GCS that we are finished sending data. if err := process.Process.CloseStdin(); err != nil { - return err + return nil, err } } - if output != nil { + if stdout != nil { // Copy the data over to the writer. - if _, err := copyWithTimeout(output, + if _, err := copyWithTimeout(stdout, process.Stdout, 0, config.UvmTimeoutSeconds, fmt.Sprintf("RunProcess: copy back from %s", commandLine)); err != nil { - return err + return nil, err + } + } + + if stderr != nil { + // Copy the data over to the writer. + if _, err := copyWithTimeout(stderr, + process.Stderr, + 0, + config.UvmTimeoutSeconds, + fmt.Sprintf("RunProcess: copy back from %s", commandLine)); err != nil { + return nil, err } } logrus.Debugf("opengcs: runProcess success: %s", commandLine) - return nil + return process.Process, nil } diff --git a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/tartovhd.go b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/tartovhd.go index 39db903262..9aa6609d48 100644 --- a/vendor/github.com/jhowardmsft/opengcs/gogcs/client/tartovhd.go +++ b/vendor/github.com/jhowardmsft/opengcs/gogcs/client/tartovhd.go @@ -19,24 +19,24 @@ func (config *Config) TarToVhd(targetVHDFile string, reader io.Reader) (int64, e process, err := config.createUtilsProcess("tar2vhd") if err != nil { - return 0, fmt.Errorf("opengcs: TarToVhd: %s: failed to create utils process tar2vhd: %s", targetVHDFile, err) + return 0, fmt.Errorf("failed to start tar2vhd for %s: %s", targetVHDFile, err) } defer process.Process.Close() // Send the tarstream into the `tar2vhd`s stdin if _, err = copyWithTimeout(process.Stdin, reader, 0, config.UvmTimeoutSeconds, fmt.Sprintf("stdin of tar2vhd for generating %s", targetVHDFile)); err != nil { - return 0, fmt.Errorf("opengcs: TarToVhd: %s: failed to send to tar2vhd in uvm: %s", targetVHDFile, err) + return 0, fmt.Errorf("failed sending to tar2vhd for %s: %s", targetVHDFile, err) } // Don't need stdin now we've sent everything. This signals GCS that we are finished sending data. if err := process.Process.CloseStdin(); err != nil { - return 0, fmt.Errorf("opengcs: TarToVhd: %s: failed closing stdin handle: %s", targetVHDFile, err) + return 0, fmt.Errorf("failed closing stdin handle for %s: %s", targetVHDFile, err) } // Write stdout contents of `tar2vhd` to the VHD file payloadSize, err := writeFileFromReader(targetVHDFile, process.Stdout, config.UvmTimeoutSeconds, fmt.Sprintf("stdout of tar2vhd to %s", targetVHDFile)) if err != nil { - return 0, fmt.Errorf("opengcs: TarToVhd: %s: failed writing VHD file: %s", targetVHDFile, err) + return 0, fmt.Errorf("failed to write %s during tar2vhd: %s", targetVHDFile, err) } logrus.Debugf("opengcs: TarToVhd: %s created, %d bytes", targetVHDFile, payloadSize)