1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Vendor jhowardmsft/opengcs v0.0.3

Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
John Howard 2017-06-20 08:37:36 -07:00
parent 8fe9eb0551
commit 07034a4420
12 changed files with 733 additions and 0 deletions

View file

@ -8,6 +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 v0.0.3
github.com/kr/pty 5cf931ef8f
github.com/mattn/go-shellwords v1.0.3
github.com/tchap/go-patricia v2.2.6

21
vendor/github.com/jhowardmsft/opengcs/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Microsoft
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -0,0 +1,234 @@
// +build windows
package client
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"github.com/Microsoft/hcsshim"
"github.com/Sirupsen/logrus"
)
// Mode is the operational mode, both requested, and actual after verification
type Mode uint
const (
// Constants for the actual mode after validation
// ModeActualError means an error has occurred during validation
ModeActualError = iota
// ModeActualVhdx means that we are going to use VHDX boot after validation
ModeActualVhdx
// ModeActualKernelInitrd means that we are going to use kernel+initrd for boot after validation
ModeActualKernelInitrd
// Constants for the requested mode
// ModeRequestAuto means auto-select the boot mode for a utility VM
ModeRequestAuto = iota // VHDX will be priority over kernel+initrd
// ModeRequestVhdx means request VHDX boot if possible
ModeRequestVhdx
// ModeRequestKernelInitrd means request Kernel+initrd boot if possible
ModeRequestKernelInitrd
// 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
)
// 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.
//
// VHD is the priority.
//
// All paths are full host path-names.
type Config struct {
Kernel string // Kernel for Utility VM (embedded in a UEFI bootloader)
Initrd string // Initrd image for Utility VM
Vhdx string // VHD for booting the utility VM
Name string // Name of the utility VM
RequestedMode Mode // What mode is preferred when validating
ActualMode Mode // What mode was obtained during validation
UvmTimeoutSeconds int // How long to wait for the utility VM to respond in seconds
Uvm hcsshim.Container // The actual container
}
// GenerateDefault generates a default config from a set of options
// If baseDir is not supplied, defaults to $env:ProgramFiles\lcow
func (config *Config) GenerateDefault(options []string) error {
baseDir := filepath.Join(os.Getenv("ProgramFiles"), "lcow")
if _, err := os.Stat(baseDir); os.IsNotExist(err) {
return fmt.Errorf("opengcs: cannot create default utility VM configuration as directory '%s' was not found", baseDir)
}
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")
}
}
}
config.Vhdx = filepath.Join(baseDir, `uvm.vhdx`)
config.Kernel = filepath.Join(baseDir, `bootx64.efi`)
config.Initrd = filepath.Join(baseDir, `initrd.img`)
for _, v := range options {
opt := strings.SplitN(v, "=", 2)
if len(opt) == 2 {
switch strings.ToLower(opt[0]) {
case "opengcskernel":
config.Kernel = opt[1]
case "opengcsinitrd":
config.Initrd = opt[1]
case "opengcsvhdx":
config.Vhdx = opt[1]
case "opengcstimeoutsecs":
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 optTimeoutSeconds < 0 {
return fmt.Errorf("opengcs: opengcstimeoutsecs option cannot be negative")
}
}
}
}
// 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
}
}
}
return nil
}
// 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")
}
if config.RequestedMode == ModeRequestKernelInitrd && (config.Kernel == "" || config.Initrd == "") {
return fmt.Errorf("opengcs: config is invalid - request for 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)
}
} else {
config.ActualMode = ModeActualVhdx
return nil
}
}
// So must be kernel+initrd, or auto where we fallback as the VHDX doesn't exist
if config.Initrd == "" || config.Kernel == "" {
if config.RequestedMode == ModeRequestKernelInitrd {
return fmt.Errorf("opengcs: both initrd and kernel options for utility VM boot must be supplied")
}
return fmt.Errorf("opengcs: configuration is invalid")
}
if _, err := os.Stat(config.Kernel); os.IsNotExist(err) {
return fmt.Errorf("opengcs: kernel '%s' was not found", config.Kernel)
}
if _, err := os.Stat(config.Initrd); os.IsNotExist(err) {
return fmt.Errorf("opengcs: initrd '%s' was not found", config.Initrd)
}
dk, _ := filepath.Split(config.Kernel)
di, _ := filepath.Split(config.Initrd)
if dk != di {
return fmt.Errorf("initrd '%s' and kernel '%s' must be located in the same directory", config.Initrd, config.Kernel)
}
config.ActualMode = ModeActualKernelInitrd
return nil
}
// Create creates a utility VM from a configuration.
func (config *Config) Create() error {
logrus.Debugf("opengcs Create: %+v", config)
if err := config.validate(); err != nil {
return err
}
configuration := &hcsshim.ContainerConfig{
HvPartition: true,
Name: config.Name,
SystemType: "container",
ContainerType: "linux",
TerminateOnLastHandleClosed: true,
}
if config.ActualMode == ModeActualVhdx {
configuration.HvRuntime = &hcsshim.HvRuntime{
ImagePath: config.Vhdx,
}
} else {
// TODO @jhowardmsft - with a platform change that is in-flight, remove ImagePath for
// initrd/kernel boot. Current platform requires it.
dir, _ := filepath.Split(config.Initrd)
configuration.HvRuntime = &hcsshim.HvRuntime{
ImagePath: dir,
LinuxInitrdPath: config.Initrd,
LinuxKernelPath: config.Kernel,
}
}
configurationS, _ := json.Marshal(configuration)
logrus.Debugf("opengcs Create: 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...")
err = uvm.Start()
if err != nil {
logrus.Debugf("opengcs Create: 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)
return nil
}

View file

@ -0,0 +1,78 @@
// +build windows
package client
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon
import (
"fmt"
"os"
"sync"
"github.com/Sirupsen/logrus"
)
var sandboxCacheLock sync.Mutex
// CreateSandbox does what it says on the tin. This is done by copying a prebuilt-sandbox from the ServiceVM
// 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 maxSizeInMB == DefaultSandboxSizeMB {
sandboxCacheLock.Lock()
if _, err := os.Stat(cacheFile); err == nil {
if err := copyFile(cacheFile, destFile); err != nil {
sandboxCacheLock.Unlock()
return fmt.Errorf("opengcs: CreateSandbox: Failed to copy cached sandbox '%s' to '%s': %s", cacheFile, destFile, err)
}
sandboxCacheLock.Unlock()
logrus.Debugf("opengcs: CreateSandbox: %s fulfilled from cache", destFile)
return nil
}
sandboxCacheLock.Unlock()
}
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 maxSizeInMB == DefaultSandboxSizeMB {
sandboxCacheLock.Lock()
// 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); err != nil {
sandboxCacheLock.Unlock()
return fmt.Errorf("opengcs: CreateSandbox: Failed to seed sandbox cache '%s' from '%s': %s", destFile, cacheFile, err)
}
}
sandboxCacheLock.Unlock()
}
logrus.Debugf("opengcs: CreateSandbox: %s created (non-cache)", destFile)
return nil
}

View file

@ -0,0 +1,41 @@
// +build windows
package client
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon
import (
"fmt"
"github.com/Microsoft/hcsshim"
"github.com/Sirupsen/logrus"
)
// HotAddVhd hot-adds a VHD to a utility VM. This is used in the global one-utility-VM-
// service-VM per host scenario. In order to do a graphdriver `Diff`, we hot-add the
// sandbox to /mnt/<id> 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 {
logrus.Debugf("opengcs: HotAddVhd: %s: %s", hostPath, containerPath)
if config.Uvm == nil {
return fmt.Errorf("cannot hot-add VHD as no utility VM is in configuration")
}
modification := &hcsshim.ResourceModificationRequestResponse{
Resource: "MappedVirtualDisk",
Data: hcsshim.MappedVirtualDisk{
HostPath: hostPath,
ContainerPath: containerPath,
CreateInUtilityVM: true,
//ReadOnly: true,
},
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)
}
logrus.Debugf("opengcs: HotAddVhd: %s added successfully", hostPath)
return nil
}

View file

@ -0,0 +1,36 @@
// +build windows
package client
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon
import (
"fmt"
"github.com/Microsoft/hcsshim"
"github.com/Sirupsen/logrus"
)
// HotRemoveVhd hot-removes a VHD from a utility VM. This is used in the global one-utility-VM-
// service-VM per host scenario.
func (config *Config) HotRemoveVhd(hostPath string) error {
logrus.Debugf("opengcs: HotRemoveVhd: %s", hostPath)
if config.Uvm == nil {
return fmt.Errorf("cannot hot-add VHD as no utility VM is in configuration")
}
modification := &hcsshim.ResourceModificationRequestResponse{
Resource: "MappedVirtualDisk",
Data: hcsshim.MappedVirtualDisk{
HostPath: hostPath,
CreateInUtilityVM: true,
},
Request: "Remove",
}
if err := config.Uvm.Modify(modification); err != nil {
return fmt.Errorf("opengcs: HotRemoveVhd: %s failed: %s", hostPath, err)
}
logrus.Debugf("opengcs: HotRemoveVhd: %s removed successfully", hostPath)
return nil
}

View file

@ -0,0 +1,33 @@
// +build windows
package client
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon
import (
"fmt"
"os"
"path/filepath"
)
// LayerVhdDetails is a utility for getting a file name, size and indication of
// sandbox for a VHD(x) in a folder. A read-only layer will be layer.vhd. A
// read-write layer will be sandbox.vhdx.
func LayerVhdDetails(folder string) (string, int64, bool, error) {
var fileInfo os.FileInfo
isSandbox := false
filename := filepath.Join(folder, "layer.vhd")
var err error
if fileInfo, err = os.Stat(filename); err != nil {
filename = filepath.Join(folder, "sandbox.vhdx")
if fileInfo, err = os.Stat(filename); err != nil {
if os.IsNotExist(err) {
return "", 0, isSandbox, fmt.Errorf("could not find layer or sandbox in %s", folder)
}
return "", 0, isSandbox, fmt.Errorf("error locating layer or sandbox in %s: %s", folder, err)
}
isSandbox = true
}
return filename, fileInfo.Size(), isSandbox, nil
}

View file

@ -0,0 +1,61 @@
// +build windows
package client
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon
import (
"fmt"
"io"
"github.com/Microsoft/hcsshim"
"github.com/Sirupsen/logrus"
)
// Process is the structure pertaining to a process running in a utility VM.
type process struct {
Process hcsshim.Process
Stdin io.WriteCloser
Stdout io.ReadCloser
}
// createUtilsProcess is a convenient wrapper for hcsshim.createUtilsProcess to use when
// communicating with a utility VM.
func (config *Config) createUtilsProcess(commandLine string) (process, error) {
logrus.Debugf("opengcs: createUtilsProcess")
if config.Uvm == nil {
return process{}, fmt.Errorf("cannot create utils process as no utility VM is in configuration")
}
var (
err error
proc process
)
env := make(map[string]string)
env["PATH"] = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:"
processConfig := &hcsshim.ProcessConfig{
EmulateConsole: false,
CreateStdInPipe: true,
CreateStdOutPipe: true,
CreateStdErrPipe: true,
CreateInUtilityVm: true,
WorkingDirectory: "/bin",
Environment: env,
CommandLine: commandLine,
}
proc.Process, err = config.Uvm.CreateProcess(processConfig)
if err != nil {
return process{}, fmt.Errorf("opengcs: createUtilsProcess: CreateProcess %+v failed %s", config, err)
}
if proc.Stdin, proc.Stdout, _, 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)
}
logrus.Debugf("opengcs: createUtilsProcess success: pid %d", proc.Process.Pid())
return proc, nil
}

View file

@ -0,0 +1,44 @@
// +build windows
package client
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon
import (
"fmt"
"io"
"github.com/Sirupsen/logrus"
)
// TarToVhd streams a tarstream contained in an io.Reader to a fixed vhd file
func (config *Config) TarToVhd(targetVHDFile string, reader io.Reader) (int64, error) {
logrus.Debugf("opengcs: TarToVhd: %s", targetVHDFile)
if config.Uvm == nil {
return 0, fmt.Errorf("cannot Tar2Vhd as no utility VM is in configuration")
}
process, err := config.createUtilsProcess("tar2vhd")
if err != nil {
return 0, fmt.Errorf("opengcs: TarToVhd: %s: failed to create utils process tar2vhd: %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("send %s, to stdin of tar2vhd", targetVHDFile)); err != nil {
return 0, fmt.Errorf("opengcs: TarToVhd: %s: failed to send to tar2vhd in uvm: %s", targetVHDFile, err)
}
// Don't need stdin now we've sent everything. This signals GCS that we are finished sending data.
process.Process.CloseStdin()
// Write stdout contents of `tar2vhd` to the VHD file
payloadSize, err := writeFileFromReader(targetVHDFile, process.Stdout, config.UvmTimeoutSeconds, fmt.Sprintf("output of tar2vhd to %s", targetVHDFile))
if err != nil {
return 0, fmt.Errorf("opengcs: TarToVhd: %s: failed writing VHD file: %s", targetVHDFile, err)
}
logrus.Debugf("opengcs: TarToVhd: %s created, %d bytes", targetVHDFile, payloadSize)
return payloadSize, err
}

View file

@ -0,0 +1,5 @@
// +build !windows
package client
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon

View file

@ -0,0 +1,110 @@
// +build windows
package client
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon
import (
"fmt"
"io"
"os"
"syscall"
"time"
"unsafe"
"github.com/Sirupsen/logrus"
)
var (
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procCopyFileW = modkernel32.NewProc("CopyFileW")
)
// writeFileFromReader writes an output file from an io.Reader
func writeFileFromReader(path string, reader io.Reader, timeoutSeconds int, context string) (int64, error) {
outFile, err := os.Create(path)
if err != nil {
return 0, fmt.Errorf("opengcs: writeFileFromReader: failed to create %s: %s", path, err)
}
defer outFile.Close()
return copyWithTimeout(outFile, reader, 0, timeoutSeconds, context)
}
// copyWithTimeout is a wrapper for io.Copy using a timeout duration
func copyWithTimeout(dst io.Writer, src io.Reader, size int64, timeoutSeconds int, context string) (int64, error) {
logrus.Debugf("opengcs: copywithtimeout: size %d: timeout %d: (%s)", size, timeoutSeconds, context)
type resultType struct {
err error
bytes int64
}
done := make(chan resultType, 1)
go func() {
// TODO @jhowardmsft. Needs platform fix. Improve reliability by
// chunking the data. Ultimately can just use io.Copy instead with no loop
result := resultType{}
var copied int64
for {
copied, result.err = io.CopyN(dst, src, 1024)
result.bytes += copied
if copied == 0 {
done <- result
break
}
// TODO @jhowardmsft - next line is debugging only. Remove
//logrus.Debugf("%s: copied so far %d\n", context, result.bytes)
}
}()
var result resultType
timedout := time.After(time.Duration(timeoutSeconds) * time.Second)
select {
case <-timedout:
return 0, fmt.Errorf("opengcs: copyWithTimeout: timed out (%s)", context)
case result = <-done:
if result.err != nil && result.err != io.EOF {
// See https://github.com/golang/go/blob/f3f29d1dea525f48995c1693c609f5e67c046893/src/os/exec/exec_windows.go for a clue as to why we are doing this :)
if se, ok := result.err.(syscall.Errno); ok {
const (
errNoData = syscall.Errno(232)
errBrokenPipe = syscall.Errno(109)
)
if se == errNoData || se == errBrokenPipe {
logrus.Debugf("opengcs: copyWithTimeout: hit NoData or BrokenPipe: %d: %s", se, context)
return result.bytes, nil
}
}
return 0, fmt.Errorf("opengcs: copyWithTimeout: error reading: '%s' after %d bytes (%s)", result.err, result.bytes, context)
}
}
logrus.Debugf("opengcs: copyWithTimeout: success - copied %d bytes (%s)", result.bytes, context)
return result.bytes, nil
}
// copyFile is a utility for copying a file - used for the sandbox cache.
// Uses CopyFileW win32 API for performance
func copyFile(srcFile, destFile string) error {
var bFailIfExists uint32 = 1
lpExistingFileName, err := syscall.UTF16PtrFromString(srcFile)
if err != nil {
return err
}
lpNewFileName, err := syscall.UTF16PtrFromString(destFile)
if err != nil {
return err
}
r1, _, err := syscall.Syscall(
procCopyFileW.Addr(),
3,
uintptr(unsafe.Pointer(lpExistingFileName)),
uintptr(unsafe.Pointer(lpNewFileName)),
uintptr(bFailIfExists))
if r1 == 0 {
return fmt.Errorf("failed CopyFileW Win32 call from '%s' to %s: %s", srcFile, destFile, err)
}
return nil
}

View file

@ -0,0 +1,69 @@
// +build windows
package client
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon
import (
"fmt"
"io"
"os"
"github.com/Sirupsen/logrus"
)
// VhdToTar does what is says - it exports a VHD in a specified
// folder (either a read-only layer.vhd, or a read-write sandbox.vhd) to a
// ReadCloser containing a tar-stream of the layers contents.
func (config *Config) VhdToTar(vhdFile string, uvmMountPath string, isSandbox bool, vhdSize int64) (io.ReadCloser, error) {
logrus.Debugf("opengcs: VhdToTar: %s isSandbox: %t", vhdFile, isSandbox)
if config.Uvm == nil {
return nil, fmt.Errorf("cannot VhdToTar as no utility VM is in configuration")
}
vhdHandle, err := os.Open(vhdFile)
if err != nil {
return nil, fmt.Errorf("opengcs: VhdToTar: failed to open %s: %s", vhdFile, err)
}
defer vhdHandle.Close()
logrus.Debugf("opengcs: VhdToTar: exporting %s, size %d, isSandbox %t", vhdHandle.Name(), vhdSize, isSandbox)
// Different binary depending on whether a RO layer or a RW sandbox
command := "vhd2tar"
if isSandbox {
command = fmt.Sprintf("exportSandbox -path %s", uvmMountPath)
}
// Start the binary in the utility VM
process, err := config.createUtilsProcess(command)
if err != nil {
return nil, fmt.Errorf("opengcs: VhdToTar: %s: failed to create utils process %s: %s", vhdHandle.Name(), command, err)
}
if !isSandbox {
// Send the VHD contents to the utility VM processes stdin handle if not a sandbox
logrus.Debugf("opengcs: VhdToTar: copying the layer VHD into the utility VM")
if _, err = copyWithTimeout(process.Stdin, vhdHandle, vhdSize, config.UvmTimeoutSeconds, fmt.Sprintf("vhdtotarstream: sending %s to %s", vhdHandle.Name(), command)); err != nil {
process.Process.Close()
return nil, fmt.Errorf("opengcs: VhdToTar: %s: failed to copyWithTimeout on the stdin pipe (to utility VM): %s", vhdHandle.Name(), err)
}
}
// Start a goroutine which copies the stdout (ie the tar stream)
reader, writer := io.Pipe()
go func() {
defer writer.Close()
defer process.Process.Close()
logrus.Debugf("opengcs: VhdToTar: copying tar stream back from the utility VM")
bytes, err := copyWithTimeout(writer, process.Stdout, vhdSize, config.UvmTimeoutSeconds, fmt.Sprintf("vhdtotarstream: copy tarstream from %s", command))
if err != nil {
logrus.Errorf("opengcs: VhdToTar: %s: copyWithTimeout on the stdout pipe (from utility VM) failed: %s", vhdHandle.Name(), err)
}
logrus.Debugf("opengcs: VhdToTar: copied %d bytes of the tarstream of %s from the utility VM", bytes, vhdHandle.Name())
}()
// Return the read-side of the pipe connected to the goroutine which is reading from the stdout of the process in the utility VM
return reader, nil
}