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:
parent
8fe9eb0551
commit
07034a4420
12 changed files with 733 additions and 0 deletions
|
@ -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
21
vendor/github.com/jhowardmsft/opengcs/LICENSE
generated
vendored
Normal 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.
|
234
vendor/github.com/jhowardmsft/opengcs/gogcs/client/config.go
generated
vendored
Normal file
234
vendor/github.com/jhowardmsft/opengcs/gogcs/client/config.go
generated
vendored
Normal 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
|
||||
}
|
78
vendor/github.com/jhowardmsft/opengcs/gogcs/client/createsandbox.go
generated
vendored
Normal file
78
vendor/github.com/jhowardmsft/opengcs/gogcs/client/createsandbox.go
generated
vendored
Normal 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
|
||||
}
|
41
vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotaddvhd.go
generated
vendored
Normal file
41
vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotaddvhd.go
generated
vendored
Normal 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
|
||||
}
|
36
vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotremovevhd.go
generated
vendored
Normal file
36
vendor/github.com/jhowardmsft/opengcs/gogcs/client/hotremovevhd.go
generated
vendored
Normal 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
|
||||
}
|
33
vendor/github.com/jhowardmsft/opengcs/gogcs/client/layervhddetails.go
generated
vendored
Normal file
33
vendor/github.com/jhowardmsft/opengcs/gogcs/client/layervhddetails.go
generated
vendored
Normal 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
|
||||
}
|
61
vendor/github.com/jhowardmsft/opengcs/gogcs/client/process.go
generated
vendored
Normal file
61
vendor/github.com/jhowardmsft/opengcs/gogcs/client/process.go
generated
vendored
Normal 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
|
||||
}
|
44
vendor/github.com/jhowardmsft/opengcs/gogcs/client/tartovhd.go
generated
vendored
Normal file
44
vendor/github.com/jhowardmsft/opengcs/gogcs/client/tartovhd.go
generated
vendored
Normal 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
|
||||
}
|
5
vendor/github.com/jhowardmsft/opengcs/gogcs/client/unsupported.go
generated
vendored
Normal file
5
vendor/github.com/jhowardmsft/opengcs/gogcs/client/unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
// +build !windows
|
||||
|
||||
package client
|
||||
|
||||
// TODO @jhowardmsft - This will move to Microsoft/opengcs soon
|
110
vendor/github.com/jhowardmsft/opengcs/gogcs/client/utilities.go
generated
vendored
Normal file
110
vendor/github.com/jhowardmsft/opengcs/gogcs/client/utilities.go
generated
vendored
Normal 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
|
||||
|
||||
}
|
69
vendor/github.com/jhowardmsft/opengcs/gogcs/client/vhdtotar.go
generated
vendored
Normal file
69
vendor/github.com/jhowardmsft/opengcs/gogcs/client/vhdtotar.go
generated
vendored
Normal 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
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue