From acf4f45c6cb565f17dc4ae1a36778516ffceb980 Mon Sep 17 00:00:00 2001 From: ksubrmnn Date: Fri, 14 Dec 2018 09:58:04 -0800 Subject: [PATCH 1/2] Update hcsshim for vxlan --- glide.lock | 2 +- glide.yaml | 2 +- .../Microsoft/hcsshim/.gometalinter.json | 17 + .../github.com/Microsoft/hcsshim/appveyor.yml | 13 +- .../Microsoft/hcsshim/cmd/runhcs/container.go | 344 +++-- .../hcsshim/cmd/runhcs/create-scratch.go | 14 +- .../Microsoft/hcsshim/cmd/runhcs/create.go | 5 +- .../Microsoft/hcsshim/cmd/runhcs/delete.go | 1 + .../Microsoft/hcsshim/cmd/runhcs/exec.go | 8 +- .../Microsoft/hcsshim/cmd/runhcs/kill.go | 120 +- .../Microsoft/hcsshim/cmd/runhcs/kill_test.go | 91 ++ .../Microsoft/hcsshim/cmd/runhcs/list.go | 1 + .../Microsoft/hcsshim/cmd/runhcs/main.go | 14 +- .../Microsoft/hcsshim/cmd/runhcs/pause.go | 2 + .../Microsoft/hcsshim/cmd/runhcs/ps.go | 1 + .../Microsoft/hcsshim/cmd/runhcs/shim.go | 39 +- .../Microsoft/hcsshim/cmd/runhcs/signalmap.go | 10 +- .../Microsoft/hcsshim/cmd/runhcs/start.go | 1 + .../Microsoft/hcsshim/cmd/runhcs/state.go | 7 +- .../Microsoft/hcsshim/cmd/runhcs/tty.go | 6 + .../Microsoft/hcsshim/cmd/runhcs/utils.go | 16 +- .../hcsshim/cmd/runhcs/utils_test.go | 39 + .../Microsoft/hcsshim/cmd/runhcs/vm.go | 111 +- .../hcsshim/cmd/tar2ext4/tar2ext4.go | 64 + .../ext4/internal/compactext4/compact.go | 1263 +++++++++++++++++ .../ext4/internal/compactext4/compact_test.go | 355 +++++ .../internal/compactext4/verify_linux_test.go | 248 ++++ .../ext4/internal/compactext4/verify_test.go | 18 + .../hcsshim/ext4/internal/format/format.go | 411 ++++++ .../hcsshim/ext4/tar2ext4/tar2ext4.go | 174 +++ .../hcsshim/ext4/tar2ext4/vhdfooter.go | 76 + .../Microsoft/hcsshim/functional/lcow_test.go | 101 +- .../Microsoft/hcsshim/functional/test.go | 21 - .../hcsshim/functional/utilities/createuvm.go | 72 +- .../functional/utilities/requiresbuild.go | 8 +- .../functional/uvm_mem_backingtype_test.go | 112 ++ .../hcsshim/functional/uvm_plannine_test.go | 7 +- .../hcsshim/functional/uvm_properties_test.go | 48 + .../hcsshim/functional/uvm_scratch_test.go | 8 +- .../hcsshim/functional/uvm_scsi_test.go | 20 +- .../hcsshim/functional/uvm_vpmem_test.go | 13 +- .../hcsshim/functional/uvm_vsmb_test.go | 8 +- .../Microsoft/hcsshim/functional/wcow_test.go | 31 +- .../hcsshim/functional/wcow_xenon_v2_test.go | 4 +- .../github.com/Microsoft/hcsshim/hcn/hcn.go | 41 +- .../Microsoft/hcsshim/hcn/hcnendpoint.go | 6 +- .../Microsoft/hcsshim/hcn/hcnendpoint_test.go | 140 +- .../Microsoft/hcsshim/hcn/hcnerrors.go | 95 ++ .../Microsoft/hcsshim/hcn/hcnerrors_test.go | 34 + .../Microsoft/hcsshim/hcn/hcnloadbalancer.go | 16 +- .../hcsshim/hcn/hcnloadbalancer_test.go | 154 +- .../Microsoft/hcsshim/hcn/hcnnamespace.go | 86 +- .../hcsshim/hcn/hcnnamespace_test.go | 319 ++++- .../Microsoft/hcsshim/hcn/hcnnetwork.go | 96 +- .../Microsoft/hcsshim/hcn/hcnnetwork_test.go | 88 +- .../Microsoft/hcsshim/hcn/hcnpolicy.go | 9 + .../Microsoft/hcsshim/hcn/hcnsupport_test.go | 4 +- .../Microsoft/hcsshim/hcn/hcnutils_test.go | 103 +- .../Microsoft/hcsshim/hcn/hcnv1schema_test.go | 39 +- .../Microsoft/hcsshim/hcn/hnsv1_test.go | 27 +- .../Microsoft/hcsshim/hcn/zsyscall_windows.go | 173 ++- .../Microsoft/hcsshim/hnsendpoint.go | 3 + .../github.com/Microsoft/hcsshim/interface.go | 5 + .../hcsshim/internal/cni/registry.go | 110 ++ .../hcsshim/internal/cni/registry_test.go | 137 ++ .../copywithtimeout/copywithtimeout.go | 22 +- .../hcsshim/internal/guestrequest/types.go | 13 + .../hcsshim/internal/hcs/callback.go | 49 +- .../Microsoft/hcsshim/internal/hcs/errors.go | 12 +- .../Microsoft/hcsshim/internal/hcs/hcs.go | 1 + .../Microsoft/hcsshim/internal/hcs/log.go | 15 + .../Microsoft/hcsshim/internal/hcs/process.go | 176 ++- .../Microsoft/hcsshim/internal/hcs/system.go | 321 +++-- .../Microsoft/hcsshim/internal/hcs/watcher.go | 9 +- .../hcsshim/internal/hcs/zsyscall_windows.go | 154 +- .../hcsshim/internal/hcserror/hcserror.go | 4 - .../hcsshim/internal/hcsoci/hcsdoc_wcow.go | 17 +- .../hcsshim/internal/hcsoci/layers.go | 117 +- .../hcsshim/internal/hcsoci/resources.go | 11 + .../hcsshim/internal/hcsoci/resources_lcow.go | 37 +- .../hcsshim/internal/hcsoci/resources_wcow.go | 53 +- .../hcsshim/internal/hns/hnspolicylist.go | 1 + .../hcsshim/internal/hns/zsyscall_windows.go | 8 +- .../hcsshim/internal/interop/interop.go | 4 +- .../internal/interop/zsyscall_windows.go | 6 +- .../hcsshim/internal/lcow/scratch.go | 25 +- .../hcsshim/internal/logfields/fields.go | 37 + .../hcsshim/internal/regstate/regstate.go | 5 + .../hcsshim/internal/runhcs/container.go | 97 +- .../Microsoft/hcsshim/internal/runhcs/util.go | 16 + .../hcsshim/internal/runhcs/util_test.go | 17 + .../Microsoft/hcsshim/internal/runhcs/vm.go | 43 + .../hcsshim/internal/schema1/schema1.go | 23 +- .../hcsshim/internal/schema2/chipset.go | 4 +- .../internal/schema2/linux_kernel_direct.go | 18 + .../hcsshim/internal/schema2/memory_2.go | 4 +- .../schema2/virtual_p_mem_controller.go | 5 +- .../internal/schemaversion/schemaversion.go | 4 +- .../schemaversion/schemaversion_test.go | 20 +- .../hcsshim/internal/uvm/constants.go | 12 +- .../Microsoft/hcsshim/internal/uvm/counter.go | 12 +- .../Microsoft/hcsshim/internal/uvm/create.go | 462 +----- .../hcsshim/internal/uvm/create_lcow.go | 389 +++++ .../hcsshim/internal/uvm/create_test.go | 23 +- .../hcsshim/internal/uvm/create_wcow.go | 167 +++ .../Microsoft/hcsshim/internal/uvm/network.go | 112 +- .../Microsoft/hcsshim/internal/uvm/plan9.go | 9 +- .../Microsoft/hcsshim/internal/uvm/scsi.go | 170 ++- .../Microsoft/hcsshim/internal/uvm/start.go | 34 +- .../Microsoft/hcsshim/internal/uvm/types.go | 23 +- .../Microsoft/hcsshim/internal/uvm/vpmem.go | 6 + .../Microsoft/hcsshim/internal/uvm/vsmb.go | 26 +- .../Microsoft/hcsshim/internal/uvm/wait.go | 23 +- .../hcsshim/internal/wclayer/activatelayer.go | 25 +- .../hcsshim/internal/wclayer/createlayer.go | 26 +- .../internal/wclayer/createscratchlayer.go | 23 +- .../internal/wclayer/deactivatelayer.go | 25 +- .../hcsshim/internal/wclayer/destroylayer.go | 25 +- .../internal/wclayer/expandscratchsize.go | 26 +- .../hcsshim/internal/wclayer/exportlayer.go | 113 +- .../internal/wclayer/getlayermountpath.go | 33 +- .../internal/wclayer/getsharedbaseimages.go | 19 +- .../hcsshim/internal/wclayer/grantvmaccess.go | 28 +- .../hcsshim/internal/wclayer/importlayer.go | 119 +- .../hcsshim/internal/wclayer/layerexists.go | 26 +- .../hcsshim/internal/wclayer/layerutils.go | 4 +- .../hcsshim/internal/wclayer/nametoguid.go | 20 +- .../hcsshim/internal/wclayer/preparelayer.go | 23 +- .../internal/wclayer/unpreparelayer.go | 25 +- .../hcsshim/internal/wclayer/wclayer.go | 12 +- .../internal/wclayer/zsyscall_windows.go | 233 +-- vendor/github.com/Microsoft/hcsshim/layer.go | 6 +- .../Microsoft/hcsshim/mksyscall_windows.go | 13 +- .../Microsoft/hcsshim/osversion/osversion.go | 51 + .../hcsshim/osversion/windowsbuilds.go | 10 + .../Microsoft/hcsshim/pkg/go-runhcs/LICENSE | 201 +++ .../Microsoft/hcsshim/pkg/go-runhcs/NOTICE | 22 + .../Microsoft/hcsshim/pkg/go-runhcs/runhcs.go | 160 +++ .../pkg/go-runhcs/runhcs_create-scratch.go | 10 + .../go-runhcs/runhcs_create-scratch_test.go | 65 + .../hcsshim/pkg/go-runhcs/runhcs_create.go | 101 ++ .../hcsshim/pkg/go-runhcs/runhcs_delete.go | 33 + .../pkg/go-runhcs/runhcs_e2e_matrix_test.go | 390 +++++ .../hcsshim/pkg/go-runhcs/runhcs_exec.go | 88 ++ .../pkg/go-runhcs/runhcs_integration_test.go | 7 + .../hcsshim/pkg/go-runhcs/runhcs_kill.go | 11 + .../hcsshim/pkg/go-runhcs/runhcs_list.go | 28 + .../hcsshim/pkg/go-runhcs/runhcs_list_test.go | 23 + .../hcsshim/pkg/go-runhcs/runhcs_pause.go | 10 + .../hcsshim/pkg/go-runhcs/runhcs_ps.go | 20 + .../pkg/go-runhcs/runhcs_resize-tty.go | 33 + .../hcsshim/pkg/go-runhcs/runhcs_resume.go | 10 + .../hcsshim/pkg/go-runhcs/runhcs_start.go | 10 + .../hcsshim/pkg/go-runhcs/runhcs_state.go | 20 + .../hcsshim/pkg/go-runhcs/runhcs_test.go | 68 + .../Microsoft/hcsshim/tools/uvmboot/main.go | 269 ++++ .../tools/uvmboot/resource_windows_386.syso | Bin 0 -> 968 bytes .../tools/uvmboot/resource_windows_amd64.syso | Bin 0 -> 968 bytes .../Microsoft/hcsshim/zsyscall_windows.go | 8 +- 159 files changed, 8897 insertions(+), 2002 deletions(-) create mode 100644 vendor/github.com/Microsoft/hcsshim/.gometalinter.json create mode 100644 vendor/github.com/Microsoft/hcsshim/cmd/runhcs/kill_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/cmd/runhcs/utils_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/cmd/tar2ext4/tar2ext4.go create mode 100644 vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/compact.go create mode 100644 vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/compact_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/verify_linux_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/verify_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/ext4/internal/format/format.go create mode 100644 vendor/github.com/Microsoft/hcsshim/ext4/tar2ext4/tar2ext4.go create mode 100644 vendor/github.com/Microsoft/hcsshim/ext4/tar2ext4/vhdfooter.go create mode 100644 vendor/github.com/Microsoft/hcsshim/functional/uvm_mem_backingtype_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/functional/uvm_properties_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/hcn/hcnerrors.go create mode 100644 vendor/github.com/Microsoft/hcsshim/hcn/hcnerrors_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/cni/registry.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/cni/registry_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/hcs/log.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/runhcs/util.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/runhcs/util_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/runhcs/vm.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/schema2/linux_kernel_direct.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/uvm/create_lcow.go create mode 100644 vendor/github.com/Microsoft/hcsshim/internal/uvm/create_wcow.go create mode 100644 vendor/github.com/Microsoft/hcsshim/osversion/osversion.go create mode 100644 vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/LICENSE create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/NOTICE create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create-scratch.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create-scratch_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_delete.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_e2e_matrix_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_exec.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_integration_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_kill.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_list.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_list_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_pause.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_ps.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resize-tty.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resume.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_start.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_state.go create mode 100644 vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_test.go create mode 100644 vendor/github.com/Microsoft/hcsshim/tools/uvmboot/main.go create mode 100644 vendor/github.com/Microsoft/hcsshim/tools/uvmboot/resource_windows_386.syso create mode 100644 vendor/github.com/Microsoft/hcsshim/tools/uvmboot/resource_windows_amd64.syso diff --git a/glide.lock b/glide.lock index 10357e3a20..12ba6894e4 100644 --- a/glide.lock +++ b/glide.lock @@ -139,7 +139,7 @@ imports: - name: github.com/Microsoft/go-winio version: 97e4973ce50b2ff5f09635a57e2b88a037aae829 - name: github.com/Microsoft/hcsshim - version: ad5b578a19495423d2546545fd5134127b272f79 + version: 2bf3a7ac42318aacfffd0805be705ff2e4d1138f subpackages: - internal/guid - internal/hcs diff --git a/glide.yaml b/glide.yaml index 1e30ddf0a9..4a9c1c1c2a 100644 --- a/glide.yaml +++ b/glide.yaml @@ -63,7 +63,7 @@ import: - package: github.com/bronze1man/goStrongswanVici version: 4d72634a2f113aa48347dbc7dcb14adb806b6534 - package: github.com/Microsoft/hcsshim - version: v0.7.4 + version: v0.8.3 - package: github.com/Microsoft/go-winio version: v0.4.11 - pacakge: github.com/sirupsen/logrus diff --git a/vendor/github.com/Microsoft/hcsshim/.gometalinter.json b/vendor/github.com/Microsoft/hcsshim/.gometalinter.json new file mode 100644 index 0000000000..00e9a6e2ec --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/.gometalinter.json @@ -0,0 +1,17 @@ +{ + "Vendor": true, + "Deadline": "2m", + "Sort": [ + "linter", + "severity", + "path", + "line" + ], + "Skip": [ + "internal\\schema2" + ], + "EnableGC": true, + "Enable": [ + "gofmt" + ] +} \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/appveyor.yml b/vendor/github.com/Microsoft/hcsshim/appveyor.yml index 8d12f59d1f..85b086a43c 100644 --- a/vendor/github.com/Microsoft/hcsshim/appveyor.yml +++ b/vendor/github.com/Microsoft/hcsshim/appveyor.yml @@ -6,14 +6,23 @@ clone_folder: c:\gopath\src\github.com\Microsoft\hcsshim environment: GOPATH: c:\gopath - PATH: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin;%PATH% + PATH: C:\mingw-w64\x86_64-7.2.0-posix-seh-rt_v5-rev1\mingw64\bin;%GOPATH%\bin;%PATH% build_script: - - go get -v -t ./... + - go get -u github.com/alecthomas/gometalinter + - gometalinter.exe --install + - gometalinter.exe --config .gometalinter.json ./... + - go get -v -d -t -tags "functional integration admin" ./... - go build ./cmd/wclayer - go build ./cmd/runhcs + - go test -c ./pkg/go-runhcs/ -tags integration + - go build ./cmd/tar2ext4 - go test -v ./... -tags admin + - go test -c ./functional/ -tags functional artifacts: - path: 'wclayer.exe' - path: 'runhcs.exe' + - path: 'go-runhcs.test.exe' + - path: 'tar2ext4.exe' + - path: 'functional.test.exe' \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/container.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/container.go index 1e31e250b5..c68745b095 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/container.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/container.go @@ -1,25 +1,25 @@ package main import ( - "bytes" "encoding/json" "errors" "fmt" - "io" - "io/ioutil" "os" "path/filepath" "strconv" "strings" - "syscall" "time" winio "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/cni" "github.com/Microsoft/hcsshim/internal/guid" "github.com/Microsoft/hcsshim/internal/hcs" "github.com/Microsoft/hcsshim/internal/hcsoci" + "github.com/Microsoft/hcsshim/internal/logfields" "github.com/Microsoft/hcsshim/internal/regstate" + "github.com/Microsoft/hcsshim/internal/runhcs" "github.com/Microsoft/hcsshim/internal/uvm" + "github.com/Microsoft/hcsshim/osversion" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "golang.org/x/sys/windows" @@ -28,17 +28,32 @@ import ( var errContainerStopped = errors.New("container is stopped") type persistedState struct { - ID string - SandboxID string - HostID string - Bundle string - Created time.Time - Rootfs string - Spec *specs.Spec - RequestedNetNS string - IsHost bool - UniqueID guid.GUID - HostUniqueID guid.GUID + // ID is the id of this container/UVM. + ID string `json:",omitempty"` + // Owner is the owner value passed into the runhcs command and may be `""`. + Owner string `json:",omitempty"` + // SandboxID is the sandbox identifer passed in via OCI specifications. This + // can either be the sandbox itself or the sandbox this container should run + // in. See `parseSandboxAnnotations`. + SandboxID string `json:",omitempty"` + // HostID will be VM ID hosting this container. If a sandbox is used it will + // match the `SandboxID`. + HostID string `json:",omitempty"` + // Bundle is the folder path on disk where the container state and spec files + // reside. + Bundle string `json:",omitempty"` + Created time.Time `json:",omitempty"` + Rootfs string `json:",omitempty"` + // Spec is the in memory deserialized values found on `Bundle\config.json`. + Spec *specs.Spec `json:",omitempty"` + RequestedNetNS string `json:",omitempty"` + // IsHost is `true` when this is a VM isolated config. + IsHost bool `json:",omitempty"` + // UniqueID is a unique ID generated per container config. + UniqueID guid.GUID `json:",omitempty"` + // HostUniqueID is the unique ID of the hosting VM if this container is + // hosted. + HostUniqueID guid.GUID `json:",omitempty"` } type containerStatus string @@ -55,6 +70,9 @@ const ( keyShimPid = "shim" keyInitPid = "pid" keyNetNS = "netns" + // keyPidMapFmt is the format to use when mapping a host OS pid to a guest + // pid. + keyPidMapFmt = "pid-%d" ) type container struct { @@ -64,32 +82,6 @@ type container struct { resources *hcsoci.Resources } -func getErrorFromPipe(pipe io.Reader, p *os.Process) error { - serr, err := ioutil.ReadAll(pipe) - if err != nil { - return err - } - - if bytes.Equal(serr, shimSuccess) { - return nil - } - - extra := "" - if p != nil { - p.Kill() - state, err := p.Wait() - if err != nil { - panic(err) - } - extra = fmt.Sprintf(", exit code %d", state.Sys().(syscall.WaitStatus).ExitCode) - } - if len(serr) == 0 { - return fmt.Errorf("unknown shim failure%s", extra) - } - - return errors.New(string(serr)) -} - func startProcessShim(id, pidFile, logFile string, spec *specs.Process) (_ *os.Process, err error) { // Ensure the stdio handles inherit to the child process. This isn't undone // after the StartProcess call because the caller never launches another @@ -109,6 +101,9 @@ func startProcessShim(id, pidFile, logFile string, spec *specs.Process) (_ *os.P if spec != nil { args = append(args, "--exec") } + if strings.HasPrefix(logFile, runhcs.SafePipePrefix) { + args = append(args, "--log-pipe", logFile) + } args = append(args, id) return launchShim("shim", pidFile, logFile, args, spec) } @@ -143,11 +138,13 @@ func launchShim(cmd, pidFile, logFile string, args []string, data interface{}) ( var log *os.File fullargs := []string{os.Args[0]} if logFile != "" { - log, err = os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666) - if err != nil { - return nil, err + if !strings.HasPrefix(logFile, runhcs.SafePipePrefix) { + log, err = os.OpenFile(logFile, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666) + if err != nil { + return nil, err + } + defer log.Close() } - defer log.Close() fullargs = append(fullargs, "--log-format", logFormat) if logrus.GetLevel() == logrus.DebugLevel { @@ -185,7 +182,7 @@ func launchShim(cmd, pidFile, logFile string, args []string, data interface{}) ( wdatap.Close() } - err = getErrorFromPipe(rp, p) + err = runhcs.GetErrorFromPipe(rp, p) if err != nil { return nil, err } @@ -199,8 +196,14 @@ func launchShim(cmd, pidFile, logFile string, args []string, data interface{}) ( return p, nil } -func parseSandboxAnnotations(spec *specs.Spec) (string, bool) { - a := spec.Annotations +// parseSandboxAnnotations searches `a` for various annotations used by +// different runtimes to represent a sandbox ID, and sandbox type. +// +// If found returns the tuple `(sandboxID, isSandbox)` where `isSandbox == true` +// indicates the identifer is the sandbox itself; `isSandbox == false` indicates +// the identifer is the sandbox in which to place this container. Otherwise +// returns `("", false)`. +func parseSandboxAnnotations(a map[string]string) (string, bool) { var t, id string if t = a["io.kubernetes.cri.container-type"]; t != "" { id = a["io.kubernetes.cri.sandbox-id"] @@ -221,37 +224,104 @@ func parseSandboxAnnotations(spec *specs.Spec) (string, bool) { return "", false } -func (c *container) startVMShim(logFile string, consolePipe string) (*os.Process, error) { - opts := &uvm.UVMOptions{ - ID: vmID(c.ID), - ConsolePipe: consolePipe, - } - if c.Spec.Windows != nil { - opts.Resources = c.Spec.Windows.Resources +// parseAnnotationsBool searches `a` for `key` and if found verifies that the +// value is `true` or `false` in any case. If `key` is not found returns `nil`. +func parseAnnotationsBool(a map[string]string, key string) *bool { + if v, ok := a[key]; ok { + yes := true + no := false + switch strings.ToLower(v) { + case "true": + return &yes + case "false": + return &no + default: + logrus.WithFields(logrus.Fields{ + logfields.OCIAnnotation: key, + logfields.Value: v, + logfields.ExpectedType: logfields.Bool, + }).Warning("annotation could not be parsed") + } } + return nil +} - if c.Spec.Linux != nil { - opts.OperatingSystem = "linux" - } else { - opts.OperatingSystem = "windows" - layers := make([]string, len(c.Spec.Windows.LayerFolders)) - for i, f := range c.Spec.Windows.LayerFolders { - if i == len(c.Spec.Windows.LayerFolders)-1 { - f = filepath.Join(f, "vm") - err := os.MkdirAll(f, 0) - if err != nil { - return nil, err - } +// parseAnnotationsPreferredRootFSType searches `a` for `key` and verifies that the +// value is in the set of allowed values. If `key` is not found returns `nil`. +// Otherwise returns the index at which it was found in allowed values. +func parseAnnotationsPreferredRootFSType(a map[string]string, key string) *uvm.PreferredRootFSType { + if v, ok := a[key]; ok { + // Following array must match enumeration uvm.PreferredRootFSType indexes + possibles := []string{"initrd", "vhd"} + for index, possible := range possibles { + if possible == v { + prfstype := uvm.PreferredRootFSType(index) + return &prfstype } - layers[i] = f } - opts.LayerFolders = layers + logrus.Warningf("annotation: '%s', with value: '%s' must be one of %+v", key, v, possibles) + return nil + + } + return nil +} + +// parseAnnotationsUint32 searches `a` for `key` and if found verifies that the +// value is a 32 bit unsigned integer. If `key` is not found returns `nil`. +func parseAnnotationsUint32(a map[string]string, key string) *uint32 { + if v, ok := a[key]; ok { + countu, err := strconv.ParseUint(v, 10, 32) + if err == nil { + v := uint32(countu) + return &v + } + logrus.WithFields(logrus.Fields{ + logfields.OCIAnnotation: key, + logfields.Value: v, + logfields.ExpectedType: logfields.Uint32, + logrus.ErrorKey: err, + }).Warning("annotation could not be parsed") } - return launchShim("vmshim", "", logFile, []string{c.VMPipePath()}, opts) + return nil +} + +// parseAnnotationsUint64 searches `a` for `key` and if found verifies that the +// value is a 64 bit unsigned integer. If `key` is not found returns `nil`. +func parseAnnotationsUint64(a map[string]string, key string) *uint64 { + if v, ok := a[key]; ok { + countu, err := strconv.ParseUint(v, 10, 64) + if err == nil { + return &countu + } + logrus.WithFields(logrus.Fields{ + logfields.OCIAnnotation: key, + logfields.Value: v, + logfields.ExpectedType: logfields.Uint64, + logrus.ErrorKey: err, + }).Warning("annotation could not be parsed") + } + return nil +} + +// startVMShim starts a vm-shim command with the specified `opts`. `opts` can be `uvm.OptionsWCOW` or `uvm.OptionsLCOW` +func (c *container) startVMShim(logFile string, opts interface{}) (*os.Process, error) { + var os string + if _, ok := opts.(*uvm.OptionsLCOW); ok { + os = "linux" + } else { + os = "windows" + } + args := []string{"--os", os} + if strings.HasPrefix(logFile, runhcs.SafePipePrefix) { + args = append(args, "--log-pipe", logFile) + } + args = append(args, c.VMPipePath()) + return launchShim("vmshim", "", logFile, args, opts) } type containerConfig struct { ID string + Owner string HostID string PidFile string ShimLogFile, VMLogFile string @@ -268,7 +338,7 @@ func createContainer(cfg *containerConfig) (_ *container, err error) { vmisolated := cfg.Spec.Linux != nil || (cfg.Spec.Windows != nil && cfg.Spec.Windows.HyperV != nil) - sandboxID, isSandbox := parseSandboxAnnotations(cfg.Spec) + sandboxID, isSandbox := parseSandboxAnnotations(cfg.Spec.Annotations) hostID := cfg.HostID if isSandbox { if sandboxID != cfg.ID { @@ -311,7 +381,8 @@ func createContainer(cfg *containerConfig) (_ *container, err error) { return nil, fmt.Errorf("host container %s is not a VM host", hostID) } hostUniqueID = host.UniqueID - } else if vmisolated && (isSandbox || cfg.Spec.Linux != nil) { + } else if vmisolated && (isSandbox || cfg.Spec.Linux != nil || osversion.Get().Build >= osversion.RS5) { + // This handles all LCOW, Pod Sandbox, and (Windows Xenon V2 for RS5+) hostID = cfg.ID newvm = true hostUniqueID = uniqueID @@ -336,12 +407,18 @@ func createContainer(cfg *containerConfig) (_ *container, err error) { } // Determine the network namespace to use. - if cfg.Spec.Windows.Network != nil && cfg.Spec.Windows.Network.NetworkSharedContainerName != "" { - err = stateKey.Get(cfg.Spec.Windows.Network.NetworkSharedContainerName, keyNetNS, &netNS) - if err != nil { - if _, ok := err.(*regstate.NoStateError); !ok { - return nil, err + 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 } } } @@ -351,6 +428,7 @@ func createContainer(cfg *containerConfig) (_ *container, err error) { c := &container{ persistedState: persistedState{ ID: cfg.ID, + Owner: cfg.Owner, Bundle: cwd, Rootfs: rootfs, Created: time.Now(), @@ -372,10 +450,89 @@ func createContainer(cfg *containerConfig) (_ *container, err error) { 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 { - shim, err := c.startVMShim(cfg.VMLogFile, cfg.VMConsolePipe) + var opts interface{} + + const ( + annotationAllowOverCommit = "io.microsoft.virtualmachine.computetopology.memory.allowovercommit" + annotationEnableDeferredCommit = "io.microsoft.virtualmachine.computetopology.memory.enabledeferredcommit" + annotationMemorySizeInMB = "io.microsoft.virtualmachine.computetopology.memory.sizeinmb" + annotationProcessorCount = "io.microsoft.virtualmachine.computetopology.processor.count" + annotationVPMemCount = "io.microsoft.virtualmachine.devices.virtualpmem.maximumcount" + annotationVPMemSize = "io.microsoft.virtualmachine.devices.virtualpmem.maximumsizebytes" + annotationPreferredRootFSType = "io.microsoft.virtualmachine.lcow.preferredrootfstype" + ) + + bothOpts := &uvm.Options{ + ID: vmID(c.ID), + Owner: cfg.Owner, + AllowOvercommit: parseAnnotationsBool(cfg.Spec.Annotations, annotationAllowOverCommit), + EnableDeferredCommit: parseAnnotationsBool(cfg.Spec.Annotations, annotationEnableDeferredCommit), + } + + // If the Resources section of the config specified mem/cpu set it now. + if cfg.Spec.Windows != nil && cfg.Spec.Windows.Resources != nil { + if cfg.Spec.Windows.Resources.Memory != nil && cfg.Spec.Windows.Resources.Memory.Limit != nil { + bothOpts.MemorySizeInMB = int32(*cfg.Spec.Windows.Resources.Memory.Limit / 1024 / 1024) + } + if cfg.Spec.Windows.Resources.CPU != nil && cfg.Spec.Windows.Resources.CPU.Count != nil { + bothOpts.ProcessorCount = int32(*cfg.Spec.Windows.Resources.CPU.Count) + } + } + // Allow overrides of any mem/cpu annotations + memSize := parseAnnotationsUint64(cfg.Spec.Annotations, annotationMemorySizeInMB) + if memSize != nil { + bothOpts.MemorySizeInMB = int32(*memSize) + } + cpuCount := parseAnnotationsUint64(cfg.Spec.Annotations, annotationProcessorCount) + if cpuCount != nil { + bothOpts.ProcessorCount = int32(*cpuCount) + } + + if cfg.Spec.Linux != nil { + opts = &uvm.OptionsLCOW{ + Options: bothOpts, + ConsolePipe: cfg.VMConsolePipe, + VPMemDeviceCount: parseAnnotationsUint32(cfg.Spec.Annotations, annotationVPMemCount), + VPMemSizeBytes: parseAnnotationsUint64(cfg.Spec.Annotations, annotationVPMemSize), + PreferredRootFSType: parseAnnotationsPreferredRootFSType(cfg.Spec.Annotations, annotationPreferredRootFSType), + } + } else { + // 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 = &uvm.OptionsWCOW{ + Options: bothOpts, + LayerFolders: layers, + } + } + + shim, err := c.startVMShim(cfg.VMLogFile, opts) if err != nil { return nil, err } @@ -386,7 +543,7 @@ func createContainer(cfg *containerConfig) (_ *container, err error) { // 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(opCreateContainer) + err = c.issueVMRequest(runhcs.OpCreateContainer) if err != nil { return nil, err } @@ -415,11 +572,11 @@ func createContainer(cfg *containerConfig) (_ *container, err error) { } func (c *container) ShimPipePath() string { - return safePipePath("runhcs-shim-" + c.UniqueID.String()) + return runhcs.SafePipePath("runhcs-shim-" + c.UniqueID.String()) } func (c *container) VMPipePath() string { - return safePipePath("runhcs-vm-" + c.HostUniqueID.String()) + return runhcs.VMPipePath(c.HostUniqueID) } func (c *container) VMIsolated() bool { @@ -450,14 +607,18 @@ func (c *container) unmountInHost(vm *uvm.UtilityVM, all bool) error { func (c *container) Unmount(all bool) error { if c.VMIsolated() { - op := opUnmountContainerDiskOnly + op := runhcs.OpUnmountContainerDiskOnly if all { - op = opUnmountContainer + op = runhcs.OpUnmountContainer } err := c.issueVMRequest(op) if err != nil { if _, ok := err.(*noVMError); ok { - logrus.Warnf("did not unmount resources for container %s because VM shim for %s could not be contacted", c.ID, c.HostID) + logrus.WithFields(logrus.Fields{ + logfields.ContainerID: c.ID, + logfields.UVMID: c.HostID, + logrus.ErrorKey: errors.New("failed to unmount container resources"), + }).Warning("VM shim could not be contacted") } else { return err } @@ -476,6 +637,7 @@ func createContainerInHost(c *container, vm *uvm.UtilityVM) (err error) { // Create the container without starting it. opts := &hcsoci.CreateOptions{ ID: c.ID, + Owner: c.Owner, Spec: c.Spec, HostingSystem: vm, NetworkNamespace: c.RequestedNetNS, @@ -484,7 +646,10 @@ func createContainerInHost(c *container, vm *uvm.UtilityVM) (err error) { if vm != nil { vmid = vm.ID() } - logrus.Infof("creating container %s (VM: '%s')", c.ID, vmid) + logrus.WithFields(logrus.Fields{ + logfields.ContainerID: c.ID, + logfields.UVMID: vmid, + }).Info("creating container in UVM") hc, resources, err := hcsoci.CreateContainer(opts) if err != nil { return err @@ -499,7 +664,7 @@ func createContainerInHost(c *container, vm *uvm.UtilityVM) (err error) { // Record the network namespace to support namespace sharing by container ID. if resources.NetNS() != "" { - err = stateKey.Set(c.ID, keyNetNS, resources.NetNS) + err = stateKey.Set(c.ID, keyNetNS, resources.NetNS()) if err != nil { return err } @@ -571,7 +736,7 @@ func (c *container) Exec() error { } defer shim.Release() - err = getErrorFromPipe(pipe, shim) + err = runhcs.GetErrorFromPipe(pipe, shim) if err != nil { return err } @@ -648,7 +813,10 @@ func (c *container) Status() (containerStatus, error) { } props, err := c.hc.Properties() if err != nil { - return "", err + if !strings.Contains(err.Error(), "operation is not valid in the current state") { + return "", err + } + return containerUnknown, nil } state := containerUnknown switch props.State { diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/create-scratch.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/create-scratch.go index 55d26b5b07..544b5dc26a 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/create-scratch.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/create-scratch.go @@ -6,8 +6,8 @@ import ( "github.com/Microsoft/hcsshim/internal/appargs" "github.com/Microsoft/hcsshim/internal/lcow" - "github.com/Microsoft/hcsshim/internal/osversion" "github.com/Microsoft/hcsshim/internal/uvm" + "github.com/Microsoft/hcsshim/osversion" gcsclient "github.com/Microsoft/opengcs/client" "github.com/pkg/errors" "github.com/urfave/cli" @@ -51,18 +51,20 @@ var createScratchCommand = cli.Command{ return errors.Wrapf(err, "failed to create ext4vhdx for '%s'", cfg.Name) } } else { - opts := uvm.UVMOptions{ - ID: "createscratch-uvm", - OperatingSystem: "linux", + opts := uvm.OptionsLCOW{ + Options: &uvm.Options{ + ID: "createscratch-uvm", + Owner: context.GlobalString("owner"), + }, } - convertUVM, err := uvm.Create(&opts) + convertUVM, err := uvm.CreateLCOW(&opts) if err != nil { return errors.Wrapf(err, "failed to create '%s'", opts.ID) } + defer convertUVM.Close() if err := convertUVM.Start(); err != nil { return errors.Wrapf(err, "failed to start '%s'", opts.ID) } - defer convertUVM.Terminate() if err := lcow.CreateScratch(convertUVM, dest, lcow.DefaultScratchSizeGB, "", ""); err != nil { return errors.Wrapf(err, "failed to create ext4vhdx for '%s'", opts.ID) diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/create.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/create.go index ec371ef5a9..5eb5b9e61a 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/create.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/create.go @@ -19,12 +19,12 @@ var createRunFlags = []cli.Flag{ cli.StringFlag{ Name: "shim-log", Value: "", - Usage: "path to the log file for the launched shim process", + Usage: `path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs--shim-log) for the launched shim process`, }, cli.StringFlag{ Name: "vm-log", Value: "", - Usage: "path to the log file for the launched VM shim process", + Usage: `path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs--vm-log) for the launched VM shim process`, }, cli.StringFlag{ Name: "vm-console", @@ -89,6 +89,7 @@ func containerConfigFromContext(context *cli.Context) (*containerConfig, error) } return &containerConfig{ ID: id, + Owner: context.GlobalString("owner"), PidFile: pidFile, ShimLogFile: shimLog, VMLogFile: vmLog, diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/delete.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/delete.go index fcb938cad2..cebea043c8 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/delete.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/delete.go @@ -44,6 +44,7 @@ status of "ubuntu01" as "stopped" the following will delete resources held for } return err } + defer container.Close() s, err := container.Status() if err != nil { return err diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/exec.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/exec.go index 8ef899bb2d..befc79f029 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/exec.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/exec.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "syscall" "github.com/Microsoft/hcsshim/internal/appargs" @@ -58,7 +59,7 @@ following will output a list of processes running in the container: cli.StringFlag{ Name: "shim-log", Value: "", - Usage: "path to the log file for the launched shim process", + Usage: `path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs---log) for the launched shim process`, }, }, Before: appargs.Validate(argID, appargs.Rest(appargs.String)), @@ -76,6 +77,7 @@ following will output a list of processes running in the container: if err != nil { return err } + defer c.Close() status, err := c.Status() if err != nil { return err @@ -146,7 +148,9 @@ func validateProcessSpec(spec *specs.Process) error { if spec.Cwd == "" { return fmt.Errorf("Cwd property must not be empty") } - if !filepath.IsAbs(spec.Cwd) { + // IsAbs doesnt recognize Unix paths on Windows builds so handle that case + // here. + if !filepath.IsAbs(spec.Cwd) && !strings.HasPrefix(spec.Cwd, "/") { return fmt.Errorf("Cwd must be an absolute path") } if len(spec.Args) == 0 { diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/kill.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/kill.go index 12a3c7e932..b82128255a 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/kill.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/kill.go @@ -1,7 +1,15 @@ package main import ( + "strconv" + "strings" + "github.com/Microsoft/hcsshim/internal/appargs" + "github.com/Microsoft/hcsshim/internal/guestrequest" + "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/schema1" + "github.com/Microsoft/hcsshim/osversion" + "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -26,6 +34,7 @@ signal to the init process of the "ubuntu01" container: if err != nil { return err } + defer c.Close() status, err := c.Status() if err != nil { return err @@ -34,9 +43,40 @@ signal to the init process of the "ubuntu01" container: return errContainerStopped } - sigstr := context.Args().Get(1) - if sigstr == "" { - sigstr = "SIGTERM" + signalsSupported := false + + // The Signal feature was added in RS5 + if osversion.Get().Build >= osversion.RS5 { + if c.IsHost || c.HostID != "" { + var hostID string + if c.IsHost { + // This is the LCOW, Pod Sandbox, or Windows Xenon V2 for RS5+ + hostID = vmID(c.ID) + } else { + // This is the Nth container in a Pod + hostID = c.HostID + } + uvm, err := hcs.OpenComputeSystem(hostID) + if err != nil { + return err + } + defer uvm.Close() + if props, err := uvm.Properties(schema1.PropertyTypeGuestConnection); err == nil && + props.GuestConnectionInfo.GuestDefinedCapabilities.SignalProcessSupported { + signalsSupported = true + } + } else if c.Spec.Linux == nil && c.Spec.Windows.HyperV == nil { + // RS5+ Windows Argon + signalsSupported = true + } + } + + signal := 0 + if signalsSupported { + signal, err = validateSigstr(context.Args().Get(1), signalsSupported, c.Spec.Linux != nil) + if err != nil { + return err + } } var pid int @@ -49,6 +89,78 @@ signal to the init process of the "ubuntu01" container: return err } defer p.Close() - return p.Kill() // BUGBUG: should be Signal + + if signalsSupported && (c.Spec.Linux != nil || !c.Spec.Process.Terminal) { + opts := guestrequest.SignalProcessOptions{ + Signal: signal, + } + return p.Signal(opts) + } + + // Legacy signal issue a kill + return p.Kill() }, } + +func validateSigstr(sigstr string, signalsSupported bool, isLcow bool) (int, error) { + errInvalidSignal := errors.Errorf("invalid signal '%s'", sigstr) + + // All flavors including legacy default to SIGTERM on LCOW CtrlC on Windows + if sigstr == "" { + if isLcow { + return 0xf, nil + } + return 0, nil + } + + sigstr = strings.ToUpper(sigstr) + + if !signalsSupported { + if isLcow { + switch sigstr { + case "15": + fallthrough + case "TERM": + fallthrough + case "SIGTERM": + return 0xf, nil + default: + return 0, errInvalidSignal + } + } + switch sigstr { + case "0": + fallthrough + case "CTRLC": + return 0x0, nil + default: + return 0, errInvalidSignal + } + } + + var sigmap map[string]int + if isLcow { + sigmap = signalMapLcow + } else { + sigmap = signalMapWindows + } + + signal, err := strconv.Atoi(sigstr) + if err != nil { + // Signal might still match the string value + for k, v := range sigmap { + if k == sigstr { + return v, nil + } + } + return 0, errInvalidSignal + } + + // Match signal by value + for _, v := range sigmap { + if signal == v { + return signal, nil + } + } + return 0, errInvalidSignal +} diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/kill_test.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/kill_test.go new file mode 100644 index 0000000000..ab0b3dcbbc --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/kill_test.go @@ -0,0 +1,91 @@ +package main + +import ( + "fmt" + "strconv" + "strings" + "testing" +) + +func runValidateSigstrTest(sigstr string, signalsSupported, isLcow bool, + expectedSignal int, expectedError bool, t *testing.T) { + signal, err := validateSigstr(sigstr, signalsSupported, isLcow) + if expectedError { + if err == nil { + t.Fatalf("Expected err: %v, got: nil", expectedError) + } else if err.Error() != fmt.Sprintf("invalid signal '%s'", sigstr) { + t.Fatalf("Expected err: %v, got: %v", expectedError, err) + } + } + if signal != expectedSignal { + t.Fatalf("Test - Signal: %s, Support: %v, LCOW: %v\nExpected signal: %v, got: %v", + sigstr, signalsSupported, isLcow, + expectedSignal, signal) + } +} + +func TestValidateSigstrEmpty(t *testing.T) { + runValidateSigstrTest("", false, false, 0, false, t) + runValidateSigstrTest("", false, true, 0xf, false, t) + runValidateSigstrTest("", true, false, 0, false, t) + runValidateSigstrTest("", true, true, 0xf, false, t) +} + +func TestValidateSigstrDefaultLCOW(t *testing.T) { + runValidateSigstrTest("15", false, true, 0xf, false, t) + runValidateSigstrTest("TERM", false, true, 0xf, false, t) + runValidateSigstrTest("SIGTERM", false, true, 0xf, false, t) +} + +func TestValidateSigstrDefaultLCOWInvalid(t *testing.T) { + runValidateSigstrTest("2", false, true, 0, true, t) + runValidateSigstrTest("test", false, true, 0, true, t) +} + +func TestValidateSigstrDefaultWCOW(t *testing.T) { + runValidateSigstrTest("0", false, false, 0x0, false, t) + runValidateSigstrTest("CTRLC", false, false, 0x0, false, t) +} + +func TestValidateSigstrDefaultWCOWInvalid(t *testing.T) { + runValidateSigstrTest("15", false, false, 0, true, t) + runValidateSigstrTest("test", false, false, 0, true, t) +} + +func TestValidateSignalStringLCOW(t *testing.T) { + for k, v := range signalMapLcow { + runValidateSigstrTest(k, true, true, v, false, t) + // run it again with a case not in the map + lc := strings.ToLower(k) + if k == lc { + t.Fatalf("Expected lower casing - map: %v, got: %v", k, lc) + } + runValidateSigstrTest(lc, true, true, v, false, t) + } +} + +func TestValidateSignalStringWCOW(t *testing.T) { + for k, v := range signalMapWindows { + runValidateSigstrTest(k, true, false, v, false, t) + // run it again with a case not in the map + lc := strings.ToLower(k) + if k == lc { + t.Fatalf("Expected lower casing - map: %v, got: %v", k, lc) + } + runValidateSigstrTest(lc, true, false, v, false, t) + } +} + +func TestValidateSignalValueLCOW(t *testing.T) { + for _, v := range signalMapLcow { + str := strconv.Itoa(v) + runValidateSigstrTest(str, true, true, v, false, t) + } +} + +func TestValidateSignalValueWCOW(t *testing.T) { + for _, v := range signalMapWindows { + str := strconv.Itoa(v) + runValidateSigstrTest(str, true, false, v, false, t) + } +} diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/list.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/list.go index c666d8ab62..a1b1a7555e 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/list.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/list.go @@ -110,6 +110,7 @@ func getContainers(context *cli.Context) ([]runhcs.ContainerState, error) { Created: c.Created, Annotations: c.Spec.Annotations, }) + c.Close() } return s, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/main.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/main.go index eb395f354e..cc165e4028 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/main.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/main.go @@ -6,9 +6,10 @@ import ( "os" "strings" + "github.com/Microsoft/go-winio" "github.com/Microsoft/hcsshim/internal/regstate" + "github.com/Microsoft/hcsshim/internal/runhcs" "github.com/opencontainers/runtime-spec/specs-go" - "github.com/sirupsen/logrus" "github.com/urfave/cli" ) @@ -71,7 +72,7 @@ func main() { cli.StringFlag{ Name: "log", Value: "nul", - Usage: "set the log file path where internal debug information is written", + Usage: `set the log file path or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs-log) where internal debug information is written`, }, cli.StringFlag{ Name: "log-format", @@ -105,7 +106,6 @@ func main() { shimCommand, startCommand, stateCommand, - tarToVhdCommand, // updateCommand, vmshimCommand, } @@ -114,7 +114,13 @@ func main() { logrus.SetLevel(logrus.DebugLevel) } if path := context.GlobalString("log"); path != "" { - f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666) + var f io.Writer + var err error + if strings.HasPrefix(path, runhcs.SafePipePrefix) { + f, err = winio.DialPipe(path, nil) + } else { + f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND|os.O_SYNC, 0666) + } if err != nil { return err } diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/pause.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/pause.go index 3e5c2b240e..f9b3654192 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/pause.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/pause.go @@ -22,6 +22,7 @@ Use runhcs list to identify instances of containers and their current status.`, if err != nil { return err } + defer container.Close() if err := container.hc.Pause(); err != nil { return err } @@ -47,6 +48,7 @@ Use runhcs list to identify instances of containers and their current status.`, if err != nil { return err } + defer container.Close() if err := container.hc.Resume(); err != nil { return err } diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/ps.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/ps.go index 3f47da7158..2f8bf1fb83 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/ps.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/ps.go @@ -28,6 +28,7 @@ var psCommand = cli.Command{ if err != nil { return err } + defer container.Close() props, err := container.hc.Properties(schema1.PropertyTypeProcessList) if err != nil { diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/shim.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/shim.go index 15811ad31e..321ce4d144 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/shim.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/shim.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "fmt" "io" "io/ioutil" "net" @@ -14,6 +15,7 @@ import ( "github.com/Microsoft/hcsshim/internal/appargs" "github.com/Microsoft/hcsshim/internal/hcs" "github.com/Microsoft/hcsshim/internal/lcow" + "github.com/Microsoft/hcsshim/internal/runhcs" "github.com/Microsoft/hcsshim/internal/schema2" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" @@ -22,7 +24,7 @@ import ( ) func containerPipePath(id string) string { - return safePipePath("runhcs-shim-" + id) + return runhcs.SafePipePath("runhcs-shim-" + id) } func newFile(context *cli.Context, param string) *os.File { @@ -42,10 +44,21 @@ var shimCommand = cli.Command{ &cli.IntFlag{Name: "stdout", Hidden: true}, &cli.IntFlag{Name: "stderr", Hidden: true}, &cli.BoolFlag{Name: "exec", Hidden: true}, + cli.StringFlag{Name: "log-pipe", Hidden: true}, }, Before: appargs.Validate(argID), Action: func(context *cli.Context) error { - logrus.SetOutput(os.Stderr) + logPipe := context.String("log-pipe") + if logPipe != "" { + lpc, err := winio.DialPipe(logPipe, nil) + if err != nil { + return err + } + defer lpc.Close() + logrus.SetOutput(lpc) + } else { + logrus.SetOutput(os.Stderr) + } fatalWriter.Writer = os.Stdout id := context.Args().First() @@ -53,6 +66,7 @@ var shimCommand = cli.Command{ if err != nil { return err } + defer c.Close() // Asynchronously wait for the container to exit. containerExitCh := make(chan error) @@ -98,7 +112,7 @@ var shimCommand = cli.Command{ // Alert the parent process that initialization has completed // successfully. - errorOut.Write(shimSuccess) + errorOut.Write(runhcs.ShimSuccess) errorOut.Close() fatalWriter.Writer = ioutil.Discard @@ -164,6 +178,7 @@ var shimCommand = cli.Command{ WorkingDirectory: spec.Cwd, EmulateConsole: spec.Terminal, Environment: environment, + User: spec.User.Username, } for i, arg := range spec.Args { e := windows.EscapeArg(arg) @@ -173,6 +188,12 @@ var shimCommand = cli.Command{ wpp.CommandLine += " " + e } } + if spec.ConsoleSize != nil { + wpp.ConsoleSize = []int32{ + int32(spec.ConsoleSize.Height), + int32(spec.ConsoleSize.Width), + } + } wpp.CreateStdInPipe = stdin != nil wpp.CreateStdOutPipe = stdout != nil @@ -209,11 +230,21 @@ var shimCommand = cli.Command{ } } + // Store the Guest pid map + err = stateKey.Set(c.ID, fmt.Sprintf(keyPidMapFmt, os.Getpid()), p.Pid()) + if err != nil { + return err + } + defer func() { + // Remove the Guest pid map when this process is cleaned up + stateKey.Clear(c.ID, fmt.Sprintf(keyPidMapFmt, os.Getpid())) + }() + terminateOnFailure = false // Alert the connected process that the process was launched // successfully. - errorOut.Write(shimSuccess) + errorOut.Write(runhcs.ShimSuccess) errorOut.Close() fatalWriter.Writer = ioutil.Discard diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/signalmap.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/signalmap.go index b00303428e..edc73e01d5 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/signalmap.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/signalmap.go @@ -1,6 +1,6 @@ package main -var signalMap = map[string]int{ +var signalMapLcow = map[string]int{ "ABRT": 0x6, "ALRM": 0xe, "BUS": 0x7, @@ -36,3 +36,11 @@ var signalMap = map[string]int{ "XCPU": 0x18, "XFSZ": 0x19, } + +var signalMapWindows = map[string]int{ + "CTRLC": 0x0, + "CTRLBREAK": 0x1, + "CTRLCLOSE": 0x2, + "CTRLLOGOFF": 0x5, + "CTRLSHUTDOWN": 0x6, +} diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/start.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/start.go index b510c88363..d5d004cdf5 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/start.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/start.go @@ -24,6 +24,7 @@ your host.`, if err != nil { return err } + defer container.Close() status, err := container.Status() if err != nil { return err diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/state.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/state.go index 2f45a684a4..bae1c3deea 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/state.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/state.go @@ -3,7 +3,6 @@ package main import ( "encoding/json" "os" - "strings" "github.com/Microsoft/hcsshim/internal/appargs" "github.com/Microsoft/hcsshim/internal/runhcs" @@ -25,12 +24,10 @@ instance of a container.`, if err != nil { return err } + defer c.Close() status, err := c.Status() if err != nil { - if !strings.Contains(err.Error(), "operation is not valid in the current state") { - return err - } - status = containerUnknown + return err } cs := runhcs.ContainerState{ Version: c.Spec.Version, diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/tty.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/tty.go index 2c5d4ac670..0b9e43e4f7 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/tty.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/tty.go @@ -1,6 +1,7 @@ package main import ( + "fmt" "strconv" "github.com/Microsoft/hcsshim/internal/appargs" @@ -37,6 +38,11 @@ var resizeTtyCommand = cli.Command{ if err := stateKey.Get(id, keyInitPid, &pid); err != nil { return err } + } else { + // If a pid was provided map it to its hcs pid. + if err := stateKey.Get(id, fmt.Sprintf(keyPidMapFmt, pid), &pid); err != nil { + return err + } } p, err := c.hc.OpenProcess(pid) diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/utils.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/utils.go index fb29f253a0..846dd73351 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/utils.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/utils.go @@ -3,21 +3,25 @@ package main import ( "fmt" "net" - "net/url" "os" "path/filepath" + "strings" "github.com/Microsoft/hcsshim/internal/appargs" + "github.com/Microsoft/hcsshim/internal/runhcs" ) -var shimSuccess = []byte{0, 'O', 'K', 0} - var argID = appargs.NonEmptyString func absPathOrEmpty(path string) (string, error) { if path == "" { return "", nil } + if strings.HasPrefix(path, runhcs.SafePipePrefix) { + if len(path) > len(runhcs.SafePipePrefix) { + return runhcs.SafePipePath(path[len(runhcs.SafePipePrefix):]), nil + } + } return filepath.Abs(path) } @@ -41,12 +45,6 @@ func createPidFile(path string, pid int) error { return os.Rename(tmpName, path) } -func safePipePath(name string) string { - // Use a pipe in the Administrators protected prefixed to prevent malicious - // squatting. - return `\\.\pipe\ProtectedPrefix\Administrators\` + url.PathEscape(name) -} - func closeWritePipe(pipe net.Conn) error { return pipe.(interface { CloseWrite() error diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/utils_test.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/utils_test.go new file mode 100644 index 0000000000..8fbebcdae2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/utils_test.go @@ -0,0 +1,39 @@ +package main + +import ( + "os" + "testing" + + "github.com/Microsoft/hcsshim/internal/runhcs" +) + +func Test_AbsPathOrEmpty(t *testing.T) { + wd, err := os.Getwd() + if err != nil { + t.Fatalf("failed to get test wd: %v", err) + } + + tests := []string{ + "", + runhcs.SafePipePrefix + "test", + runhcs.SafePipePrefix + "test with spaces", + "test", + "C:\\test..\\test", + } + expected := []string{ + "", + runhcs.SafePipePrefix + "test", + runhcs.SafePipePrefix + "test%20with%20spaces", + wd + "\\test", + "C:\\test..\\test", + } + for i, test := range tests { + actual, err := absPathOrEmpty(test) + if err != nil { + t.Fatalf("absPathOrEmpty: error '%v'", err) + } + if actual != expected[i] { + t.Fatalf("absPathOrEmpty: actual '%s' != '%s'", actual, expected[i]) + } + } +} diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/vm.go b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/vm.go index ed60f1567b..582a600997 100644 --- a/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/vm.go +++ b/vendor/github.com/Microsoft/hcsshim/cmd/runhcs/vm.go @@ -10,7 +10,10 @@ import ( winio "github.com/Microsoft/go-winio" "github.com/Microsoft/hcsshim/internal/appargs" + "github.com/Microsoft/hcsshim/internal/logfields" + "github.com/Microsoft/hcsshim/internal/runhcs" "github.com/Microsoft/hcsshim/internal/uvm" + "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) @@ -23,10 +26,23 @@ var vmshimCommand = cli.Command{ Name: "vmshim", Usage: `launch a VM and containers inside it (do not call it outside of runhcs)`, Hidden: true, - Flags: []cli.Flag{}, + Flags: []cli.Flag{ + cli.StringFlag{Name: "log-pipe", Hidden: true}, + cli.StringFlag{Name: "os", Hidden: true}, + }, Before: appargs.Validate(argID), Action: func(context *cli.Context) error { - logrus.SetOutput(os.Stderr) + logPipe := context.String("log-pipe") + if logPipe != "" { + lpc, err := winio.DialPipe(logPipe, nil) + if err != nil { + return err + } + defer lpc.Close() + logrus.SetOutput(lpc) + } else { + logrus.SetOutput(os.Stderr) + } fatalWriter.Writer = os.Stdout pipePath := context.Args().First() @@ -37,7 +53,14 @@ var vmshimCommand = cli.Command{ } os.Stdin.Close() - opts := &uvm.UVMOptions{} + var opts interface{} + isLCOW := context.String("os") == "linux" + if isLCOW { + opts = &uvm.OptionsLCOW{} + } else { + opts = &uvm.OptionsWCOW{} + } + err = json.Unmarshal(optsj, opts) if err != nil { return err @@ -49,10 +72,19 @@ var vmshimCommand = cli.Command{ return err } - vm, err := startVM(opts) + var vm *uvm.UtilityVM + if isLCOW { + vm, err = uvm.CreateLCOW(opts.(*uvm.OptionsLCOW)) + } else { + vm, err = uvm.CreateWCOW(opts.(*uvm.OptionsWCOW)) + } if err != nil { return err } + defer vm.Close() + if err = vm.Start(); err != nil { + return err + } // Asynchronously wait for the VM to exit. exitCh := make(chan error) @@ -64,7 +96,7 @@ var vmshimCommand = cli.Command{ // Alert the parent process that initialization has completed // successfully. - os.Stdout.Write(shimSuccess) + os.Stdout.Write(runhcs.ShimSuccess) os.Stdout.Close() fatalWriter.Writer = ioutil.Discard @@ -87,7 +119,7 @@ var vmshimCommand = cli.Command{ case pipe := <-pipeCh: err = processRequest(vm, pipe) if err == nil { - _, err = pipe.Write(shimSuccess) + _, err = pipe.Write(runhcs.ShimSuccess) // Wait until the pipe is closed before closing the // container so that it is properly handed off to the other // process. @@ -98,7 +130,8 @@ var vmshimCommand = cli.Command{ ioutil.ReadAll(pipe) } } else { - logrus.Error("failed creating container in VM: ", err) + logrus.WithError(err). + Error("failed creating container in VM") fmt.Fprintf(pipe, "%v", err) } pipe.Close() @@ -107,39 +140,16 @@ var vmshimCommand = cli.Command{ }, } -type vmRequestOp string - -const ( - opCreateContainer vmRequestOp = "create" - opUnmountContainer vmRequestOp = "unmount" - opUnmountContainerDiskOnly vmRequestOp = "unmount-disk" -) - -type vmRequest struct { - ID string - Op vmRequestOp -} - -func startVM(opts *uvm.UVMOptions) (*uvm.UtilityVM, error) { - vm, err := uvm.Create(opts) - if err != nil { - return nil, err - } - err = vm.Start() - if err != nil { - vm.Close() - return nil, err - } - return vm, nil -} - func processRequest(vm *uvm.UtilityVM, pipe net.Conn) error { - var req vmRequest + var req runhcs.VMRequest err := json.NewDecoder(pipe).Decode(&req) if err != nil { return err } - logrus.Debug("received operation ", req.Op, " for ", req.ID) + logrus.WithFields(logrus.Fields{ + logfields.ContainerID: req.ID, + logfields.VMShimOperation: req.Op, + }).Debug("process request") c, err := getContainer(req.ID, false) if err != nil { return err @@ -150,7 +160,7 @@ func processRequest(vm *uvm.UtilityVM, pipe net.Conn) error { } }() switch req.Op { - case opCreateContainer: + case runhcs.OpCreateContainer: err = createContainerInHost(c, vm) if err != nil { return err @@ -161,14 +171,15 @@ func processRequest(vm *uvm.UtilityVM, pipe net.Conn) error { c2.hc.Wait() c2.Close() }() - c = nil - case opUnmountContainer, opUnmountContainerDiskOnly: - err = c.unmountInHost(vm, req.Op == opUnmountContainer) + case runhcs.OpUnmountContainer, runhcs.OpUnmountContainerDiskOnly: + err = c.unmountInHost(vm, req.Op == runhcs.OpUnmountContainer) if err != nil { return err } + case runhcs.OpSyncNamespace: + return errors.New("Not implemented") default: panic("unknown operation") } @@ -183,25 +194,15 @@ func (err *noVMError) Error() string { return "VM " + err.ID + " cannot be contacted" } -func (c *container) issueVMRequest(op vmRequestOp) error { - pipe, err := winio.DialPipe(c.VMPipePath(), nil) - if err != nil { - if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND { - return &noVMError{c.HostID} - } - return err - } - defer pipe.Close() - req := vmRequest{ +func (c *container) issueVMRequest(op runhcs.VMRequestOp) error { + req := runhcs.VMRequest{ ID: c.ID, Op: op, } - err = json.NewEncoder(pipe).Encode(&req) - if err != nil { - return err - } - err = getErrorFromPipe(pipe, nil) - if err != nil { + if err := runhcs.IssueVMRequest(c.VMPipePath(), &req); err != nil { + if perr, ok := err.(*os.PathError); ok && perr.Err == syscall.ERROR_FILE_NOT_FOUND { + return &noVMError{c.HostID} + } return err } return nil diff --git a/vendor/github.com/Microsoft/hcsshim/cmd/tar2ext4/tar2ext4.go b/vendor/github.com/Microsoft/hcsshim/cmd/tar2ext4/tar2ext4.go new file mode 100644 index 0000000000..9f298d2ae8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/cmd/tar2ext4/tar2ext4.go @@ -0,0 +1,64 @@ +package main + +import ( + "flag" + "fmt" + "io" + "io/ioutil" + "os" + + "github.com/Microsoft/hcsshim/ext4/tar2ext4" +) + +var ( + input = flag.String("i", "", "input file") + output = flag.String("o", "", "output file") + overlay = flag.Bool("overlay", false, "produce overlayfs-compatible layer image") + vhd = flag.Bool("vhd", false, "add a VHD footer to the end of the image") + inlineData = flag.Bool("inline", false, "write small file data into the inode; not compatible with DAX") +) + +func main() { + flag.Parse() + if flag.NArg() != 0 || len(*output) == 0 { + flag.Usage() + os.Exit(1) + } + + err := func() (err error) { + in := os.Stdin + if *input != "" { + in, err = os.Open(*input) + if err != nil { + return err + } + } + out, err := os.Create(*output) + if err != nil { + return err + } + + var opts []tar2ext4.Option + if *overlay { + opts = append(opts, tar2ext4.ConvertWhiteout) + } + if *vhd { + opts = append(opts, tar2ext4.AppendVhdFooter) + } + if *inlineData { + opts = append(opts, tar2ext4.InlineData) + } + err = tar2ext4.Convert(in, out, opts...) + if err != nil { + return err + } + + // Exhaust the tar stream. + io.Copy(ioutil.Discard, in) + return nil + }() + if err != nil { + fmt.Fprintln(os.Stderr, err) + os.Exit(1) + } +} diff --git a/vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/compact.go b/vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/compact.go new file mode 100644 index 0000000000..f2274fd4cf --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/compact.go @@ -0,0 +1,1263 @@ +package compactext4 + +import ( + "bufio" + "bytes" + "encoding/binary" + "errors" + "fmt" + "io" + "path" + "sort" + "strings" + "time" + + "github.com/Microsoft/hcsshim/ext4/internal/format" +) + +// Writer writes a compact ext4 file system. +type Writer struct { + f io.ReadWriteSeeker + bw *bufio.Writer + inodes []*inode + curName string + curInode *inode + pos int64 + dataWritten, dataMax int64 + err error + initialized bool + supportInlineData bool + maxDiskSize int64 + gdBlocks uint32 +} + +// Mode flags for Linux files. +const ( + S_IXOTH = format.S_IXOTH + S_IWOTH = format.S_IWOTH + S_IROTH = format.S_IROTH + S_IXGRP = format.S_IXGRP + S_IWGRP = format.S_IWGRP + S_IRGRP = format.S_IRGRP + S_IXUSR = format.S_IXUSR + S_IWUSR = format.S_IWUSR + S_IRUSR = format.S_IRUSR + S_ISVTX = format.S_ISVTX + S_ISGID = format.S_ISGID + S_ISUID = format.S_ISUID + S_IFIFO = format.S_IFIFO + S_IFCHR = format.S_IFCHR + S_IFDIR = format.S_IFDIR + S_IFBLK = format.S_IFBLK + S_IFREG = format.S_IFREG + S_IFLNK = format.S_IFLNK + S_IFSOCK = format.S_IFSOCK + + TypeMask = format.TypeMask +) + +type inode struct { + Size int64 + Atime, Ctime, Mtime, Crtime uint64 + Number format.InodeNumber + Mode uint16 + Uid, Gid uint32 + LinkCount uint32 + XattrBlock uint32 + BlockCount uint32 + Devmajor, Devminor uint32 + Flags format.InodeFlag + Data []byte + XattrInline []byte + Children directory +} + +func (node *inode) FileType() uint16 { + return node.Mode & format.TypeMask +} + +func (node *inode) IsDir() bool { + return node.FileType() == S_IFDIR +} + +// A File represents a file to be added to an ext4 file system. +type File struct { + Linkname string + Size int64 + Mode uint16 + Uid, Gid uint32 + Atime, Ctime, Mtime, Crtime time.Time + Devmajor, Devminor uint32 + Xattrs map[string][]byte +} + +const ( + inodeFirst = 11 + inodeLostAndFound = inodeFirst + + blockSize = 4096 + blocksPerGroup = blockSize * 8 + inodeSize = 256 + maxInodesPerGroup = blockSize * 8 // Limited by the inode bitmap + inodesPerGroupIncrement = blockSize / inodeSize + + defaultMaxDiskSize = 16 * 1024 * 1024 * 1024 // 16GB + maxMaxDiskSize = 16 * 1024 * 1024 * 1024 * 1024 // 16TB + + groupDescriptorSize = 32 // Use the small group descriptor + groupsPerDescriptorBlock = blockSize / groupDescriptorSize + + maxFileSize = 128 * 1024 * 1024 * 1024 // 128GB file size maximum for now + smallSymlinkSize = 59 // max symlink size that goes directly in the inode + maxBlocksPerExtent = 0x8000 // maximum number of blocks in an extent + inodeDataSize = 60 + inodeUsedSize = 152 // fields through CrtimeExtra + inodeExtraSize = inodeSize - inodeUsedSize + xattrInodeOverhead = 4 + 4 // magic number + empty next entry value + xattrBlockOverhead = 32 + 4 // header + empty next entry value + inlineDataXattrOverhead = xattrInodeOverhead + 16 + 4 // entry + "data" + inlineDataSize = inodeDataSize + inodeExtraSize - inlineDataXattrOverhead +) + +type exceededMaxSizeError struct { + Size int64 +} + +func (err exceededMaxSizeError) Error() string { + return fmt.Sprintf("disk exceeded maximum size of %d bytes", err.Size) +} + +var directoryEntrySize = binary.Size(format.DirectoryEntry{}) +var extraIsize = uint16(inodeUsedSize - 128) + +type directory map[string]*inode + +func splitFirst(p string) (string, string) { + n := strings.IndexByte(p, '/') + if n >= 0 { + return p[:n], p[n+1:] + } + return p, "" +} + +func (w *Writer) findPath(root *inode, p string) *inode { + inode := root + for inode != nil && len(p) != 0 { + name, rest := splitFirst(p) + p = rest + inode = inode.Children[name] + } + return inode +} + +func timeToFsTime(t time.Time) uint64 { + if t.IsZero() { + return 0 + } + s := t.Unix() + if s < -0x80000000 { + return 0x80000000 + } + if s > 0x37fffffff { + return 0x37fffffff + } + return uint64(s) | uint64(t.Nanosecond())<<34 +} + +func fsTimeToTime(t uint64) time.Time { + if t == 0 { + return time.Time{} + } + s := int64(t & 0x3ffffffff) + if s > 0x7fffffff && s < 0x100000000 { + s = int64(int32(uint32(s))) + } + return time.Unix(s, int64(t>>34)) +} + +func (w *Writer) getInode(i format.InodeNumber) *inode { + if i == 0 || int(i) > len(w.inodes) { + return nil + } + return w.inodes[i-1] +} + +var xattrPrefixes = []struct { + Index uint8 + Prefix string +}{ + {2, "system.posix_acl_access"}, + {3, "system.posix_acl_default"}, + {8, "system.richacl"}, + {7, "system."}, + {1, "user."}, + {4, "trusted."}, + {6, "security."}, +} + +func compressXattrName(name string) (uint8, string) { + for _, p := range xattrPrefixes { + if strings.HasPrefix(name, p.Prefix) { + return p.Index, name[len(p.Prefix):] + } + } + return 0, name +} + +func decompressXattrName(index uint8, name string) string { + for _, p := range xattrPrefixes { + if index == p.Index { + return p.Prefix + name + } + } + return name +} + +func hashXattrEntry(name string, value []byte) uint32 { + var hash uint32 + for i := 0; i < len(name); i++ { + hash = (hash << 5) ^ (hash >> 27) ^ uint32(name[i]) + } + + for i := 0; i+3 < len(value); i += 4 { + hash = (hash << 16) ^ (hash >> 16) ^ binary.LittleEndian.Uint32(value[i:i+4]) + } + + if len(value)%4 != 0 { + var last [4]byte + copy(last[:], value[len(value)&^3:]) + hash = (hash << 16) ^ (hash >> 16) ^ binary.LittleEndian.Uint32(last[:]) + } + return hash +} + +type xattr struct { + Name string + Index uint8 + Value []byte +} + +func (x *xattr) EntryLen() int { + return (len(x.Name)+3)&^3 + 16 +} + +func (x *xattr) ValueLen() int { + return (len(x.Value) + 3) &^ 3 +} + +type xattrState struct { + inode, block []xattr + inodeLeft, blockLeft int +} + +func (s *xattrState) init() { + s.inodeLeft = inodeExtraSize - xattrInodeOverhead + s.blockLeft = blockSize - xattrBlockOverhead +} + +func (s *xattrState) addXattr(name string, value []byte) bool { + index, name := compressXattrName(name) + x := xattr{ + Index: index, + Name: name, + Value: value, + } + length := x.EntryLen() + x.ValueLen() + if s.inodeLeft >= length { + s.inode = append(s.inode, x) + s.inodeLeft -= length + } else if s.blockLeft >= length { + s.block = append(s.block, x) + s.blockLeft -= length + } else { + return false + } + return true +} + +func putXattrs(xattrs []xattr, b []byte, offsetDelta uint16) { + offset := uint16(len(b)) + offsetDelta + eb := b + db := b + for _, xattr := range xattrs { + vl := xattr.ValueLen() + offset -= uint16(vl) + eb[0] = uint8(len(xattr.Name)) + eb[1] = xattr.Index + binary.LittleEndian.PutUint16(eb[2:], offset) + binary.LittleEndian.PutUint32(eb[8:], uint32(len(xattr.Value))) + binary.LittleEndian.PutUint32(eb[12:], hashXattrEntry(xattr.Name, xattr.Value)) + copy(eb[16:], xattr.Name) + eb = eb[xattr.EntryLen():] + copy(db[len(db)-vl:], xattr.Value) + db = db[:len(db)-vl] + } +} + +func getXattrs(b []byte, xattrs map[string][]byte, offsetDelta uint16) { + eb := b + for len(eb) != 0 { + nameLen := eb[0] + if nameLen == 0 { + break + } + index := eb[1] + offset := binary.LittleEndian.Uint16(eb[2:]) - offsetDelta + valueLen := binary.LittleEndian.Uint32(eb[8:]) + attr := xattr{ + Index: index, + Name: string(eb[16 : 16+nameLen]), + Value: b[offset : uint32(offset)+valueLen], + } + xattrs[decompressXattrName(index, attr.Name)] = attr.Value + eb = eb[attr.EntryLen():] + } +} + +func (w *Writer) writeXattrs(inode *inode, state *xattrState) error { + // Write the inline attributes. + if len(state.inode) != 0 { + inode.XattrInline = make([]byte, inodeExtraSize) + binary.LittleEndian.PutUint32(inode.XattrInline[0:], format.XAttrHeaderMagic) // Magic + putXattrs(state.inode, inode.XattrInline[4:], 0) + } + + // Write the block attributes. If there was previously an xattr block, then + // rewrite it even if it is now empty. + if len(state.block) != 0 || inode.XattrBlock != 0 { + sort.Slice(state.block, func(i, j int) bool { + return state.block[i].Index < state.block[j].Index || + len(state.block[i].Name) < len(state.block[j].Name) || + state.block[i].Name < state.block[j].Name + }) + + var b [blockSize]byte + binary.LittleEndian.PutUint32(b[0:], format.XAttrHeaderMagic) // Magic + binary.LittleEndian.PutUint32(b[4:], 1) // ReferenceCount + binary.LittleEndian.PutUint32(b[8:], 1) // Blocks + putXattrs(state.block, b[32:], 32) + + orig := w.block() + if inode.XattrBlock == 0 { + inode.XattrBlock = orig + inode.BlockCount++ + } else { + // Reuse the original block. + w.seekBlock(inode.XattrBlock) + defer w.seekBlock(orig) + } + + if _, err := w.write(b[:]); err != nil { + return err + } + } + + return nil +} + +func (w *Writer) write(b []byte) (int, error) { + if w.err != nil { + return 0, w.err + } + if w.pos+int64(len(b)) > w.maxDiskSize { + w.err = exceededMaxSizeError{w.maxDiskSize} + return 0, w.err + } + n, err := w.bw.Write(b) + w.pos += int64(n) + w.err = err + return n, err +} + +func (w *Writer) zero(n int64) (int64, error) { + if w.err != nil { + return 0, w.err + } + if w.pos+int64(n) > w.maxDiskSize { + w.err = exceededMaxSizeError{w.maxDiskSize} + return 0, w.err + } + n, err := io.CopyN(w.bw, zero, n) + w.pos += n + w.err = err + return n, err +} + +func (w *Writer) makeInode(f *File, node *inode) (*inode, error) { + mode := f.Mode + if mode&format.TypeMask == 0 { + mode |= format.S_IFREG + } + typ := mode & format.TypeMask + ino := format.InodeNumber(len(w.inodes) + 1) + if node == nil { + node = &inode{ + Number: ino, + } + if typ == S_IFDIR { + node.Children = make(directory) + node.LinkCount = 1 // A directory is linked to itself. + } + } else if node.Flags&format.InodeFlagExtents != 0 { + // Since we cannot deallocate or reuse blocks, don't allow updates that + // would invalidate data that has already been written. + return nil, errors.New("cannot overwrite file with non-inline data") + } + node.Mode = mode + node.Uid = f.Uid + node.Gid = f.Gid + node.Flags = format.InodeFlagHugeFile + node.Atime = timeToFsTime(f.Atime) + node.Ctime = timeToFsTime(f.Ctime) + node.Mtime = timeToFsTime(f.Mtime) + node.Crtime = timeToFsTime(f.Crtime) + node.Devmajor = f.Devmajor + node.Devminor = f.Devminor + node.Data = nil + node.XattrInline = nil + + var xstate xattrState + xstate.init() + + var size int64 + switch typ { + case format.S_IFREG: + size = f.Size + if f.Size > maxFileSize { + return nil, fmt.Errorf("file too big: %d > %d", f.Size, int64(maxFileSize)) + } + if f.Size <= inlineDataSize && w.supportInlineData { + node.Data = make([]byte, f.Size) + extra := 0 + if f.Size > inodeDataSize { + extra = int(f.Size - inodeDataSize) + } + // Add a dummy entry for now. + if !xstate.addXattr("system.data", node.Data[:extra]) { + panic("not enough room for inline data") + } + node.Flags |= format.InodeFlagInlineData + } + case format.S_IFLNK: + node.Mode |= 0777 // Symlinks should appear as ugw rwx + size = int64(len(f.Linkname)) + if size <= smallSymlinkSize { + // Special case: small symlinks go directly in Block without setting + // an inline data flag. + node.Data = make([]byte, len(f.Linkname)) + copy(node.Data, f.Linkname) + } + case format.S_IFDIR, format.S_IFIFO, format.S_IFSOCK, format.S_IFCHR, format.S_IFBLK: + default: + return nil, fmt.Errorf("invalid mode %o", mode) + } + + // Accumulate the extended attributes. + if len(f.Xattrs) != 0 { + // Sort the xattrs to avoid non-determinism in map iteration. + var xattrs []string + for name := range f.Xattrs { + xattrs = append(xattrs, name) + } + sort.Strings(xattrs) + for _, name := range xattrs { + if !xstate.addXattr(name, f.Xattrs[name]) { + return nil, fmt.Errorf("could not fit xattr %s", name) + } + } + } + + if err := w.writeXattrs(node, &xstate); err != nil { + return nil, err + } + + node.Size = size + if typ == format.S_IFLNK && size > smallSymlinkSize { + // Write the link name as data. + w.startInode("", node, size) + if _, err := w.Write([]byte(f.Linkname)); err != nil { + return nil, err + } + if err := w.finishInode(); err != nil { + return nil, err + } + } + + if int(node.Number-1) >= len(w.inodes) { + w.inodes = append(w.inodes, node) + } + return node, nil +} + +func (w *Writer) root() *inode { + return w.getInode(format.InodeRoot) +} + +func (w *Writer) lookup(name string, mustExist bool) (*inode, *inode, string, error) { + root := w.root() + cleanname := path.Clean("/" + name)[1:] + if len(cleanname) == 0 { + return root, root, "", nil + } + dirname, childname := path.Split(cleanname) + if len(childname) == 0 || len(childname) > 0xff { + return nil, nil, "", fmt.Errorf("%s: invalid name", name) + } + dir := w.findPath(root, dirname) + if dir == nil || !dir.IsDir() { + return nil, nil, "", fmt.Errorf("%s: path not found", name) + } + child := dir.Children[childname] + if child == nil && mustExist { + return nil, nil, "", fmt.Errorf("%s: file not found", name) + } + return dir, child, childname, nil +} + +// Create adds a file to the file system. +func (w *Writer) Create(name string, f *File) error { + if err := w.finishInode(); err != nil { + return err + } + dir, existing, childname, err := w.lookup(name, false) + if err != nil { + return err + } + var reuse *inode + if existing != nil { + if existing.IsDir() { + if f.Mode&TypeMask != S_IFDIR { + return fmt.Errorf("%s: cannot replace a directory with a file", name) + } + reuse = existing + } else if f.Mode&TypeMask == S_IFDIR { + return fmt.Errorf("%s: cannot replace a file with a directory", name) + } else if existing.LinkCount < 2 { + reuse = existing + } + } else { + if f.Mode&TypeMask == S_IFDIR && dir.LinkCount >= format.MaxLinks { + return fmt.Errorf("%s: exceeded parent directory maximum link count", name) + } + } + child, err := w.makeInode(f, reuse) + if err != nil { + return fmt.Errorf("%s: %s", name, err) + } + if existing != child { + if existing != nil { + existing.LinkCount-- + } + dir.Children[childname] = child + child.LinkCount++ + if child.IsDir() { + dir.LinkCount++ + } + } + if child.Mode&format.TypeMask == format.S_IFREG { + w.startInode(name, child, f.Size) + } + return nil +} + +// Link adds a hard link to the file system. +func (w *Writer) Link(oldname, newname string) error { + if err := w.finishInode(); err != nil { + return err + } + newdir, existing, newchildname, err := w.lookup(newname, false) + if err != nil { + return err + } + if existing != nil && (existing.IsDir() || existing.LinkCount < 2) { + return fmt.Errorf("%s: cannot orphan existing file or directory", newname) + } + + _, oldfile, _, err := w.lookup(oldname, true) + if err != nil { + return err + } + switch oldfile.Mode & format.TypeMask { + case format.S_IFDIR, format.S_IFLNK: + return fmt.Errorf("%s: link target cannot be a directory or symlink: %s", newname, oldname) + } + + if existing != oldfile && oldfile.LinkCount >= format.MaxLinks { + return fmt.Errorf("%s: link target would exceed maximum link count: %s", newname, oldname) + } + + if existing != nil { + existing.LinkCount-- + } + oldfile.LinkCount++ + newdir.Children[newchildname] = oldfile + return nil +} + +// Stat returns information about a file that has been written. +func (w *Writer) Stat(name string) (*File, error) { + if err := w.finishInode(); err != nil { + return nil, err + } + _, node, _, err := w.lookup(name, true) + if err != nil { + return nil, err + } + f := &File{ + Size: node.Size, + Mode: node.Mode, + Uid: node.Uid, + Gid: node.Gid, + Atime: fsTimeToTime(node.Atime), + Ctime: fsTimeToTime(node.Ctime), + Mtime: fsTimeToTime(node.Mtime), + Crtime: fsTimeToTime(node.Crtime), + Devmajor: node.Devmajor, + Devminor: node.Devminor, + } + f.Xattrs = make(map[string][]byte) + if node.XattrBlock != 0 || len(node.XattrInline) != 0 { + if node.XattrBlock != 0 { + orig := w.block() + w.seekBlock(node.XattrBlock) + if w.err != nil { + return nil, w.err + } + var b [blockSize]byte + _, err := w.f.Read(b[:]) + w.seekBlock(orig) + if err != nil { + return nil, err + } + getXattrs(b[32:], f.Xattrs, 32) + } + if len(node.XattrInline) != 0 { + getXattrs(node.XattrInline[4:], f.Xattrs, 0) + delete(f.Xattrs, "system.data") + } + } + if node.FileType() == S_IFLNK { + if node.Size > smallSymlinkSize { + return nil, fmt.Errorf("%s: cannot retrieve link information", name) + } + f.Linkname = string(node.Data) + } + return f, nil +} + +func (w *Writer) Write(b []byte) (int, error) { + if len(b) == 0 { + return 0, nil + } + if w.dataWritten+int64(len(b)) > w.dataMax { + return 0, fmt.Errorf("%s: wrote too much: %d > %d", w.curName, w.dataWritten+int64(len(b)), w.dataMax) + } + + if w.curInode.Flags&format.InodeFlagInlineData != 0 { + copy(w.curInode.Data[w.dataWritten:], b) + w.dataWritten += int64(len(b)) + return len(b), nil + } + + n, err := w.write(b) + w.dataWritten += int64(n) + return n, err +} + +func (w *Writer) startInode(name string, inode *inode, size int64) { + if w.curInode != nil { + panic("inode already in progress") + } + w.curName = name + w.curInode = inode + w.dataWritten = 0 + w.dataMax = size +} + +func (w *Writer) block() uint32 { + return uint32(w.pos / blockSize) +} + +func (w *Writer) seekBlock(block uint32) { + w.pos = int64(block) * blockSize + if w.err != nil { + return + } + w.err = w.bw.Flush() + if w.err != nil { + return + } + _, w.err = w.f.Seek(w.pos, io.SeekStart) +} + +func (w *Writer) nextBlock() { + if w.pos%blockSize != 0 { + // Simplify callers; w.err is updated on failure. + w.zero(blockSize - w.pos%blockSize) + } +} + +func fillExtents(hdr *format.ExtentHeader, extents []format.ExtentLeafNode, startBlock, offset, inodeSize uint32) { + *hdr = format.ExtentHeader{ + Magic: format.ExtentHeaderMagic, + Entries: uint16(len(extents)), + Max: uint16(cap(extents)), + Depth: 0, + } + for i := range extents { + block := offset + uint32(i)*maxBlocksPerExtent + length := inodeSize - block + if length > maxBlocksPerExtent { + length = maxBlocksPerExtent + } + start := startBlock + block + extents[i] = format.ExtentLeafNode{ + Block: block, + Length: uint16(length), + StartLow: start, + } + } +} + +func (w *Writer) writeExtents(inode *inode) error { + start := w.pos - w.dataWritten + if start%blockSize != 0 { + panic("unaligned") + } + w.nextBlock() + + startBlock := uint32(start / blockSize) + blocks := w.block() - startBlock + usedBlocks := blocks + + const extentNodeSize = 12 + const extentsPerBlock = blockSize/extentNodeSize - 1 + + extents := (blocks + maxBlocksPerExtent - 1) / maxBlocksPerExtent + var b bytes.Buffer + if extents == 0 { + // Nothing to do. + } else if extents <= 4 { + var root struct { + hdr format.ExtentHeader + extents [4]format.ExtentLeafNode + } + fillExtents(&root.hdr, root.extents[:extents], startBlock, 0, blocks) + binary.Write(&b, binary.LittleEndian, root) + } else if extents <= 4*extentsPerBlock { + const extentsPerBlock = blockSize/extentNodeSize - 1 + extentBlocks := extents/extentsPerBlock + 1 + usedBlocks += extentBlocks + var b2 bytes.Buffer + + var root struct { + hdr format.ExtentHeader + nodes [4]format.ExtentIndexNode + } + root.hdr = format.ExtentHeader{ + Magic: format.ExtentHeaderMagic, + Entries: uint16(extentBlocks), + Max: 4, + Depth: 1, + } + for i := uint32(0); i < extentBlocks; i++ { + root.nodes[i] = format.ExtentIndexNode{ + Block: i * extentsPerBlock * maxBlocksPerExtent, + LeafLow: w.block(), + } + extentsInBlock := extents - i*extentBlocks + if extentsInBlock > extentsPerBlock { + extentsInBlock = extentsPerBlock + } + + var node struct { + hdr format.ExtentHeader + extents [extentsPerBlock]format.ExtentLeafNode + _ [blockSize - (extentsPerBlock+1)*extentNodeSize]byte + } + + offset := i * extentsPerBlock * maxBlocksPerExtent + fillExtents(&node.hdr, node.extents[:extentsInBlock], startBlock+offset, offset, blocks) + binary.Write(&b2, binary.LittleEndian, node) + if _, err := w.write(b2.Next(blockSize)); err != nil { + return err + } + } + binary.Write(&b, binary.LittleEndian, root) + } else { + panic("file too big") + } + + inode.Data = b.Bytes() + inode.Flags |= format.InodeFlagExtents + inode.BlockCount += usedBlocks + return w.err +} + +func (w *Writer) finishInode() error { + if !w.initialized { + if err := w.init(); err != nil { + return err + } + } + if w.curInode == nil { + return nil + } + if w.dataWritten != w.dataMax { + return fmt.Errorf("did not write the right amount: %d != %d", w.dataWritten, w.dataMax) + } + + if w.dataMax != 0 && w.curInode.Flags&format.InodeFlagInlineData == 0 { + if err := w.writeExtents(w.curInode); err != nil { + return err + } + } + + w.dataWritten = 0 + w.dataMax = 0 + w.curInode = nil + return w.err +} + +func modeToFileType(mode uint16) format.FileType { + switch mode & format.TypeMask { + default: + return format.FileTypeUnknown + case format.S_IFREG: + return format.FileTypeRegular + case format.S_IFDIR: + return format.FileTypeDirectory + case format.S_IFCHR: + return format.FileTypeCharacter + case format.S_IFBLK: + return format.FileTypeBlock + case format.S_IFIFO: + return format.FileTypeFIFO + case format.S_IFSOCK: + return format.FileTypeSocket + case format.S_IFLNK: + return format.FileTypeSymbolicLink + } +} + +type constReader byte + +var zero = constReader(0) + +func (r constReader) Read(b []byte) (int, error) { + for i := range b { + b[i] = byte(r) + } + return len(b), nil +} + +func (w *Writer) writeDirectory(dir, parent *inode) error { + if err := w.finishInode(); err != nil { + return err + } + + // The size of the directory is not known yet. + w.startInode("", dir, 0x7fffffffffffffff) + left := blockSize + finishBlock := func() error { + if left > 0 { + e := format.DirectoryEntry{ + RecordLength: uint16(left), + } + err := binary.Write(w, binary.LittleEndian, e) + if err != nil { + return err + } + left -= directoryEntrySize + if left < 4 { + panic("not enough space for trailing entry") + } + _, err = io.CopyN(w, zero, int64(left)) + if err != nil { + return err + } + } + left = blockSize + return nil + } + + writeEntry := func(ino format.InodeNumber, name string) error { + rlb := directoryEntrySize + len(name) + rl := (rlb + 3) & ^3 + if left < rl+12 { + if err := finishBlock(); err != nil { + return err + } + } + e := format.DirectoryEntry{ + Inode: ino, + RecordLength: uint16(rl), + NameLength: uint8(len(name)), + FileType: modeToFileType(w.getInode(ino).Mode), + } + err := binary.Write(w, binary.LittleEndian, e) + if err != nil { + return err + } + _, err = w.Write([]byte(name)) + if err != nil { + return err + } + var zero [4]byte + _, err = w.Write(zero[:rl-rlb]) + if err != nil { + return err + } + left -= rl + return nil + } + if err := writeEntry(dir.Number, "."); err != nil { + return err + } + if err := writeEntry(parent.Number, ".."); err != nil { + return err + } + + // Follow e2fsck's convention and sort the children by inode number. + var children []string + for name := range dir.Children { + children = append(children, name) + } + sort.Slice(children, func(i, j int) bool { + return dir.Children[children[i]].Number < dir.Children[children[j]].Number + }) + + for _, name := range children { + child := dir.Children[name] + if err := writeEntry(child.Number, name); err != nil { + return err + } + } + if err := finishBlock(); err != nil { + return err + } + w.curInode.Size = w.dataWritten + w.dataMax = w.dataWritten + return nil +} + +func (w *Writer) writeDirectoryRecursive(dir, parent *inode) error { + if err := w.writeDirectory(dir, parent); err != nil { + return err + } + for _, child := range dir.Children { + if child.IsDir() { + if err := w.writeDirectoryRecursive(child, dir); err != nil { + return err + } + } + } + return nil +} + +func (w *Writer) writeInodeTable(tableSize uint32) error { + var b bytes.Buffer + for _, inode := range w.inodes { + if inode != nil { + binode := format.Inode{ + Mode: inode.Mode, + Uid: uint16(inode.Uid & 0xffff), + Gid: uint16(inode.Gid & 0xffff), + SizeLow: uint32(inode.Size & 0xffffffff), + SizeHigh: uint32(inode.Size >> 32), + LinksCount: uint16(inode.LinkCount), + BlocksLow: inode.BlockCount, + Flags: inode.Flags, + XattrBlockLow: inode.XattrBlock, + UidHigh: uint16(inode.Uid >> 16), + GidHigh: uint16(inode.Gid >> 16), + ExtraIsize: uint16(inodeUsedSize - 128), + Atime: uint32(inode.Atime), + AtimeExtra: uint32(inode.Atime >> 32), + Ctime: uint32(inode.Ctime), + CtimeExtra: uint32(inode.Ctime >> 32), + Mtime: uint32(inode.Mtime), + MtimeExtra: uint32(inode.Mtime >> 32), + Crtime: uint32(inode.Crtime), + CrtimeExtra: uint32(inode.Crtime >> 32), + } + switch inode.Mode & format.TypeMask { + case format.S_IFDIR, format.S_IFREG, format.S_IFLNK: + n := copy(binode.Block[:], inode.Data) + if n < len(inode.Data) { + // Rewrite the first xattr with the data. + xattr := [1]xattr{{ + Name: "data", + Index: 7, // "system." + Value: inode.Data[n:], + }} + putXattrs(xattr[:], inode.XattrInline[4:], 0) + } + case format.S_IFBLK, format.S_IFCHR: + dev := inode.Devminor&0xff | inode.Devmajor<<8 | (inode.Devminor&0xffffff00)<<12 + binary.LittleEndian.PutUint32(binode.Block[4:], dev) + } + + binary.Write(&b, binary.LittleEndian, binode) + b.Truncate(inodeUsedSize) + n, _ := b.Write(inode.XattrInline) + io.CopyN(&b, zero, int64(inodeExtraSize-n)) + } else { + io.CopyN(&b, zero, inodeSize) + } + if _, err := w.write(b.Next(inodeSize)); err != nil { + return err + } + } + rest := tableSize - uint32(len(w.inodes)*inodeSize) + if _, err := w.zero(int64(rest)); err != nil { + return err + } + return nil +} + +// NewWriter returns a Writer that writes an ext4 file system to the provided +// WriteSeeker. +func NewWriter(f io.ReadWriteSeeker, opts ...Option) *Writer { + w := &Writer{ + f: f, + bw: bufio.NewWriterSize(f, 65536*8), + maxDiskSize: defaultMaxDiskSize, + } + for _, opt := range opts { + opt(w) + } + return w +} + +// An Option provides extra options to NewWriter. +type Option func(*Writer) + +// InlineData instructs the Writer to write small files into the inode +// structures directly. This creates smaller images but currently is not +// compatible with DAX. +func InlineData(w *Writer) { + w.supportInlineData = true +} + +// MaximumDiskSize instructs the writer to reserve enough metadata space for the +// specified disk size. If not provided, then 16GB is the default. +func MaximumDiskSize(size int64) Option { + return func(w *Writer) { + if size < 0 || size > maxMaxDiskSize { + w.maxDiskSize = maxMaxDiskSize + } else if size == 0 { + w.maxDiskSize = defaultMaxDiskSize + } else { + w.maxDiskSize = (size + blockSize - 1) &^ (blockSize - 1) + } + } +} + +func (w *Writer) init() error { + // Skip the defective block inode. + w.inodes = make([]*inode, 1, 32) + // Create the root directory. + root, _ := w.makeInode(&File{ + Mode: format.S_IFDIR | 0755, + }, nil) + root.LinkCount++ // The root is linked to itself. + // Skip until the first non-reserved inode. + w.inodes = append(w.inodes, make([]*inode, inodeFirst-len(w.inodes)-1)...) + maxBlocks := (w.maxDiskSize-1)/blockSize + 1 + maxGroups := (maxBlocks-1)/blocksPerGroup + 1 + w.gdBlocks = uint32((maxGroups-1)/groupsPerDescriptorBlock + 1) + + // Skip past the superblock and block descriptor table. + w.seekBlock(1 + w.gdBlocks) + w.initialized = true + + // The lost+found directory is required to exist for e2fsck to pass. + if err := w.Create("lost+found", &File{Mode: format.S_IFDIR | 0700}); err != nil { + return err + } + return w.err +} + +func groupCount(blocks uint32, inodes uint32, inodesPerGroup uint32) uint32 { + inodeBlocksPerGroup := inodesPerGroup * inodeSize / blockSize + dataBlocksPerGroup := blocksPerGroup - inodeBlocksPerGroup - 2 // save room for the bitmaps + + // Increase the block count to ensure there are enough groups for all the + // inodes. + minBlocks := (inodes-1)/inodesPerGroup*dataBlocksPerGroup + 1 + if blocks < minBlocks { + blocks = minBlocks + } + + return (blocks + dataBlocksPerGroup - 1) / dataBlocksPerGroup +} + +func bestGroupCount(blocks uint32, inodes uint32) (groups uint32, inodesPerGroup uint32) { + groups = 0xffffffff + for ipg := uint32(inodesPerGroupIncrement); ipg <= maxInodesPerGroup; ipg += inodesPerGroupIncrement { + g := groupCount(blocks, inodes, ipg) + if g < groups { + groups = g + inodesPerGroup = ipg + } + } + return +} + +func (w *Writer) Close() error { + if err := w.finishInode(); err != nil { + return err + } + root := w.root() + if err := w.writeDirectoryRecursive(root, root); err != nil { + return err + } + // Finish the last inode (probably a directory). + if err := w.finishInode(); err != nil { + return err + } + + // Write the inode table + inodeTableOffset := w.block() + groups, inodesPerGroup := bestGroupCount(inodeTableOffset, uint32(len(w.inodes))) + err := w.writeInodeTable(groups * inodesPerGroup * inodeSize) + if err != nil { + return err + } + + // Write the bitmaps. + bitmapOffset := w.block() + bitmapSize := groups * 2 + validDataSize := bitmapOffset + bitmapSize + diskSize := validDataSize + minSize := (groups-1)*blocksPerGroup + 1 + if diskSize < minSize { + diskSize = minSize + } + + usedGdBlocks := (groups-1)/groupDescriptorSize + 1 + if usedGdBlocks > w.gdBlocks { + return exceededMaxSizeError{w.maxDiskSize} + } + + gds := make([]format.GroupDescriptor, w.gdBlocks*groupsPerDescriptorBlock) + inodeTableSizePerGroup := inodesPerGroup * inodeSize / blockSize + var totalUsedBlocks, totalUsedInodes uint32 + for g := uint32(0); g < groups; g++ { + var b [blockSize * 2]byte + var dirCount, usedInodeCount, usedBlockCount uint16 + + // Block bitmap + if (g+1)*blocksPerGroup <= validDataSize { + // This group is fully allocated. + for j := range b[:blockSize] { + b[j] = 0xff + } + usedBlockCount = blocksPerGroup + } else if g*blocksPerGroup < validDataSize { + for j := uint32(0); j < validDataSize-g*blocksPerGroup; j++ { + b[j/8] |= 1 << (j % 8) + usedBlockCount++ + } + } + if g == 0 { + // Unused group descriptor blocks should be cleared. + for j := 1 + usedGdBlocks; j < 1+w.gdBlocks; j++ { + b[j/8] &^= 1 << (j % 8) + usedBlockCount-- + } + } + if g == groups-1 && diskSize%blocksPerGroup != 0 { + // Blocks that aren't present in the disk should be marked as + // allocated. + for j := diskSize % blocksPerGroup; j < blocksPerGroup; j++ { + b[j/8] |= 1 << (j % 8) + usedBlockCount++ + } + } + // Inode bitmap + for j := uint32(0); j < inodesPerGroup; j++ { + ino := format.InodeNumber(1 + g*inodesPerGroup + j) + inode := w.getInode(ino) + if ino < inodeFirst || inode != nil { + b[blockSize+j/8] |= 1 << (j % 8) + usedInodeCount++ + } + if inode != nil && inode.Mode&format.TypeMask == format.S_IFDIR { + dirCount++ + } + } + _, err := w.write(b[:]) + if err != nil { + return err + } + gds[g] = format.GroupDescriptor{ + BlockBitmapLow: bitmapOffset + 2*g, + InodeBitmapLow: bitmapOffset + 2*g + 1, + InodeTableLow: inodeTableOffset + g*inodeTableSizePerGroup, + UsedDirsCountLow: dirCount, + FreeInodesCountLow: uint16(inodesPerGroup) - usedInodeCount, + FreeBlocksCountLow: blocksPerGroup - usedBlockCount, + } + + totalUsedBlocks += uint32(usedBlockCount) + totalUsedInodes += uint32(usedInodeCount) + } + + // Zero up to the disk size. + _, err = w.zero(int64(diskSize-bitmapOffset-bitmapSize) * blockSize) + if err != nil { + return err + } + + // Write the block descriptors + w.seekBlock(1) + if w.err != nil { + return w.err + } + err = binary.Write(w.bw, binary.LittleEndian, gds) + if err != nil { + return err + } + + // Write the super block + var blk [blockSize]byte + b := bytes.NewBuffer(blk[:1024]) + sb := &format.SuperBlock{ + InodesCount: inodesPerGroup * groups, + BlocksCountLow: diskSize, + FreeBlocksCountLow: blocksPerGroup*groups - totalUsedBlocks, + FreeInodesCount: inodesPerGroup*groups - totalUsedInodes, + FirstDataBlock: 0, + LogBlockSize: 2, // 2^(10 + 2) + LogClusterSize: 2, + BlocksPerGroup: blocksPerGroup, + ClustersPerGroup: blocksPerGroup, + InodesPerGroup: inodesPerGroup, + Magic: format.SuperBlockMagic, + State: 1, // cleanly unmounted + Errors: 1, // continue on error? + CreatorOS: 0, // Linux + RevisionLevel: 1, // dynamic inode sizes + FirstInode: inodeFirst, + LpfInode: inodeLostAndFound, + InodeSize: inodeSize, + FeatureCompat: format.CompatSparseSuper2 | format.CompatExtAttr, + FeatureIncompat: format.IncompatFiletype | format.IncompatExtents | format.IncompatFlexBg, + FeatureRoCompat: format.RoCompatLargeFile | format.RoCompatHugeFile | format.RoCompatExtraIsize | format.RoCompatReadonly, + MinExtraIsize: extraIsize, + WantExtraIsize: extraIsize, + LogGroupsPerFlex: 31, + } + if w.supportInlineData { + sb.FeatureIncompat |= format.IncompatInlineData + } + binary.Write(b, binary.LittleEndian, sb) + w.seekBlock(0) + if _, err := w.write(blk[:]); err != nil { + return err + } + w.seekBlock(diskSize) + return w.err +} diff --git a/vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/compact_test.go b/vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/compact_test.go new file mode 100644 index 0000000000..b7b2a30b8e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/compact_test.go @@ -0,0 +1,355 @@ +package compactext4 + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "os" + "strings" + "testing" + "time" + + "github.com/Microsoft/hcsshim/ext4/internal/format" +) + +type testFile struct { + Path string + File *File + Data []byte + DataSize int64 + Link string + ExpectError bool +} + +var ( + data []byte + name string +) + +func init() { + data = make([]byte, blockSize*2) + for i := range data { + data[i] = uint8(i) + } + + nameb := make([]byte, 300) + for i := range nameb { + nameb[i] = byte('0' + i%10) + } + name = string(nameb) +} + +type largeData struct { + pos int64 +} + +func (d *largeData) Read(b []byte) (int, error) { + p := d.pos + var pb [8]byte + for i := range b { + binary.LittleEndian.PutUint64(pb[:], uint64(p+int64(i))) + b[i] = pb[i%8] + } + p += int64(len(b)) + return len(b), nil +} + +func (tf *testFile) Reader() io.Reader { + if tf.DataSize != 0 { + return io.LimitReader(&largeData{}, tf.DataSize) + } + return bytes.NewReader(tf.Data) +} + +func createTestFile(t *testing.T, w *Writer, tf testFile) { + var err error + if tf.File != nil { + tf.File.Size = int64(len(tf.Data)) + if tf.File.Size == 0 { + tf.File.Size = tf.DataSize + } + err = w.Create(tf.Path, tf.File) + } else { + err = w.Link(tf.Link, tf.Path) + } + if tf.ExpectError && err == nil { + t.Errorf("%s: expected error", tf.Path) + } else if !tf.ExpectError && err != nil { + t.Error(err) + } else { + _, err := io.Copy(w, tf.Reader()) + if err != nil { + t.Error(err) + } + } +} + +func expectedMode(f *File) uint16 { + switch f.Mode & format.TypeMask { + case 0: + return f.Mode | S_IFREG + case S_IFLNK: + return f.Mode | 0777 + default: + return f.Mode + } +} + +func expectedSize(f *File) int64 { + switch f.Mode & format.TypeMask { + case 0, S_IFREG: + return f.Size + case S_IFLNK: + return int64(len(f.Linkname)) + default: + return 0 + } +} + +func xattrsEqual(x1, x2 map[string][]byte) bool { + if len(x1) != len(x2) { + return false + } + for name, value := range x1 { + if !bytes.Equal(x2[name], value) { + return false + } + } + return true +} + +func fileEqual(f1, f2 *File) bool { + return f1.Linkname == f2.Linkname && + expectedSize(f1) == expectedSize(f2) && + expectedMode(f1) == expectedMode(f2) && + f1.Uid == f2.Uid && + f1.Gid == f2.Gid && + f1.Atime.Equal(f2.Atime) && + f1.Ctime.Equal(f2.Ctime) && + f1.Mtime.Equal(f2.Mtime) && + f1.Crtime.Equal(f2.Crtime) && + f1.Devmajor == f2.Devmajor && + f1.Devminor == f2.Devminor && + xattrsEqual(f1.Xattrs, f2.Xattrs) +} + +func runTestsOnFiles(t *testing.T, testFiles []testFile, opts ...Option) { + image := "testfs.img" + imagef, err := os.Create(image) + if err != nil { + t.Fatal(err) + } + defer os.Remove(image) + defer imagef.Close() + + w := NewWriter(imagef, opts...) + for _, tf := range testFiles { + createTestFile(t, w, tf) + if !tf.ExpectError && tf.File != nil { + f, err := w.Stat(tf.Path) + if err != nil { + if !strings.Contains(err.Error(), "cannot retrieve") { + t.Error(err) + } + } else if !fileEqual(f, tf.File) { + t.Errorf("%s: stat mismatch: %#v %#v", tf.Path, tf.File, f) + } + } + } + + if t.Failed() { + return + } + + if err := w.Close(); err != nil { + t.Fatal(err) + } + + fsck(t, image) + + mountPath := "testmnt" + + if mountImage(t, image, mountPath) { + defer unmountImage(t, mountPath) + validated := make(map[string]*testFile) + for i := range testFiles { + tf := testFiles[len(testFiles)-i-1] + if validated[tf.Link] != nil { + // The link target was subsequently replaced. Find the + // earlier instance. + for j := range testFiles[:len(testFiles)-i-1] { + otf := testFiles[j] + if otf.Path == tf.Link && !otf.ExpectError { + tf = otf + break + } + } + } + if !tf.ExpectError && validated[tf.Path] == nil { + verifyTestFile(t, mountPath, tf) + validated[tf.Path] = &tf + } + } + } +} + +func TestBasic(t *testing.T) { + now := time.Now() + testFiles := []testFile{ + {Path: "empty", File: &File{Mode: 0644}}, + {Path: "small", File: &File{Mode: 0644}, Data: data[:40]}, + {Path: "time", File: &File{Atime: now, Ctime: now.Add(time.Second), Mtime: now.Add(time.Hour)}}, + {Path: "block_1", File: &File{Mode: 0644}, Data: data[:blockSize]}, + {Path: "block_2", File: &File{Mode: 0644}, Data: data[:blockSize*2]}, + {Path: "symlink", File: &File{Linkname: "block_1", Mode: format.S_IFLNK}}, + {Path: "symlink_59", File: &File{Linkname: name[:59], Mode: format.S_IFLNK}}, + {Path: "symlink_60", File: &File{Linkname: name[:60], Mode: format.S_IFLNK}}, + {Path: "symlink_120", File: &File{Linkname: name[:120], Mode: format.S_IFLNK}}, + {Path: "symlink_300", File: &File{Linkname: name[:300], Mode: format.S_IFLNK}}, + {Path: "dir", File: &File{Mode: format.S_IFDIR | 0755}}, + {Path: "dir/fifo", File: &File{Mode: format.S_IFIFO}}, + {Path: "dir/sock", File: &File{Mode: format.S_IFSOCK}}, + {Path: "dir/blk", File: &File{Mode: format.S_IFBLK, Devmajor: 0x5678, Devminor: 0x1234}}, + {Path: "dir/chr", File: &File{Mode: format.S_IFCHR, Devmajor: 0x5678, Devminor: 0x1234}}, + {Path: "dir/hard_link", Link: "small"}, + } + + runTestsOnFiles(t, testFiles) +} + +func TestLargeDirectory(t *testing.T) { + testFiles := []testFile{ + {Path: "bigdir", File: &File{Mode: format.S_IFDIR | 0755}}, + } + for i := 0; i < 50000; i++ { + testFiles = append(testFiles, testFile{ + Path: fmt.Sprintf("bigdir/%d", i), File: &File{Mode: 0644}, + }) + } + + runTestsOnFiles(t, testFiles) +} + +func TestInlineData(t *testing.T) { + testFiles := []testFile{ + {Path: "inline_30", File: &File{Mode: 0644}, Data: data[:30]}, + {Path: "inline_60", File: &File{Mode: 0644}, Data: data[:60]}, + {Path: "inline_120", File: &File{Mode: 0644}, Data: data[:120]}, + {Path: "inline_full", File: &File{Mode: 0644}, Data: data[:inlineDataSize]}, + {Path: "block_min", File: &File{Mode: 0644}, Data: data[:inlineDataSize+1]}, + } + + runTestsOnFiles(t, testFiles, InlineData) +} + +func TestXattrs(t *testing.T) { + testFiles := []testFile{ + {Path: "withsmallxattrs", + File: &File{ + Mode: format.S_IFREG | 0644, + Xattrs: map[string][]byte{ + "user.foo": []byte("test"), + "user.bar": []byte("test2"), + }, + }, + }, + {Path: "withlargexattrs", + File: &File{ + Mode: format.S_IFREG | 0644, + Xattrs: map[string][]byte{ + "user.foo": data[:100], + "user.bar": data[:50], + }, + }, + }, + } + runTestsOnFiles(t, testFiles) +} + +func TestReplace(t *testing.T) { + testFiles := []testFile{ + {Path: "lost+found", ExpectError: true, File: &File{}}, // can't change type + {Path: "lost+found", File: &File{Mode: format.S_IFDIR | 0777}}, + + {Path: "dir", File: &File{Mode: format.S_IFDIR | 0777}}, + {Path: "dir/file", File: &File{}}, + {Path: "dir", File: &File{Mode: format.S_IFDIR | 0700}}, + + {Path: "file", File: &File{}}, + {Path: "file", File: &File{Mode: 0600}}, + {Path: "file2", File: &File{}}, + {Path: "link", Link: "file2"}, + {Path: "file2", File: &File{Mode: 0600}}, + + {Path: "nolinks", File: &File{}}, + {Path: "nolinks", ExpectError: true, Link: "file"}, // would orphan nolinks + + {Path: "onelink", File: &File{}}, + {Path: "onelink2", Link: "onelink"}, + {Path: "onelink", Link: "file"}, + + {Path: "", ExpectError: true, File: &File{}}, + {Path: "", ExpectError: true, Link: "file"}, + {Path: "", File: &File{Mode: format.S_IFDIR | 0777}}, + + {Path: "smallxattr", File: &File{Xattrs: map[string][]byte{"user.foo": data[:4]}}}, + {Path: "smallxattr", File: &File{Xattrs: map[string][]byte{"user.foo": data[:8]}}}, + + {Path: "smallxattr_delete", File: &File{Xattrs: map[string][]byte{"user.foo": data[:4]}}}, + {Path: "smallxattr_delete", File: &File{}}, + + {Path: "largexattr", File: &File{Xattrs: map[string][]byte{"user.small": data[:8], "user.foo": data[:200]}}}, + {Path: "largexattr", File: &File{Xattrs: map[string][]byte{"user.small": data[:12], "user.foo": data[:400]}}}, + + {Path: "largexattr", File: &File{Xattrs: map[string][]byte{"user.foo": data[:200]}}}, + {Path: "largexattr_delete", File: &File{}}, + } + runTestsOnFiles(t, testFiles) +} + +func TestTime(t *testing.T) { + now := time.Now() + now2 := fsTimeToTime(timeToFsTime(now)) + if now.UnixNano() != now2.UnixNano() { + t.Fatalf("%s != %s", now, now2) + } +} + +func TestLargeFile(t *testing.T) { + testFiles := []testFile{ + {Path: "small", File: &File{}, DataSize: 1024 * 1024}, // can't change type + {Path: "medium", File: &File{}, DataSize: 200 * 1024 * 1024}, // can't change type + {Path: "large", File: &File{}, DataSize: 600 * 1024 * 1024}, // can't change type + } + runTestsOnFiles(t, testFiles) +} + +func TestFileLinkLimit(t *testing.T) { + testFiles := []testFile{ + {Path: "file", File: &File{}}, + } + for i := 0; i < format.MaxLinks; i++ { + testFiles = append(testFiles, testFile{Path: fmt.Sprintf("link%d", i), Link: "file"}) + } + testFiles[len(testFiles)-1].ExpectError = true + runTestsOnFiles(t, testFiles) +} + +func TestDirLinkLimit(t *testing.T) { + testFiles := []testFile{ + {Path: "dir", File: &File{Mode: S_IFDIR}}, + } + for i := 0; i < format.MaxLinks-1; i++ { + testFiles = append(testFiles, testFile{Path: fmt.Sprintf("dir/%d", i), File: &File{Mode: S_IFDIR}}) + } + testFiles[len(testFiles)-1].ExpectError = true + runTestsOnFiles(t, testFiles) +} + +func TestLargeDisk(t *testing.T) { + testFiles := []testFile{ + {Path: "file", File: &File{}}, + } + runTestsOnFiles(t, testFiles, MaximumDiskSize(maxMaxDiskSize)) +} diff --git a/vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/verify_linux_test.go b/vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/verify_linux_test.go new file mode 100644 index 0000000000..86ece03b50 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/ext4/internal/compactext4/verify_linux_test.go @@ -0,0 +1,248 @@ +package compactext4 + +import ( + "bytes" + "fmt" + "io" + "os" + "os/exec" + "path" + "syscall" + "testing" + "time" + "unsafe" + + "github.com/Microsoft/hcsshim/ext4/internal/format" +) + +func timeEqual(ts syscall.Timespec, t time.Time) bool { + sec, nsec := t.Unix(), t.Nanosecond() + if t.IsZero() { + sec, nsec = 0, 0 + } + return ts.Sec == sec && int(ts.Nsec) == nsec +} + +func expectedDevice(f *File) uint64 { + return uint64(f.Devminor&0xff | f.Devmajor<<8 | (f.Devminor&0xffffff00)<<12) +} + +func llistxattr(path string, b []byte) (int, error) { + pathp := syscall.StringBytePtr(path) + var p unsafe.Pointer + if len(b) > 0 { + p = unsafe.Pointer(&b[0]) + } + r, _, e := syscall.Syscall(syscall.SYS_LLISTXATTR, uintptr(unsafe.Pointer(pathp)), uintptr(p), uintptr(len(b))) + if e != 0 { + return 0, &os.PathError{Path: path, Op: "llistxattr", Err: syscall.Errno(e)} + } + return int(r), nil +} + +func lgetxattr(path string, name string, b []byte) (int, error) { + pathp := syscall.StringBytePtr(path) + namep := syscall.StringBytePtr(name) + var p unsafe.Pointer + if len(b) > 0 { + p = unsafe.Pointer(&b[0]) + } + r, _, e := syscall.Syscall6(syscall.SYS_LGETXATTR, uintptr(unsafe.Pointer(pathp)), uintptr(unsafe.Pointer(namep)), uintptr(p), uintptr(len(b)), 0, 0) + if e != 0 { + return 0, &os.PathError{Path: path, Op: "lgetxattr", Err: syscall.Errno(e)} + } + return int(r), nil +} + +func readXattrs(path string) (map[string][]byte, error) { + xattrs := make(map[string][]byte) + var buf [4096]byte + var buf2 [4096]byte + b := buf[:] + n, err := llistxattr(path, b) + if err != nil { + return nil, err + } + b = b[:n] + for len(b) != 0 { + nn := bytes.IndexByte(b, 0) + name := string(b[:nn]) + b = b[nn+1:] + vn, err := lgetxattr(path, name, buf2[:]) + if err != nil { + return nil, err + } + value := buf2[:vn] + xattrs[name] = value + } + return xattrs, nil +} + +func streamEqual(r1, r2 io.Reader) (bool, error) { + var b [4096]byte + var b2 [4096]byte + for { + n, err := r1.Read(b[:]) + if n == 0 { + if err == io.EOF { + break + } + if err == nil { + continue + } + return false, err + } + _, err = io.ReadFull(r2, b2[:n]) + if err == io.EOF || err == io.ErrUnexpectedEOF { + return false, nil + } + if err != nil { + return false, err + } + if !bytes.Equal(b[n:], b2[n:]) { + return false, nil + } + } + // Check the tail of r2 + _, err := r2.Read(b[:1]) + if err == nil { + return false, nil + } + if err != io.EOF { + return false, err + } + return true, nil +} + +func verifyTestFile(t *testing.T, mountPath string, tf testFile) { + name := path.Join(mountPath, tf.Path) + fi, err := os.Lstat(name) + if err != nil { + t.Error(err) + return + } + st := fi.Sys().(*syscall.Stat_t) + if tf.File != nil { + if st.Mode != uint32(expectedMode(tf.File)) || + st.Uid != tf.File.Uid || + st.Gid != tf.File.Gid || + (!fi.IsDir() && st.Size != expectedSize(tf.File)) || + st.Rdev != expectedDevice(tf.File) || + !timeEqual(st.Atim, tf.File.Atime) || + !timeEqual(st.Mtim, tf.File.Mtime) || + !timeEqual(st.Ctim, tf.File.Ctime) { + + t.Errorf("%s: stat mismatch, expected: %#v got: %#v", tf.Path, tf.File, st) + } + + xattrs, err := readXattrs(name) + if err != nil { + t.Error(err) + } else if !xattrsEqual(xattrs, tf.File.Xattrs) { + t.Errorf("%s: xattr mismatch, expected: %#v got: %#v", tf.Path, tf.File.Xattrs, xattrs) + } + + switch tf.File.Mode & format.TypeMask { + case S_IFREG: + if f, err := os.Open(name); err != nil { + t.Error(err) + } else { + same, err := streamEqual(f, tf.Reader()) + if err != nil { + t.Error(err) + } else if !same { + t.Errorf("%s: data mismatch", tf.Path) + } + f.Close() + } + case S_IFLNK: + if link, err := os.Readlink(name); err != nil { + t.Error(err) + } else if link != tf.File.Linkname { + t.Errorf("%s: link mismatch, expected: %s got: %s", tf.Path, tf.File.Linkname, link) + } + } + } else { + lfi, err := os.Lstat(path.Join(mountPath, tf.Link)) + if err != nil { + t.Error(err) + return + } + + lst := lfi.Sys().(*syscall.Stat_t) + if lst.Ino != st.Ino { + t.Errorf("%s: hard link mismatch with %s, expected inode: %d got inode: %d", tf.Path, tf.Link, lst.Ino, st.Ino) + } + } +} + +type capHeader struct { + version uint32 + pid int +} + +type capData struct { + effective uint32 + permitted uint32 + inheritable uint32 +} + +const CAP_SYS_ADMIN = 21 + +type caps struct { + hdr capHeader + data [2]capData +} + +func getCaps() (caps, error) { + var c caps + + // Get capability version + if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(nil)), 0); errno != 0 { + return c, fmt.Errorf("SYS_CAPGET: %v", errno) + } + + // Get current capabilities + if _, _, errno := syscall.Syscall(syscall.SYS_CAPGET, uintptr(unsafe.Pointer(&c.hdr)), uintptr(unsafe.Pointer(&c.data[0])), 0); errno != 0 { + return c, fmt.Errorf("SYS_CAPGET: %v", errno) + } + + return c, nil +} + +func mountImage(t *testing.T, image string, mountPath string) bool { + caps, err := getCaps() + if err != nil || caps.data[0].effective&(1< 0 { - log = fmt.Sprintf("%d bytes", size) - } - logrus.Debugf(fmt.Sprintf("hcsshim::copywithtimeout (%s) %s", context, log)) + logrus.WithFields(logrus.Fields{ + "stdval": context, + "size": size, + "timeout": timeout, + }).Debug("hcsshim::copywithtimeout - Begin") type resultType struct { err error @@ -62,7 +62,7 @@ func Copy(dst io.Writer, src io.Reader, size int64, context string, timeout time if size > 0 { bytes := make([]byte, size) if _, err := buf.Read(bytes); err == nil { - logrus.Debugf(fmt.Sprintf("hcsshim: copyWithTimeout\n%s", hex.Dump(bytes))) + logrus.Debugf("hcsshim::copyWithTimeout - Read bytes\n%s", hex.Dump(bytes)) } } } @@ -85,13 +85,19 @@ func Copy(dst io.Writer, src io.Reader, size int64, context string, timeout time errBrokenPipe = syscall.Errno(109) ) if se == errNoData || se == errBrokenPipe { - logrus.Debugf("hcsshim::copyWithTimeout: hit NoData or BrokenPipe: %d: %s", se, context) + logrus.WithFields(logrus.Fields{ + "stdval": context, + logrus.ErrorKey: se, + }).Debug("hcsshim::copywithtimeout - End") return result.bytes, nil } } return 0, fmt.Errorf("hcsshim::copyWithTimeout: error reading: '%s' after %d bytes (%s)", result.err, result.bytes, context) } } - logrus.Debugf("hcsshim::copyWithTimeout: success - copied %d bytes (%s)", result.bytes, context) + logrus.WithFields(logrus.Fields{ + "stdval": context, + "copied-bytes": result.bytes, + }).Debug("hcsshim::copywithtimeout - Completed Successfully") return result.bytes, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go b/vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go index e44a6dacec..9f926c6be7 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/guestrequest/types.go @@ -54,6 +54,7 @@ const ( ResourceTypeMappedDirectory ResourceType = "MappedDirectory" ResourceTypeMappedVirtualDisk ResourceType = "MappedVirtualDisk" ResourceTypeNetwork ResourceType = "Network" + ResourceTypeNetworkNamespace ResourceType = "NetworkNamespace" ResourceTypeCombinedLayers ResourceType = "CombinedLayers" ResourceTypeVPMemDevice ResourceType = "VPMemDevice" ) @@ -66,7 +67,19 @@ type GuestRequest struct { } type NetworkModifyRequest struct { + AdapterId string `json:"AdapterId,omitempty"` + RequestType string `json:"RequestType,omitempty"` + Settings interface{} `json:"Settings,omitempty"` +} + +type RS4NetworkModifyRequest struct { AdapterInstanceId string `json:"AdapterInstanceId,omitempty"` RequestType string `json:"RequestType,omitempty"` Settings interface{} `json:"Settings,omitempty"` } + +// SignalProcessOptions is the options passed to either WCOW or LCOW +// to signal a given process. +type SignalProcessOptions struct { + Signal int `json:,omitempty` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go index e41c40ec8f..f9a922a4bb 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go @@ -5,6 +5,7 @@ import ( "syscall" "github.com/Microsoft/hcsshim/internal/interop" + "github.com/sirupsen/logrus" ) var ( @@ -15,11 +16,20 @@ var ( notificationWatcherCallback = syscall.NewCallback(notificationWatcher) // Notifications for HCS_SYSTEM handles - hcsNotificationSystemExited hcsNotification = 0x00000001 - hcsNotificationSystemCreateCompleted hcsNotification = 0x00000002 - hcsNotificationSystemStartCompleted hcsNotification = 0x00000003 - hcsNotificationSystemPauseCompleted hcsNotification = 0x00000004 - hcsNotificationSystemResumeCompleted hcsNotification = 0x00000005 + hcsNotificationSystemExited hcsNotification = 0x00000001 + hcsNotificationSystemCreateCompleted hcsNotification = 0x00000002 + hcsNotificationSystemStartCompleted hcsNotification = 0x00000003 + hcsNotificationSystemPauseCompleted hcsNotification = 0x00000004 + hcsNotificationSystemResumeCompleted hcsNotification = 0x00000005 + hcsNotificationSystemCrashReport hcsNotification = 0x00000006 + hcsNotificationSystemSiloJobCreated hcsNotification = 0x00000007 + hcsNotificationSystemSaveCompleted hcsNotification = 0x00000008 + hcsNotificationSystemRdpEnhancedModeStateChanged hcsNotification = 0x00000009 + hcsNotificationSystemShutdownFailed hcsNotification = 0x0000000A + hcsNotificationSystemGetPropertiesCompleted hcsNotification = 0x0000000B + hcsNotificationSystemModifyCompleted hcsNotification = 0x0000000C + hcsNotificationSystemCrashInitiated hcsNotification = 0x0000000D + hcsNotificationSystemGuestConnectionClosed hcsNotification = 0x0000000E // Notifications for HCS_PROCESS handles hcsNotificationProcessExited hcsNotification = 0x00010000 @@ -49,16 +59,23 @@ func newChannels() notificationChannels { channels[hcsNotificationSystemResumeCompleted] = make(notificationChannel, 1) channels[hcsNotificationProcessExited] = make(notificationChannel, 1) channels[hcsNotificationServiceDisconnect] = make(notificationChannel, 1) + channels[hcsNotificationSystemCrashReport] = make(notificationChannel, 1) + channels[hcsNotificationSystemSiloJobCreated] = make(notificationChannel, 1) + channels[hcsNotificationSystemSaveCompleted] = make(notificationChannel, 1) + channels[hcsNotificationSystemRdpEnhancedModeStateChanged] = make(notificationChannel, 1) + channels[hcsNotificationSystemShutdownFailed] = make(notificationChannel, 1) + channels[hcsNotificationSystemGetPropertiesCompleted] = make(notificationChannel, 1) + channels[hcsNotificationSystemModifyCompleted] = make(notificationChannel, 1) + channels[hcsNotificationSystemCrashInitiated] = make(notificationChannel, 1) + channels[hcsNotificationSystemGuestConnectionClosed] = make(notificationChannel, 1) + return channels } + func closeChannels(channels notificationChannels) { - close(channels[hcsNotificationSystemExited]) - close(channels[hcsNotificationSystemCreateCompleted]) - close(channels[hcsNotificationSystemStartCompleted]) - close(channels[hcsNotificationSystemPauseCompleted]) - close(channels[hcsNotificationSystemResumeCompleted]) - close(channels[hcsNotificationProcessExited]) - close(channels[hcsNotificationServiceDisconnect]) + for _, c := range channels { + close(c) + } } func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr { @@ -75,7 +92,13 @@ func notificationWatcher(notificationType hcsNotification, callbackNumber uintpt return 0 } - context.channels[notificationType] <- result + if channel, ok := context.channels[notificationType]; ok { + channel <- result + } else { + logrus.WithFields(logrus.Fields{ + "notification-type": notificationType, + }).Warn("Received a callback of an unsupported type") + } return 0 } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go index 7471f5cc13..079b565353 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go @@ -7,6 +7,7 @@ import ( "syscall" "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/logfields" "github.com/sirupsen/logrus" ) @@ -72,6 +73,9 @@ var ( // ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b) + // ErrVmcomputeUnexpectedExit is an error encountered when the compute system terminates unexpectedly + ErrVmcomputeUnexpectedExit = syscall.Errno(0xC0370106) + // ErrNotSupported is an error encountered when hcs doesn't support the request ErrPlatformNotSupported = errors.New("unsupported platform request") ) @@ -116,10 +120,14 @@ func (ev *ErrorEvent) String() string { func processHcsResult(resultp *uint16) []ErrorEvent { if resultp != nil { resultj := interop.ConvertAndFreeCoTaskMemString(resultp) - logrus.Debugf("Result: %s", resultj) + logrus.WithField(logfields.JSON, resultj). + Debug("HCS Result") result := &hcsResult{} if err := json.Unmarshal([]byte(resultj), result); err != nil { - logrus.Warnf("Could not unmarshal HCS result %s: %s", resultj, err) + logrus.WithFields(logrus.Fields{ + logfields.JSON: resultj, + logrus.ErrorKey: err, + }).Warning("Could not unmarshal HCS result") return nil } return result.ErrorEvents diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go index b8e30eba17..b0d49cbcf1 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go @@ -27,6 +27,7 @@ import ( //sys hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsOpenProcess? //sys hcsCloseProcess(process hcsProcess) (hr error) = vmcompute.HcsCloseProcess? //sys hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) = vmcompute.HcsTerminateProcess? +//sys hcsSignalProcess(process hcsProcess, options string, result **uint16) (hr error) = vmcompute.HcsTerminateProcess? //sys hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) = vmcompute.HcsGetProcessInfo? //sys hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) = vmcompute.HcsGetProcessProperties? //sys hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) = vmcompute.HcsModifyProcess? diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/log.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/log.go new file mode 100644 index 0000000000..90d164e35e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/log.go @@ -0,0 +1,15 @@ +package hcs + +import "github.com/sirupsen/logrus" + +func logOperationBegin(ctx logrus.Fields, msg string) { + logrus.WithFields(ctx).Debug(msg) +} + +func logOperationEnd(ctx logrus.Fields, msg string, err error) { + if err == nil { + logrus.WithFields(ctx).Debug(msg) + } else { + logrus.WithFields(ctx).WithError(err).Error(msg) + } +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go index 8294d66d7b..67b9ecb57b 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go @@ -2,13 +2,14 @@ package hcs import ( "encoding/json" - "fmt" "io" "sync" "syscall" "time" + "github.com/Microsoft/hcsshim/internal/guestrequest" "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/logfields" "github.com/sirupsen/logrus" ) @@ -20,6 +21,21 @@ type Process struct { system *System cachedPipes *cachedPipes callbackNumber uintptr + + logctx logrus.Fields +} + +func newProcess(process hcsProcess, processID int, computeSystem *System) *Process { + return &Process{ + handle: process, + processID: processID, + system: computeSystem, + logctx: logrus.Fields{ + logfields.HCSOperation: "", + logfields.ContainerID: computeSystem.ID(), + logfields.ProcessID: processID, + }, + } } type cachedPipes struct { @@ -71,13 +87,69 @@ func (process *Process) SystemID() string { return process.system.ID() } +func (process *Process) logOperationBegin(operation string) { + process.logctx[logfields.HCSOperation] = operation + logOperationBegin( + process.logctx, + "hcsshim::Process - Begin Operation") +} + +func (process *Process) logOperationEnd(err error) { + var result string + if err == nil { + result = "Success" + } else { + result = "Error" + } + + logOperationEnd( + process.logctx, + "hcsshim::Process - End Operation - "+result, + err) + process.logctx[logfields.HCSOperation] = "" +} + +// Signal signals the process with `options`. +func (process *Process) Signal(options guestrequest.SignalProcessOptions) (err error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + + operation := "hcsshim::Process::Signal" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(err) }() + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + optionsb, err := json.Marshal(options) + if err != nil { + return err + } + + optionsStr := string(optionsb) + + var resultp *uint16 + completed := false + go syscallWatcher(process.logctx, &completed) + err = hcsSignalProcess(process.handle, optionsStr, &resultp) + completed = true + events := processHcsResult(resultp) + if err != nil { + return makeProcessError(process, operation, err, events) + } + + return nil +} + // Kill signals the process to terminate but does not wait for it to finish terminating. -func (process *Process) Kill() error { +func (process *Process) Kill() (err error) { process.handleLock.RLock() defer process.handleLock.RUnlock() - operation := "Kill" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) + + operation := "hcsshim::Process::Kill" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(err) }() if process.handle == 0 { return makeProcessError(process, operation, ErrAlreadyClosed, nil) @@ -85,56 +157,54 @@ func (process *Process) Kill() error { var resultp *uint16 completed := false - go syscallWatcher(fmt.Sprintf("TerminateProcess %s: %d", process.SystemID(), process.Pid()), &completed) - err := hcsTerminateProcess(process.handle, &resultp) + go syscallWatcher(process.logctx, &completed) + err = hcsTerminateProcess(process.handle, &resultp) completed = true events := processHcsResult(resultp) if err != nil { return makeProcessError(process, operation, err, events) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) return nil } // Wait waits for the process to exit. -func (process *Process) Wait() error { - operation := "Wait" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) +func (process *Process) Wait() (err error) { + operation := "hcsshim::Process::Wait" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(err) }() - err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, nil) + err = waitForNotification(process.callbackNumber, hcsNotificationProcessExited, nil) if err != nil { return makeProcessError(process, operation, err, nil) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) return nil } // WaitTimeout waits for the process to exit or the duration to elapse. It returns // false if timeout occurs. -func (process *Process) WaitTimeout(timeout time.Duration) error { - operation := "WaitTimeout" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) +func (process *Process) WaitTimeout(timeout time.Duration) (err error) { + operation := "hcssshim::Process::WaitTimeout" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(err) }() - err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, &timeout) + err = waitForNotification(process.callbackNumber, hcsNotificationProcessExited, &timeout) if err != nil { return makeProcessError(process, operation, err, nil) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) return nil } // ResizeConsole resizes the console of the process. -func (process *Process) ResizeConsole(width, height uint16) error { +func (process *Process) ResizeConsole(width, height uint16) (err error) { process.handleLock.RLock() defer process.handleLock.RUnlock() - operation := "ResizeConsole" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) + + operation := "hcsshim::Process::ResizeConsole" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(err) }() if process.handle == 0 { return makeProcessError(process, operation, ErrAlreadyClosed, nil) @@ -162,16 +232,16 @@ func (process *Process) ResizeConsole(width, height uint16) error { return makeProcessError(process, operation, err, events) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) return nil } -func (process *Process) Properties() (*ProcessStatus, error) { +func (process *Process) Properties() (_ *ProcessStatus, err error) { process.handleLock.RLock() defer process.handleLock.RUnlock() - operation := "Properties" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) + + operation := "hcsshim::Process::Properties" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(err) }() if process.handle == 0 { return nil, makeProcessError(process, operation, ErrAlreadyClosed, nil) @@ -182,8 +252,8 @@ func (process *Process) Properties() (*ProcessStatus, error) { propertiesp *uint16 ) completed := false - go syscallWatcher(fmt.Sprintf("GetProcessProperties %s: %d", process.SystemID(), process.Pid()), &completed) - err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp) + go syscallWatcher(process.logctx, &completed) + err = hcsGetProcessProperties(process.handle, &propertiesp, &resultp) completed = true events := processHcsResult(resultp) if err != nil { @@ -200,14 +270,16 @@ func (process *Process) Properties() (*ProcessStatus, error) { return nil, makeProcessError(process, operation, err, nil) } - logrus.Debugf(title+" succeeded processid=%d, properties=%s", process.processID, propertiesRaw) return properties, nil } // ExitCode returns the exit code of the process. The process must have // already terminated. -func (process *Process) ExitCode() (int, error) { - operation := "ExitCode" +func (process *Process) ExitCode() (_ int, err error) { + operation := "hcsshim::Process::ExitCode" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(err) }() + properties, err := process.Properties() if err != nil { return 0, makeProcessError(process, operation, err, nil) @@ -227,12 +299,13 @@ func (process *Process) ExitCode() (int, error) { // Stdio returns the stdin, stdout, and stderr pipes, respectively. Closing // these pipes does not close the underlying pipes; it should be possible to // call this multiple times to get multiple interfaces. -func (process *Process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, error) { +func (process *Process) Stdio() (_ io.WriteCloser, _ io.ReadCloser, _ io.ReadCloser, err error) { process.handleLock.RLock() defer process.handleLock.RUnlock() - operation := "Stdio" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) + + operation := "hcsshim::Process::Stdio" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(err) }() if process.handle == 0 { return nil, nil, nil, makeProcessError(process, operation, ErrAlreadyClosed, nil) @@ -245,7 +318,7 @@ func (process *Process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, e processInfo hcsProcessInformation resultp *uint16 ) - err := hcsGetProcessInfo(process.handle, &processInfo, &resultp) + err = hcsGetProcessInfo(process.handle, &processInfo, &resultp) events := processHcsResult(resultp) if err != nil { return nil, nil, nil, makeProcessError(process, operation, err, events) @@ -265,18 +338,18 @@ func (process *Process) Stdio() (io.WriteCloser, io.ReadCloser, io.ReadCloser, e return nil, nil, nil, makeProcessError(process, operation, err, nil) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) return pipes[0], pipes[1], pipes[2], nil } // CloseStdin closes the write side of the stdin pipe so that the process is // notified on the read side that there is no more data in stdin. -func (process *Process) CloseStdin() error { +func (process *Process) CloseStdin() (err error) { process.handleLock.RLock() defer process.handleLock.RUnlock() - operation := "CloseStdin" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) + + operation := "hcsshim::Process::CloseStdin" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(err) }() if process.handle == 0 { return makeProcessError(process, operation, ErrAlreadyClosed, nil) @@ -303,35 +376,34 @@ func (process *Process) CloseStdin() error { return makeProcessError(process, operation, err, events) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) return nil } // Close cleans up any state associated with the process but does not kill // or wait on it. -func (process *Process) Close() error { +func (process *Process) Close() (err error) { process.handleLock.Lock() defer process.handleLock.Unlock() - operation := "Close" - title := "hcsshim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) + + operation := "hcsshim::Process::Close" + process.logOperationBegin(operation) + defer func() { process.logOperationEnd(err) }() // Don't double free this if process.handle == 0 { return nil } - if err := process.unregisterCallback(); err != nil { + if err = process.unregisterCallback(); err != nil { return makeProcessError(process, operation, err, nil) } - if err := hcsCloseProcess(process.handle); err != nil { + if err = hcsCloseProcess(process.handle); err != nil { return makeProcessError(process, operation, err, nil) } process.handle = 0 - logrus.Debugf(title+" succeeded processid=%d", process.processID) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go index 57afd5ec6b..09d6f3d66c 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go @@ -2,7 +2,6 @@ package hcs import ( "encoding/json" - "fmt" "os" "strconv" "sync" @@ -10,6 +9,7 @@ import ( "time" "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/logfields" "github.com/Microsoft/hcsshim/internal/schema1" "github.com/Microsoft/hcsshim/internal/timeout" "github.com/sirupsen/logrus" @@ -41,16 +41,49 @@ type System struct { handle hcsSystem id string callbackNumber uintptr -} -// CreateComputeSystem creates a new compute system with the given configuration but does not start it. -func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (*System, error) { - operation := "CreateComputeSystem" - title := "hcsshim::" + operation + logctx logrus.Fields +} - computeSystem := &System{ +func newSystem(id string) *System { + return &System{ id: id, + logctx: logrus.Fields{ + logfields.HCSOperation: "", + logfields.ContainerID: id, + }, } +} + +func (computeSystem *System) logOperationBegin(operation string) { + computeSystem.logctx[logfields.HCSOperation] = operation + logOperationBegin( + computeSystem.logctx, + "hcsshim::ComputeSystem - Begin Operation") +} + +func (computeSystem *System) logOperationEnd(err error) { + var result string + if err == nil { + result = "Success" + } else { + result = "Error" + } + + logOperationEnd( + computeSystem.logctx, + "hcsshim::ComputeSystem - End Operation - "+result, + err) + computeSystem.logctx[logfields.HCSOperation] = "" +} + +// CreateComputeSystem creates a new compute system with the given configuration but does not start it. +func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (_ *System, err error) { + operation := "hcsshim::CreateComputeSystem" + + computeSystem := newSystem(id) + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() hcsDocumentB, err := json.Marshal(hcsDocumentInterface) if err != nil { @@ -58,19 +91,22 @@ func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (*System, } hcsDocument := string(hcsDocumentB) - logrus.Debugf(title+" ID=%s config=%s", id, hcsDocument) + + logrus.WithFields(computeSystem.logctx). + WithField(logfields.JSON, hcsDocument). + Debug("HCS ComputeSystem Document") var ( resultp *uint16 identity syscall.Handle ) completed := false - go syscallWatcher(fmt.Sprintf("CreateCompleteSystem %s: %s", id, hcsDocument), &completed) + go syscallWatcher(computeSystem.logctx, &completed) createError := hcsCreateComputeSystem(id, hcsDocument, identity, &computeSystem.handle, &resultp) completed = true if createError == nil || IsPending(createError) { - if err := computeSystem.registerCallback(); err != nil { + if err = computeSystem.registerCallback(); err != nil { // Terminate the compute system if it still exists. We're okay to // ignore a failure here. computeSystem.Terminate() @@ -88,25 +124,22 @@ func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (*System, return nil, makeSystemError(computeSystem, operation, hcsDocument, err, events) } - logrus.Debugf(title+" succeeded id=%s handle=%d", id, computeSystem.handle) return computeSystem, nil } // OpenComputeSystem opens an existing compute system by ID. -func OpenComputeSystem(id string) (*System, error) { - operation := "OpenComputeSystem" - title := "hcsshim::" + operation - logrus.Debugf(title+" ID=%s", id) +func OpenComputeSystem(id string) (_ *System, err error) { + operation := "hcsshim::OpenComputeSystem" - computeSystem := &System{ - id: id, - } + computeSystem := newSystem(id) + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() var ( handle hcsSystem resultp *uint16 ) - err := hcsOpenComputeSystem(id, &handle, &resultp) + err = hcsOpenComputeSystem(id, &handle, &resultp) events := processHcsResult(resultp) if err != nil { return nil, makeSystemError(computeSystem, operation, "", err, events) @@ -114,18 +147,36 @@ func OpenComputeSystem(id string) (*System, error) { computeSystem.handle = handle - if err := computeSystem.registerCallback(); err != nil { + if err = computeSystem.registerCallback(); err != nil { return nil, makeSystemError(computeSystem, operation, "", err, nil) } - logrus.Debugf(title+" succeeded id=%s handle=%d", id, handle) return computeSystem, nil } // GetComputeSystems gets a list of the compute systems on the system that match the query -func GetComputeSystems(q schema1.ComputeSystemQuery) ([]schema1.ContainerProperties, error) { - operation := "GetComputeSystems" - title := "hcsshim::" + operation +func GetComputeSystems(q schema1.ComputeSystemQuery) (_ []schema1.ContainerProperties, err error) { + operation := "hcsshim::GetComputeSystems" + fields := logrus.Fields{ + logfields.HCSOperation: operation, + } + logOperationBegin( + fields, + "hcsshim::ComputeSystem - Begin Operation") + + defer func() { + var result string + if err == nil { + result = "Success" + } else { + result = "Error" + } + + logOperationEnd( + fields, + "hcsshim::ComputeSystem - End Operation - "+result, + err) + }() queryb, err := json.Marshal(q) if err != nil { @@ -133,14 +184,17 @@ func GetComputeSystems(q schema1.ComputeSystemQuery) ([]schema1.ContainerPropert } query := string(queryb) - logrus.Debugf(title+" query=%s", query) + + logrus.WithFields(fields). + WithField(logfields.JSON, query). + Debug("HCS ComputeSystem Query") var ( resultp *uint16 computeSystemsp *uint16 ) completed := false - go syscallWatcher(fmt.Sprintf("GetComputeSystems %s:", query), &completed) + go syscallWatcher(fields, &completed) err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp) completed = true events := processHcsResult(resultp) @@ -153,20 +207,21 @@ func GetComputeSystems(q schema1.ComputeSystemQuery) ([]schema1.ContainerPropert } computeSystemsRaw := interop.ConvertAndFreeCoTaskMemBytes(computeSystemsp) computeSystems := []schema1.ContainerProperties{} - if err := json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil { + if err = json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil { return nil, err } - logrus.Debugf(title + " succeeded") return computeSystems, nil } // Start synchronously starts the computeSystem. -func (computeSystem *System) Start() error { +func (computeSystem *System) Start() (err error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::Start ID=" + computeSystem.ID() - logrus.Debugf(title) + + operation := "hcsshim::ComputeSystem::Start" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() if computeSystem.handle == 0 { return makeSystemError(computeSystem, "Start", "", ErrAlreadyClosed, nil) @@ -200,15 +255,14 @@ func (computeSystem *System) Start() error { var resultp *uint16 completed := false - go syscallWatcher(fmt.Sprintf("StartComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsStartComputeSystem(computeSystem.handle, "", &resultp) + go syscallWatcher(computeSystem.logctx, &completed) + err = hcsStartComputeSystem(computeSystem.handle, "", &resultp) completed = true events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.SystemStart) if err != nil { return makeSystemError(computeSystem, "Start", "", err, events) } - logrus.Debugf(title + " succeeded") return nil } @@ -219,36 +273,40 @@ func (computeSystem *System) ID() string { // Shutdown requests a compute system shutdown, if IsPending() on the error returned is true, // it may not actually be shut down until Wait() succeeds. -func (computeSystem *System) Shutdown() error { +func (computeSystem *System) Shutdown() (err error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::Shutdown" - logrus.Debugf(title) + + operation := "hcsshim::ComputeSystem::Shutdown" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() + if computeSystem.handle == 0 { return makeSystemError(computeSystem, "Shutdown", "", ErrAlreadyClosed, nil) } var resultp *uint16 completed := false - go syscallWatcher(fmt.Sprintf("ShutdownComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsShutdownComputeSystem(computeSystem.handle, "", &resultp) + go syscallWatcher(computeSystem.logctx, &completed) + err = hcsShutdownComputeSystem(computeSystem.handle, "", &resultp) completed = true events := processHcsResult(resultp) if err != nil { return makeSystemError(computeSystem, "Shutdown", "", err, events) } - logrus.Debugf(title + " succeeded") return nil } // Terminate requests a compute system terminate, if IsPending() on the error returned is true, // it may not actually be shut down until Wait() succeeds. -func (computeSystem *System) Terminate() error { +func (computeSystem *System) Terminate() (err error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::Terminate ID=" + computeSystem.ID() - logrus.Debugf(title) + + operation := "hcsshim::ComputeSystem::Terminate" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() if computeSystem.handle == 0 { return makeSystemError(computeSystem, "Terminate", "", ErrAlreadyClosed, nil) @@ -256,59 +314,81 @@ func (computeSystem *System) Terminate() error { var resultp *uint16 completed := false - go syscallWatcher(fmt.Sprintf("TerminateComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsTerminateComputeSystem(computeSystem.handle, "", &resultp) + go syscallWatcher(computeSystem.logctx, &completed) + err = hcsTerminateComputeSystem(computeSystem.handle, "", &resultp) completed = true events := processHcsResult(resultp) - if err != nil { + if err != nil && err != ErrVmcomputeAlreadyStopped { return makeSystemError(computeSystem, "Terminate", "", err, events) } - logrus.Debugf(title + " succeeded") return nil } // Wait synchronously waits for the compute system to shutdown or terminate. -func (computeSystem *System) Wait() error { - title := "hcsshim::ComputeSystem::Wait ID=" + computeSystem.ID() - logrus.Debugf(title) +func (computeSystem *System) Wait() (err error) { + operation := "hcsshim::ComputeSystem::Wait" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() - err := waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, nil) + err = waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, nil) if err != nil { return makeSystemError(computeSystem, "Wait", "", err, nil) } - logrus.Debugf(title + " succeeded") + return nil +} + +// WaitExpectedError synchronously waits for the compute system to shutdown or +// terminate, and ignores the passed error if it occurs. +func (computeSystem *System) WaitExpectedError(expected error) (err error) { + operation := "hcsshim::ComputeSystem::WaitExpectedError" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() + + err = waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, nil) + if err != nil && err != expected { + return makeSystemError(computeSystem, "WaitExpectedError", "", err, nil) + } + return nil } // WaitTimeout synchronously waits for the compute system to terminate or the duration to elapse. // If the timeout expires, IsTimeout(err) == true -func (computeSystem *System) WaitTimeout(timeout time.Duration) error { - title := "hcsshim::ComputeSystem::WaitTimeout ID=" + computeSystem.ID() - logrus.Debugf(title) +func (computeSystem *System) WaitTimeout(timeout time.Duration) (err error) { + operation := "hcsshim::ComputeSystem::WaitTimeout" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() - err := waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, &timeout) + err = waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, &timeout) if err != nil { return makeSystemError(computeSystem, "WaitTimeout", "", err, nil) } - logrus.Debugf(title + " succeeded") return nil } -func (computeSystem *System) Properties(types ...schema1.PropertyType) (*schema1.ContainerProperties, error) { +func (computeSystem *System) Properties(types ...schema1.PropertyType) (_ *schema1.ContainerProperties, err error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() + operation := "hcsshim::ComputeSystem::Properties" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() + queryj, err := json.Marshal(schema1.PropertyQuery{types}) if err != nil { return nil, makeSystemError(computeSystem, "Properties", "", err, nil) } + logrus.WithFields(computeSystem.logctx). + WithField(logfields.JSON, queryj). + Debug("HCS ComputeSystem Properties Query") + var resultp, propertiesp *uint16 completed := false - go syscallWatcher(fmt.Sprintf("GetComputeSystemProperties %s:", computeSystem.ID()), &completed) + go syscallWatcher(computeSystem.logctx, &completed) err = hcsGetComputeSystemProperties(computeSystem.handle, string(queryj), &propertiesp, &resultp) completed = true events := processHcsResult(resultp) @@ -324,15 +404,18 @@ func (computeSystem *System) Properties(types ...schema1.PropertyType) (*schema1 if err := json.Unmarshal(propertiesRaw, properties); err != nil { return nil, makeSystemError(computeSystem, "Properties", "", err, nil) } + return properties, nil } // Pause pauses the execution of the computeSystem. This feature is not enabled in TP5. -func (computeSystem *System) Pause() error { +func (computeSystem *System) Pause() (err error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::Pause ID=" + computeSystem.ID() - logrus.Debugf(title) + + operation := "hcsshim::ComputeSystem::Pause" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() if computeSystem.handle == 0 { return makeSystemError(computeSystem, "Pause", "", ErrAlreadyClosed, nil) @@ -340,24 +423,25 @@ func (computeSystem *System) Pause() error { var resultp *uint16 completed := false - go syscallWatcher(fmt.Sprintf("PauseComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsPauseComputeSystem(computeSystem.handle, "", &resultp) + go syscallWatcher(computeSystem.logctx, &completed) + err = hcsPauseComputeSystem(computeSystem.handle, "", &resultp) completed = true events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.SystemPause) if err != nil { return makeSystemError(computeSystem, "Pause", "", err, events) } - logrus.Debugf(title + " succeeded") return nil } // Resume resumes the execution of the computeSystem. This feature is not enabled in TP5. -func (computeSystem *System) Resume() error { +func (computeSystem *System) Resume() (err error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::Resume ID=" + computeSystem.ID() - logrus.Debugf(title) + + operation := "hcsshim::ComputeSystem::Resume" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() if computeSystem.handle == 0 { return makeSystemError(computeSystem, "Resume", "", ErrAlreadyClosed, nil) @@ -365,23 +449,26 @@ func (computeSystem *System) Resume() error { var resultp *uint16 completed := false - go syscallWatcher(fmt.Sprintf("ResumeComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsResumeComputeSystem(computeSystem.handle, "", &resultp) + go syscallWatcher(computeSystem.logctx, &completed) + err = hcsResumeComputeSystem(computeSystem.handle, "", &resultp) completed = true events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.SystemResume) if err != nil { return makeSystemError(computeSystem, "Resume", "", err, events) } - logrus.Debugf(title + " succeeded") return nil } // CreateProcess launches a new process within the computeSystem. -func (computeSystem *System) CreateProcess(c interface{}) (*Process, error) { +func (computeSystem *System) CreateProcess(c interface{}) (_ *Process, err error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::CreateProcess ID=" + computeSystem.ID() + + operation := "hcsshim::ComputeSystem::CreateProcess" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() + var ( processInfo hcsProcessInformation processHandle hcsProcess @@ -398,10 +485,13 @@ func (computeSystem *System) CreateProcess(c interface{}) (*Process, error) { } configuration := string(configurationb) - logrus.Debugf(title+" config=%s", configuration) + + logrus.WithFields(computeSystem.logctx). + WithField(logfields.JSON, configuration). + Debug("HCS ComputeSystem Process Document") completed := false - go syscallWatcher(fmt.Sprintf("CreateProcess %s: %s", computeSystem.ID(), configuration), &completed) + go syscallWatcher(computeSystem.logctx, &completed) err = hcsCreateProcess(computeSystem.handle, configuration, &processInfo, &processHandle, &resultp) completed = true events := processHcsResult(resultp) @@ -409,31 +499,37 @@ func (computeSystem *System) CreateProcess(c interface{}) (*Process, error) { return nil, makeSystemError(computeSystem, "CreateProcess", configuration, err, events) } - process := &Process{ - handle: processHandle, - processID: int(processInfo.ProcessId), - system: computeSystem, - cachedPipes: &cachedPipes{ - stdIn: processInfo.StdInput, - stdOut: processInfo.StdOutput, - stdErr: processInfo.StdError, - }, + logrus.WithFields(computeSystem.logctx). + WithField(logfields.ProcessID, processInfo.ProcessId). + Debug("HCS ComputeSystem CreateProcess PID") + + process := newProcess(processHandle, int(processInfo.ProcessId), computeSystem) + process.cachedPipes = &cachedPipes{ + stdIn: processInfo.StdInput, + stdOut: processInfo.StdOutput, + stdErr: processInfo.StdError, } - if err := process.registerCallback(); err != nil { + if err = process.registerCallback(); err != nil { return nil, makeSystemError(computeSystem, "CreateProcess", "", err, nil) } - logrus.Debugf(title+" succeeded processid=%d", process.processID) return process, nil } // OpenProcess gets an interface to an existing process within the computeSystem. -func (computeSystem *System) OpenProcess(pid int) (*Process, error) { +func (computeSystem *System) OpenProcess(pid int) (_ *Process, err error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::ComputeSystem::OpenProcess ID=" + computeSystem.ID() - logrus.Debugf(title+" processid=%d", pid) + + // Add PID for the context of this operation + computeSystem.logctx[logfields.ProcessID] = pid + defer delete(computeSystem.logctx, logfields.ProcessID) + + operation := "hcsshim::ComputeSystem::OpenProcess" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() + var ( processHandle hcsProcess resultp *uint16 @@ -444,47 +540,43 @@ func (computeSystem *System) OpenProcess(pid int) (*Process, error) { } completed := false - go syscallWatcher(fmt.Sprintf("OpenProcess %s: %d", computeSystem.ID(), pid), &completed) - err := hcsOpenProcess(computeSystem.handle, uint32(pid), &processHandle, &resultp) + go syscallWatcher(computeSystem.logctx, &completed) + err = hcsOpenProcess(computeSystem.handle, uint32(pid), &processHandle, &resultp) completed = true events := processHcsResult(resultp) if err != nil { return nil, makeSystemError(computeSystem, "OpenProcess", "", err, events) } - process := &Process{ - handle: processHandle, - processID: pid, - system: computeSystem, - } - - if err := process.registerCallback(); err != nil { + process := newProcess(processHandle, pid, computeSystem) + if err = process.registerCallback(); err != nil { return nil, makeSystemError(computeSystem, "OpenProcess", "", err, nil) } - logrus.Debugf(title+" succeeded processid=%s", process.processID) return process, nil } // Close cleans up any state associated with the compute system but does not terminate or wait for it. -func (computeSystem *System) Close() error { +func (computeSystem *System) Close() (err error) { computeSystem.handleLock.Lock() defer computeSystem.handleLock.Unlock() - title := "hcsshim::ComputeSystem::Close ID=" + computeSystem.ID() - logrus.Debugf(title) + + operation := "hcsshim::ComputeSystem::Close" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() // Don't double free this if computeSystem.handle == 0 { return nil } - if err := computeSystem.unregisterCallback(); err != nil { + if err = computeSystem.unregisterCallback(); err != nil { return makeSystemError(computeSystem, "Close", "", err, nil) } completed := false - go syscallWatcher(fmt.Sprintf("CloseComputeSystem %s:", computeSystem.ID()), &completed) - err := hcsCloseComputeSystem(computeSystem.handle) + go syscallWatcher(computeSystem.logctx, &completed) + err = hcsCloseComputeSystem(computeSystem.handle) completed = true if err != nil { return makeSystemError(computeSystem, "Close", "", err, nil) @@ -492,7 +584,6 @@ func (computeSystem *System) Close() error { computeSystem.handle = 0 - logrus.Debugf(title + " succeeded") return nil } @@ -553,11 +644,14 @@ func (computeSystem *System) unregisterCallback() error { return nil } -// Modifies the System by sending a request to HCS -func (computeSystem *System) Modify(config interface{}) error { +// Modify the System by sending a request to HCS +func (computeSystem *System) Modify(config interface{}) (err error) { computeSystem.handleLock.RLock() defer computeSystem.handleLock.RUnlock() - title := "hcsshim::Modify ID=" + computeSystem.id + + operation := "hcsshim::ComputeSystem::Modify" + computeSystem.logOperationBegin(operation) + defer func() { computeSystem.logOperationEnd(err) }() if computeSystem.handle == 0 { return makeSystemError(computeSystem, "Modify", "", ErrAlreadyClosed, nil) @@ -569,17 +663,20 @@ func (computeSystem *System) Modify(config interface{}) error { } requestString := string(requestJSON) - logrus.Debugf(title + " " + requestString) + + logrus.WithFields(computeSystem.logctx). + WithField(logfields.JSON, requestString). + Debug("HCS ComputeSystem Modify Document") var resultp *uint16 completed := false - go syscallWatcher(fmt.Sprintf("ModifyComputeSystem %s: %s", computeSystem.ID(), requestString), &completed) + go syscallWatcher(computeSystem.logctx, &completed) err = hcsModifyComputeSystem(computeSystem.handle, requestString, &resultp) completed = true events := processHcsResult(resultp) if err != nil { return makeSystemError(computeSystem, "Modify", requestString, err, events) } - logrus.Debugf(title + " succeeded ") + return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go index 6b94bc9ff8..e09dd1334f 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/watcher.go @@ -3,6 +3,7 @@ package hcs import ( "time" + "github.com/Microsoft/hcsshim/internal/logfields" "github.com/Microsoft/hcsshim/internal/timeout" "github.com/sirupsen/logrus" ) @@ -17,14 +18,16 @@ import ( // Usage is: // // completed := false -// go syscallWatcher("some description", &completed) +// go syscallWatcher(context, &completed) // // completed = true // -func syscallWatcher(description string, syscallCompleted *bool) { +func syscallWatcher(context logrus.Fields, syscallCompleted *bool) { time.Sleep(timeout.SyscallWatcher) if *syscallCompleted { return } - logrus.Warnf("%s: Did not complete within %s. This may indicate a platform issue. If it appears to be making no forward progress, obtain the stacks and see is there is a syscall stuck in the platform API for a significant length of time.", description, timeout.SyscallWatcher) + logrus.WithFields(context). + WithField(logfields.Timeout, timeout.SyscallWatcher). + Warning("Syscall did not complete within operation timeout. This may indicate a platform issue. If it appears to be making no forward progress, obtain the stacks and see if there is a syscall stuck in the platform API for a significant length of time.") } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go index 48d5cd32b9..fcd5cdc87f 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated mksyscall_windows.exe DO NOT EDIT package hcs @@ -6,7 +6,6 @@ import ( "syscall" "unsafe" - "github.com/Microsoft/hcsshim/internal/interop" "golang.org/x/sys/windows" ) @@ -57,12 +56,13 @@ var ( procHcsOpenProcess = modvmcompute.NewProc("HcsOpenProcess") procHcsCloseProcess = modvmcompute.NewProc("HcsCloseProcess") procHcsTerminateProcess = modvmcompute.NewProc("HcsTerminateProcess") - procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo") - procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties") - procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess") - procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties") - procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback") - procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback") + + procHcsGetProcessInfo = modvmcompute.NewProc("HcsGetProcessInfo") + procHcsGetProcessProperties = modvmcompute.NewProc("HcsGetProcessProperties") + procHcsModifyProcess = modvmcompute.NewProc("HcsModifyProcess") + procHcsGetServiceProperties = modvmcompute.NewProc("HcsGetServiceProperties") + procHcsRegisterProcessCallback = modvmcompute.NewProc("HcsRegisterProcessCallback") + procHcsUnregisterProcessCallback = modvmcompute.NewProc("HcsUnregisterProcessCallback") ) func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) { @@ -80,7 +80,10 @@ func _hcsEnumerateComputeSystems(query *uint16, computeSystems **uint16, result } r0, _, _ := syscall.Syscall(procHcsEnumerateComputeSystems.Addr(), 3, uintptr(unsafe.Pointer(query)), uintptr(unsafe.Pointer(computeSystems)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -105,7 +108,10 @@ func _hcsCreateComputeSystem(id *uint16, configuration *uint16, identity syscall } r0, _, _ := syscall.Syscall6(procHcsCreateComputeSystem.Addr(), 5, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(configuration)), uintptr(identity), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -125,7 +131,10 @@ func _hcsOpenComputeSystem(id *uint16, computeSystem *hcsSystem, result **uint16 } r0, _, _ := syscall.Syscall(procHcsOpenComputeSystem.Addr(), 3, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -136,7 +145,10 @@ func hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) { } r0, _, _ := syscall.Syscall(procHcsCloseComputeSystem.Addr(), 1, uintptr(computeSystem), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -156,7 +168,10 @@ func _hcsStartComputeSystem(computeSystem hcsSystem, options *uint16, result **u } r0, _, _ := syscall.Syscall(procHcsStartComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -176,7 +191,10 @@ func _hcsShutdownComputeSystem(computeSystem hcsSystem, options *uint16, result } r0, _, _ := syscall.Syscall(procHcsShutdownComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -196,7 +214,10 @@ func _hcsTerminateComputeSystem(computeSystem hcsSystem, options *uint16, result } r0, _, _ := syscall.Syscall(procHcsTerminateComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -216,7 +237,10 @@ func _hcsPauseComputeSystem(computeSystem hcsSystem, options *uint16, result **u } r0, _, _ := syscall.Syscall(procHcsPauseComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -236,7 +260,10 @@ func _hcsResumeComputeSystem(computeSystem hcsSystem, options *uint16, result ** } r0, _, _ := syscall.Syscall(procHcsResumeComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -256,7 +283,10 @@ func _hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery *uint } r0, _, _ := syscall.Syscall6(procHcsGetComputeSystemProperties.Addr(), 4, uintptr(computeSystem), uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -276,7 +306,10 @@ func _hcsModifyComputeSystem(computeSystem hcsSystem, configuration *uint16, res } r0, _, _ := syscall.Syscall(procHcsModifyComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -287,7 +320,10 @@ func hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, } r0, _, _ := syscall.Syscall6(procHcsRegisterComputeSystemCallback.Addr(), 4, uintptr(computeSystem), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -298,7 +334,10 @@ func hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) { } r0, _, _ := syscall.Syscall(procHcsUnregisterComputeSystemCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -318,7 +357,10 @@ func _hcsCreateProcess(computeSystem hcsSystem, processParameters *uint16, proce } r0, _, _ := syscall.Syscall6(procHcsCreateProcess.Addr(), 5, uintptr(computeSystem), uintptr(unsafe.Pointer(processParameters)), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -329,7 +371,10 @@ func hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, re } r0, _, _ := syscall.Syscall6(procHcsOpenProcess.Addr(), 4, uintptr(computeSystem), uintptr(pid), uintptr(unsafe.Pointer(process)), uintptr(unsafe.Pointer(result)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -340,7 +385,10 @@ func hcsCloseProcess(process hcsProcess) (hr error) { } r0, _, _ := syscall.Syscall(procHcsCloseProcess.Addr(), 1, uintptr(process), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -351,7 +399,33 @@ func hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) { } r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 2, uintptr(process), uintptr(unsafe.Pointer(result)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) + } + return +} + +func hcsSignalProcess(process hcsProcess, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsSignalProcess(process, _p0, result) +} + +func _hcsSignalProcess(process hcsProcess, options *uint16, result **uint16) (hr error) { + if hr = procHcsTerminateProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -362,7 +436,10 @@ func hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInforma } r0, _, _ := syscall.Syscall(procHcsGetProcessInfo.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -373,7 +450,10 @@ func hcsGetProcessProperties(process hcsProcess, processProperties **uint16, res } r0, _, _ := syscall.Syscall(procHcsGetProcessProperties.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processProperties)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -393,7 +473,10 @@ func _hcsModifyProcess(process hcsProcess, settings *uint16, result **uint16) (h } r0, _, _ := syscall.Syscall(procHcsModifyProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -413,7 +496,10 @@ func _hcsGetServiceProperties(propertyQuery *uint16, properties **uint16, result } r0, _, _ := syscall.Syscall(procHcsGetServiceProperties.Addr(), 3, uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -424,7 +510,10 @@ func hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context ui } r0, _, _ := syscall.Syscall6(procHcsRegisterProcessCallback.Addr(), 4, uintptr(process), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -435,7 +524,10 @@ func hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) { } r0, _, _ := syscall.Syscall(procHcsUnregisterProcessCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go b/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go index c8d362c66c..921c2c8556 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go @@ -36,10 +36,6 @@ func New(err error, title, rest string) error { return &HcsError{title, rest, err} } -func Errorf(err error, title, format string, a ...interface{}) error { - return New(err, title, fmt.Sprintf(format, a...)) -} - func Win32FromError(err error) uint32 { if herr, ok := err.(*HcsError); ok { return Win32FromError(herr.Err) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/hcsdoc_wcow.go b/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/hcsdoc_wcow.go index 55cd4106e5..8fce2f3f19 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/hcsdoc_wcow.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/hcsdoc_wcow.go @@ -9,12 +9,13 @@ import ( "runtime" "strings" - "github.com/Microsoft/hcsshim/internal/osversion" "github.com/Microsoft/hcsshim/internal/schema1" "github.com/Microsoft/hcsshim/internal/schema2" "github.com/Microsoft/hcsshim/internal/schemaversion" + "github.com/Microsoft/hcsshim/internal/uvm" "github.com/Microsoft/hcsshim/internal/uvmfolder" "github.com/Microsoft/hcsshim/internal/wclayer" + "github.com/Microsoft/hcsshim/osversion" "github.com/sirupsen/logrus" ) @@ -146,8 +147,8 @@ func createWindowsContainerDocument(coi *createOptionsInternal) (interface{}, er if (schemaversion.IsV21(coi.actualSchemaVersion) && coi.HostingSystem == nil) || (schemaversion.IsV10(coi.actualSchemaVersion) && coi.Spec.Windows.HyperV == nil) { // Argon v1 or v2. - const volumeGUIDRegex = `^\\\\\?\\(Volume)\{{0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}\}\\$` - if _, err := regexp.MatchString(volumeGUIDRegex, coi.Spec.Root.Path); err != nil { + const volumeGUIDRegex = `^\\\\\?\\(Volume)\{{0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}\}(|\\)$` + if matched, err := regexp.MatchString(volumeGUIDRegex, coi.Spec.Root.Path); !matched || err != nil { return nil, fmt.Errorf(`invalid container spec - Root.Path '%s' must be a volume GUID path in the format '\\?\Volume{GUID}\'`, coi.Spec.Root.Path) } if coi.Spec.Root.Path[len(coi.Spec.Root.Path)-1] != '\\' { @@ -228,7 +229,15 @@ func createWindowsContainerDocument(coi *createOptionsInternal) (interface{}, er } else { uvmPath, err := coi.HostingSystem.GetVSMBUvmPath(mount.Source) if err != nil { - return nil, err + if err == uvm.ErrNotAttached { + // It could also be a scsi mount. + uvmPath, err = coi.HostingSystem.GetScsiUvmPath(mount.Source) + if err != nil { + return nil, err + } + } else { + return nil, err + } } mdv2.HostPath = uvmPath } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/layers.go b/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/layers.go index a7c3e690e5..dabca16045 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/layers.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/layers.go @@ -4,6 +4,7 @@ package hcsoci import ( "fmt" + "os" "path" "path/filepath" @@ -17,9 +18,10 @@ import ( "github.com/sirupsen/logrus" ) -type vpMemEntry struct { +type lcowLayerEntry struct { hostPath string uvmPath string + scsi bool } const scratchPath = "scratch" @@ -71,11 +73,12 @@ func MountContainerLayers(layerFolders []string, guestRoot string, uvm *uvm.Util logrus.Debugf("hcsshim::mountContainerLayers Is a %s V2 UVM", uvm.OS()) // Add each read-only layers. For Windows, this is a VSMB share with the ResourceUri ending in - // a GUID based on the folder path. For Linux, this is a VPMEM device. + // a GUID based on the folder path. For Linux, this is a VPMEM device, except where is over the + // max size supported, where we put it on SCSI instead. // // Each layer is ref-counted so that multiple containers in the same utility VM can share them. - var vsmbAdded []string - var vpmemAdded []vpMemEntry + var wcowLayersAdded []string + var lcowlayersAdded []lcowLayerEntry attachedSCSIHostPath := "" for _, layerPath := range layerFolders[:len(layerFolders)-1] { @@ -90,21 +93,42 @@ func MountContainerLayers(layerFolders []string, guestRoot string, uvm *uvm.Util } err = uvm.AddVSMB(layerPath, "", options) if err == nil { - vsmbAdded = append(vsmbAdded, layerPath) + wcowLayersAdded = append(wcowLayersAdded, layerPath) } } else { uvmPath := "" - _, uvmPath, err = uvm.AddVPMEM(filepath.Join(layerPath, "layer.vhd"), true) // UVM path is calculated. Will be /tmp/vN/ - if err == nil { - vpmemAdded = append(vpmemAdded, - vpMemEntry{ - hostPath: filepath.Join(layerPath, "layer.vhd"), - uvmPath: uvmPath, - }) + hostPath := filepath.Join(layerPath, "layer.vhd") + + var fi os.FileInfo + fi, err = os.Stat(hostPath) + if err == nil && uint64(fi.Size()) > uvm.PMemMaxSizeBytes() { + // Too big for PMEM. Add on SCSI instead (at /tmp/S/). + var ( + controller int + lun int32 + ) + controller, lun, err = uvm.AddSCSILayer(hostPath) + if err == nil { + lcowlayersAdded = append(lcowlayersAdded, + lcowLayerEntry{ + hostPath: hostPath, + uvmPath: fmt.Sprintf("/tmp/S%d/%d", controller, lun), + scsi: true, + }) + } + } else { + _, uvmPath, err = uvm.AddVPMEM(hostPath, true) // UVM path is calculated. Will be /tmp/vN/ + if err == nil { + lcowlayersAdded = append(lcowlayersAdded, + lcowLayerEntry{ + hostPath: hostPath, + uvmPath: uvmPath, + }) + } } } if err != nil { - cleanupOnMountFailure(uvm, vsmbAdded, vpmemAdded, attachedSCSIHostPath) + cleanupOnMountFailure(uvm, wcowLayersAdded, lcowlayersAdded, attachedSCSIHostPath) return nil, err } } @@ -113,19 +137,11 @@ func MountContainerLayers(layerFolders []string, guestRoot string, uvm *uvm.Util // utility VM will be C:\. hostPath := filepath.Join(layerFolders[len(layerFolders)-1], "sandbox.vhdx") - // On Linux, we need to grant access to the scratch - if uvm.OS() == "linux" { - if err := wclayer.GrantVmAccess(uvm.ID(), hostPath); err != nil { - cleanupOnMountFailure(uvm, vsmbAdded, vpmemAdded, attachedSCSIHostPath) - return nil, err - } - } - // BUGBUG Rename guestRoot better. containerScratchPathInUVM := ospath.Join(uvm.OS(), guestRoot, scratchPath) - _, _, err := uvm.AddSCSI(hostPath, containerScratchPathInUVM) + _, _, err := uvm.AddSCSI(hostPath, containerScratchPathInUVM, false) if err != nil { - cleanupOnMountFailure(uvm, vsmbAdded, vpmemAdded, attachedSCSIHostPath) + cleanupOnMountFailure(uvm, wcowLayersAdded, lcowlayersAdded, attachedSCSIHostPath) return nil, err } attachedSCSIHostPath = hostPath @@ -133,9 +149,9 @@ func MountContainerLayers(layerFolders []string, guestRoot string, uvm *uvm.Util if uvm.OS() == "windows" { // Load the filter at the C:\s location calculated above. We pass into this request each of the // read-only layer folders. - layers, err := computeV2Layers(uvm, vsmbAdded) + layers, err := computeV2Layers(uvm, wcowLayersAdded) if err != nil { - cleanupOnMountFailure(uvm, vsmbAdded, vpmemAdded, attachedSCSIHostPath) + cleanupOnMountFailure(uvm, wcowLayersAdded, lcowlayersAdded, attachedSCSIHostPath) return nil, err } guestRequest := guestrequest.CombinedLayers{ @@ -150,7 +166,7 @@ func MountContainerLayers(layerFolders []string, guestRoot string, uvm *uvm.Util }, } if err := uvm.Modify(combinedLayersModification); err != nil { - cleanupOnMountFailure(uvm, vsmbAdded, vpmemAdded, attachedSCSIHostPath) + cleanupOnMountFailure(uvm, wcowLayersAdded, lcowlayersAdded, attachedSCSIHostPath) return nil, err } logrus.Debugln("hcsshim::mountContainerLayers Succeeded") @@ -175,8 +191,8 @@ func MountContainerLayers(layerFolders []string, guestRoot string, uvm *uvm.Util // /dev/sd(b...) are scratch spaces for each container layers := []hcsschema.Layer{} - for _, vpmem := range vpmemAdded { - layers = append(layers, hcsschema.Layer{Path: vpmem.uvmPath}) + for _, l := range lcowlayersAdded { + layers = append(layers, hcsschema.Layer{Path: l.uvmPath}) } guestRequest := guestrequest.CombinedLayers{ ContainerRootPath: path.Join(guestRoot, rootfsPath), @@ -191,7 +207,7 @@ func MountContainerLayers(layerFolders []string, guestRoot string, uvm *uvm.Util }, } if err := uvm.Modify(combinedLayersModification); err != nil { - cleanupOnMountFailure(uvm, vsmbAdded, vpmemAdded, attachedSCSIHostPath) + cleanupOnMountFailure(uvm, wcowLayersAdded, lcowlayersAdded, attachedSCSIHostPath) return nil, err } logrus.Debugln("hcsshim::mountContainerLayers Succeeded") @@ -289,17 +305,26 @@ func UnmountContainerLayers(layerFolders []string, guestRoot string, uvm *uvm.Ut } } - // Remove each of the read-only layers from VPMEM. These's are ref-counted and - // only removed once the count drops to zero. This allows multiple containers - // to share layers. + // Remove each of the read-only layers from VPMEM (or SCSI). These's are ref-counted + // and only removed once the count drops to zero. This allows multiple containers to + // share layers. Note that SCSI is used on large layers. if uvm.OS() == "linux" && len(layerFolders) > 1 && (op&UnmountOperationVPMEM) == UnmountOperationVPMEM { for _, layerPath := range layerFolders[:len(layerFolders)-1] { - if e := uvm.RemoveVPMEM(filepath.Join(layerPath, "layer.vhd")); e != nil { - logrus.Debugln(e) - if retError == nil { - retError = e + hostPath := filepath.Join(layerPath, "layer.vhd") + if fi, err := os.Stat(hostPath); err != nil { + var e error + if uint64(fi.Size()) > uvm.PMemMaxSizeBytes() { + e = uvm.RemoveSCSI(hostPath) } else { - retError = errors.Wrapf(retError, e.Error()) + e = uvm.RemoveVPMEM(hostPath) + } + if e != nil { + logrus.Debugln(e) + if retError == nil { + retError = e + } else { + retError = errors.Wrapf(retError, e.Error()) + } } } } @@ -310,19 +335,23 @@ func UnmountContainerLayers(layerFolders []string, guestRoot string, uvm *uvm.Ut return retError } -func cleanupOnMountFailure(uvm *uvm.UtilityVM, vsmbShares []string, vpmemDevices []vpMemEntry, scsiHostPath string) { - for _, vsmbShare := range vsmbShares { - if err := uvm.RemoveVSMB(vsmbShare); err != nil { +func cleanupOnMountFailure(uvm *uvm.UtilityVM, wcowLayers []string, lcowLayers []lcowLayerEntry, scratchHostPath string) { + for _, wl := range wcowLayers { + if err := uvm.RemoveVSMB(wl); err != nil { logrus.Warnf("Possibly leaked vsmbshare on error removal path: %s", err) } } - for _, vpmemDevice := range vpmemDevices { - if err := uvm.RemoveVPMEM(vpmemDevice.hostPath); err != nil { + for _, ll := range lcowLayers { + if ll.scsi { + if err := uvm.RemoveSCSI(ll.hostPath); err != nil { + logrus.Warnf("Possibly leaked SCSI on error removal path: %s", err) + } + } else if err := uvm.RemoveVPMEM(ll.hostPath); err != nil { logrus.Warnf("Possibly leaked vpmemdevice on error removal path: %s", err) } } - if scsiHostPath != "" { - if err := uvm.RemoveSCSI(scsiHostPath); err != nil { + if scratchHostPath != "" { + if err := uvm.RemoveSCSI(scratchHostPath); err != nil { logrus.Warnf("Possibly leaked SCSI disk on error removal path: %s", err) } } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources.go b/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources.go index fd9fb91c47..ffbe855591 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources.go @@ -50,6 +50,10 @@ type Resources struct { // addedNetNSToVM indicates if the network namespace has been added to the containers utility VM addedNetNSToVM bool + + // scsiMounts is an array of the host-paths mounted into a utility VM to + // support scsi device passthrough. + scsiMounts []string } // TODO: Method on the resources? @@ -110,6 +114,13 @@ func ReleaseResources(r *Resources, vm *uvm.UtilityVM, all bool) error { } r.plan9Mounts = r.plan9Mounts[:len(r.plan9Mounts)-1] } + + for _, path := range r.scsiMounts { + if err := vm.RemoveSCSI(path); err != nil { + return err + } + r.scsiMounts = nil + } } return nil diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_lcow.go b/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_lcow.go index 77b6872d35..2de308d043 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_lcow.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_lcow.go @@ -48,7 +48,12 @@ func allocateLinuxResources(coi *createOptionsInternal, resources *Resources) er } for i, mount := range coi.Spec.Mounts { - if mount.Type != "bind" { + switch mount.Type { + case "bind": + case "physical-disk": + case "virtual-disk": + default: + // Unknown mount type continue } if mount.Destination == "" || mount.Source == "" { @@ -56,8 +61,6 @@ func allocateLinuxResources(coi *createOptionsInternal, resources *Resources) er } if coi.HostingSystem != nil { - logrus.Debugf("hcsshim::allocateLinuxResources Hot-adding Plan9 for OCI mount %+v", mount) - hostPath := mount.Source uvmPathForShare := path.Join(resources.containerRootInUVM, mountPathPrefix+strconv.Itoa(i)) @@ -68,12 +71,32 @@ func allocateLinuxResources(coi *createOptionsInternal, resources *Resources) er break } } - err := coi.HostingSystem.AddPlan9(hostPath, uvmPathForShare, readOnly) - if err != nil { - return fmt.Errorf("adding plan9 mount %+v: %s", mount, err) + + if mount.Type == "physical-disk" { + logrus.Debugf("hcsshim::allocateLinuxResources Hot-adding SCSI physical disk for OCI mount %+v", mount) + _, _, err := coi.HostingSystem.AddSCSIPhysicalDisk(hostPath, uvmPathForShare, readOnly) + if err != nil { + return fmt.Errorf("adding SCSI physical disk mount %+v: %s", mount, err) + } + resources.scsiMounts = append(resources.scsiMounts, hostPath) + coi.Spec.Mounts[i].Type = "none" + } else if mount.Type == "virtual-disk" { + logrus.Debugf("hcsshim::allocateLinuxResources Hot-adding SCSI virtual disk for OCI mount %+v", mount) + _, _, err := coi.HostingSystem.AddSCSI(hostPath, uvmPathForShare, readOnly) + if err != nil { + return fmt.Errorf("adding SCSI virtual disk mount %+v: %s", mount, err) + } + resources.scsiMounts = append(resources.scsiMounts, hostPath) + coi.Spec.Mounts[i].Type = "none" + } else { + logrus.Debugf("hcsshim::allocateLinuxResources Hot-adding Plan9 for OCI mount %+v", mount) + err := coi.HostingSystem.AddPlan9(hostPath, uvmPathForShare, readOnly) + if err != nil { + return fmt.Errorf("adding plan9 mount %+v: %s", mount, err) + } + resources.plan9Mounts = append(resources.plan9Mounts, hostPath) } coi.Spec.Mounts[i].Source = uvmPathForShare - resources.plan9Mounts = append(resources.plan9Mounts, hostPath) } } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_wcow.go b/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_wcow.go index bcfcd9e007..046fdfaef2 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_wcow.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcsoci/resources_wcow.go @@ -19,6 +19,10 @@ import ( ) func allocateWindowsResources(coi *createOptionsInternal, resources *Resources) error { + if coi.Spec == nil || coi.Spec.Windows == nil || coi.Spec.Windows.LayerFolders == nil { + return fmt.Errorf("field 'Spec.Windows.Layerfolders' is not populated") + } + scratchFolder := coi.Spec.Windows.LayerFolders[len(coi.Spec.Windows.LayerFolders)-1] logrus.Debugf("hcsshim::allocateWindowsResources scratch folder: %s", scratchFolder) @@ -61,32 +65,61 @@ func allocateWindowsResources(coi *createOptionsInternal, resources *Resources) // Validate each of the mounts. If this is a V2 Xenon, we have to add them as // VSMB shares to the utility VM. For V1 Xenon and Argons, there's nothing for // us to do as it's done by HCS. - for _, mount := range coi.Spec.Mounts { + for i, mount := range coi.Spec.Mounts { if mount.Destination == "" || mount.Source == "" { return fmt.Errorf("invalid OCI spec - a mount must have both source and a destination: %+v", mount) } - if mount.Type != "" { - return fmt.Errorf("invalid OCI spec - Type '%s' must not be set", mount.Type) + switch mount.Type { + case "": + case "physical-disk": + case "virtual-disk": + default: + return fmt.Errorf("invalid OCI spec - Type '%s' not supported", mount.Type) } if coi.HostingSystem != nil && schemaversion.IsV21(coi.actualSchemaVersion) { - logrus.Debugf("hcsshim::allocateWindowsResources Hot-adding VSMB share for OCI mount %+v", mount) - options := &hcsschema.VirtualSmbShareOptions{} + uvmPath := fmt.Sprintf("C:\\%s\\%d", coi.actualID, i) + + readOnly := false for _, o := range mount.Options { if strings.ToLower(o) == "ro" { + readOnly = true + break + } + } + if mount.Type == "physical-disk" { + logrus.Debugf("hcsshim::allocateWindowsResources Hot-adding SCSI physical disk for OCI mount %+v", mount) + _, _, err := coi.HostingSystem.AddSCSIPhysicalDisk(mount.Source, uvmPath, readOnly) + if err != nil { + return fmt.Errorf("adding SCSI physical disk mount %+v: %s", mount, err) + } + coi.Spec.Mounts[i].Type = "" + resources.scsiMounts = append(resources.scsiMounts, mount.Source) + } else if mount.Type == "virtual-disk" { + logrus.Debugf("hcsshim::allocateWindowsResources Hot-adding SCSI virtual disk for OCI mount %+v", mount) + _, _, err := coi.HostingSystem.AddSCSI(mount.Source, uvmPath, readOnly) + if err != nil { + return fmt.Errorf("adding SCSI virtual disk mount %+v: %s", mount, err) + } + coi.Spec.Mounts[i].Type = "" + resources.scsiMounts = append(resources.scsiMounts, mount.Source) + } else { + logrus.Debugf("hcsshim::allocateWindowsResources Hot-adding VSMB share for OCI mount %+v", mount) + options := &hcsschema.VirtualSmbShareOptions{} + if readOnly { options.ReadOnly = true options.CacheIo = true options.ShareRead = true options.ForceLevelIIOplocks = true break } - } - err := coi.HostingSystem.AddVSMB(mount.Source, "", options) - if err != nil { - return fmt.Errorf("failed to add VSMB share to utility VM for mount %+v: %s", mount, err) + err := coi.HostingSystem.AddVSMB(mount.Source, "", options) + if err != nil { + return fmt.Errorf("failed to add VSMB share to utility VM for mount %+v: %s", mount, err) + } + resources.vsmbMounts = append(resources.vsmbMounts, mount.Source) } - resources.vsmbMounts = append(resources.vsmbMounts, mount.Source) } } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go index ff7369e6ff..31322a6816 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go @@ -20,6 +20,7 @@ type ELBPolicy struct { SourceVIP string `json:"SourceVIP,omitempty"` VIPs []string `json:"VIPs,omitempty"` ILB bool `json:"ILB,omitempty"` + DSR bool `json:"IsDSR,omitempty"` } // LBPolicy is a structure defining schema for LoadBalancing based Policy diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go index 863e3429c7..204633a488 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated mksyscall_windows.exe DO NOT EDIT package hns @@ -6,7 +6,6 @@ import ( "syscall" "unsafe" - "github.com/Microsoft/hcsshim/internal/interop" "golang.org/x/sys/windows" ) @@ -68,7 +67,10 @@ func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16) } r0, _, _ := syscall.Syscall6(procHNSCall.Addr(), 4, uintptr(unsafe.Pointer(method)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(object)), uintptr(unsafe.Pointer(response)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go b/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go index f10c88d08c..2f6ec029ec 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go @@ -5,9 +5,9 @@ import ( "unsafe" ) -//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go interop.go +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go interop.go -//sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree +//sys coTaskMemFree(buffer unsafe.Pointer) = api_ms_win_core_com_l1_1_0.CoTaskMemFree func ConvertAndFreeCoTaskMemString(buffer *uint16) string { str := syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(buffer))[:]) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go index 2f5bf8f555..12b0c71c5a 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go @@ -1,4 +1,4 @@ -// Code generated by 'go generate'; DO NOT EDIT. +// Code generated mksyscall_windows.exe DO NOT EDIT package interop @@ -37,9 +37,9 @@ func errnoErr(e syscall.Errno) error { } var ( - modole32 = windows.NewLazySystemDLL("ole32.dll") + modapi_ms_win_core_com_l1_1_0 = windows.NewLazySystemDLL("api-ms-win-core-com-l1-1-0.dll") - procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") + procCoTaskMemFree = modapi_ms_win_core_com_l1_1_0.NewProc("CoTaskMemFree") ) func coTaskMemFree(buffer unsafe.Pointer) { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/lcow/scratch.go b/vendor/github.com/Microsoft/hcsshim/internal/lcow/scratch.go index 525cba6398..82f8b01b74 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/lcow/scratch.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/lcow/scratch.go @@ -5,14 +5,12 @@ import ( "fmt" "os" "strings" - "time" "github.com/Microsoft/go-winio/vhd" "github.com/Microsoft/hcsshim/internal/copyfile" "github.com/Microsoft/hcsshim/internal/timeout" "github.com/Microsoft/hcsshim/internal/uvm" - "github.com/Microsoft/hcsshim/internal/wclayer" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" ) @@ -46,14 +44,6 @@ func CreateScratch(lcowUVM *uvm.UtilityVM, destFile string, sizeGB uint32, cache return fmt.Errorf("failed to copy cached file '%s' to '%s': %s", cacheFile, destFile, err) } logrus.Debugf("hcsshim::CreateLCOWScratch: %s fulfilled from cache (%s)", destFile, cacheFile) - - if vmID != "" { - if err := wclayer.GrantVmAccess(vmID, destFile); err != nil { - os.Remove(destFile) - return err - } - } - return nil } } @@ -63,13 +53,7 @@ func CreateScratch(lcowUVM *uvm.UtilityVM, destFile string, sizeGB uint32, cache return fmt.Errorf("failed to create VHDx %s: %s", destFile, err) } - // uvmc.DebugLCOWGCS() - // Grant access - if err := wclayer.GrantVmAccess(lcowUVM.ID(), destFile); err != nil { - return err - } - - controller, lun, err := lcowUVM.AddSCSI(destFile, "") // No destination as not formatted + controller, lun, err := lcowUVM.AddSCSI(destFile, "", false) // No destination as not formatted if err != nil { return err } @@ -179,13 +163,6 @@ func CreateScratch(lcowUVM *uvm.UtilityVM, destFile string, sizeGB uint32, cache } } - if vmID != "" { - if err := wclayer.GrantVmAccess(vmID, destFile); err != nil { - os.Remove(destFile) - return err - } - } - logrus.Debugf("hcsshim::CreateLCOWScratch: %s created (non-cache)", destFile) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go b/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go new file mode 100644 index 0000000000..a1527d7060 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/logfields/fields.go @@ -0,0 +1,37 @@ +package logfields + +const ( + // Identifiers + + ContainerID = "cid" + UVMID = "uvm-id" + ProcessID = "pid" + + // Common Misc + + // Timeout represents an operation timeout. + Timeout = "timeout" + JSON = "json" + + // Keys/values + + Field = "field" + OCIAnnotation = "oci-annotation" + Value = "value" + + // Golang type's + + ExpectedType = "expected-type" + Bool = "bool" + Uint32 = "uint32" + Uint64 = "uint64" + + // HCS + + HCSOperation = "hcs-op" + HCSOperationResult = "hcs-op-result" + + // runhcs + + VMShimOperation = "vmshim-op" +) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/regstate/regstate.go b/vendor/github.com/Microsoft/hcsshim/internal/regstate/regstate.go index ba6c0c8cec..6c4a641561 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/regstate/regstate.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/regstate/regstate.go @@ -41,6 +41,11 @@ func (err *NotFoundError) Error() string { return fmt.Sprintf("ID '%s' was not found", err.Id) } +func IsNotFoundError(err error) bool { + _, ok := err.(*NotFoundError) + return ok +} + type NoStateError struct { ID string Key string diff --git a/vendor/github.com/Microsoft/hcsshim/internal/runhcs/container.go b/vendor/github.com/Microsoft/hcsshim/internal/runhcs/container.go index 1e5f7c0f24..3015c3640e 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/runhcs/container.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/runhcs/container.go @@ -1,26 +1,71 @@ -package runhcs - -import "time" - -// ContainerState represents the platform agnostic pieces relating to a -// running container's status and state -type ContainerState struct { - // Version is the OCI version for the container - Version string `json:"ociVersion"` - // ID is the container ID - ID string `json:"id"` - // InitProcessPid is the init process id in the parent namespace - InitProcessPid int `json:"pid"` - // Status is the current status of the container, running, paused, ... - Status string `json:"status"` - // Bundle is the path on the filesystem to the bundle - Bundle string `json:"bundle"` - // Rootfs is a path to a directory containing the container's root filesystem. - Rootfs string `json:"rootfs"` - // Created is the unix timestamp for the creation time of the container in UTC - Created time.Time `json:"created"` - // Annotations is the user defined annotations added to the config. - Annotations map[string]string `json:"annotations,omitempty"` - // The owner of the state directory (the owner of the container). - Owner string `json:"owner"` -} +package runhcs + +import ( + "bytes" + "errors" + "fmt" + "io" + "io/ioutil" + "os" + "syscall" + "time" + + "github.com/Microsoft/hcsshim/internal/guid" +) + +// ContainerState represents the platform agnostic pieces relating to a +// running container's status and state +type ContainerState struct { + // Version is the OCI version for the container + Version string `json:"ociVersion"` + // ID is the container ID + ID string `json:"id"` + // InitProcessPid is the init process id in the parent namespace + InitProcessPid int `json:"pid"` + // Status is the current status of the container, running, paused, ... + Status string `json:"status"` + // Bundle is the path on the filesystem to the bundle + Bundle string `json:"bundle"` + // Rootfs is a path to a directory containing the container's root filesystem. + Rootfs string `json:"rootfs"` + // Created is the unix timestamp for the creation time of the container in UTC + Created time.Time `json:"created"` + // Annotations is the user defined annotations added to the config. + Annotations map[string]string `json:"annotations,omitempty"` + // The owner of the state directory (the owner of the container). + Owner string `json:"owner"` +} + +// GetErrorFromPipe returns reads from `pipe` and verifies if the operation +// returned success or error. If error converts that to an error and returns. If +// `p` is not nill will issue a `Kill` and `Wait` for exit. +func GetErrorFromPipe(pipe io.Reader, p *os.Process) error { + serr, err := ioutil.ReadAll(pipe) + if err != nil { + return err + } + + if bytes.Equal(serr, ShimSuccess) { + return nil + } + + extra := "" + if p != nil { + p.Kill() + state, err := p.Wait() + if err != nil { + panic(err) + } + extra = fmt.Sprintf(", exit code %d", state.Sys().(syscall.WaitStatus).ExitCode) + } + if len(serr) == 0 { + return fmt.Errorf("unknown shim failure%s", extra) + } + + return errors.New(string(serr)) +} + +// VMPipePath returns the named pipe path for the vm shim. +func VMPipePath(hostUniqueID guid.GUID) string { + return SafePipePath("runhcs-vm-" + hostUniqueID.String()) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/runhcs/util.go b/vendor/github.com/Microsoft/hcsshim/internal/runhcs/util.go new file mode 100644 index 0000000000..dcbb1903b8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/runhcs/util.go @@ -0,0 +1,16 @@ +package runhcs + +import "net/url" + +const ( + SafePipePrefix = `\\.\pipe\ProtectedPrefix\Administrators\` +) + +// ShimSuccess is the byte stream returned on a successful operation. +var ShimSuccess = []byte{0, 'O', 'K', 0} + +func SafePipePath(name string) string { + // Use a pipe in the Administrators protected prefixed to prevent malicious + // squatting. + return SafePipePrefix + url.PathEscape(name) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/runhcs/util_test.go b/vendor/github.com/Microsoft/hcsshim/internal/runhcs/util_test.go new file mode 100644 index 0000000000..980d4be84b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/runhcs/util_test.go @@ -0,0 +1,17 @@ +package runhcs + +import ( + "testing" +) + +func Test_SafePipePath(t *testing.T) { + tests := []string{"test", "test with spaces", "test/with\\\\.\\slashes", "test.with..dots..."} + expected := []string{"test", "test%20with%20spaces", "test%2Fwith%5C%5C.%5Cslashes", "test.with..dots..."} + for i, test := range tests { + actual := SafePipePath(test) + e := SafePipePrefix + expected[i] + if actual != e { + t.Fatalf("SafePipePath: actual '%s' != '%s'", actual, expected[i]) + } + } +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/runhcs/vm.go b/vendor/github.com/Microsoft/hcsshim/internal/runhcs/vm.go new file mode 100644 index 0000000000..2c8957b88d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/runhcs/vm.go @@ -0,0 +1,43 @@ +package runhcs + +import ( + "encoding/json" + + "github.com/Microsoft/go-winio" +) + +// VMRequestOp is an operation that can be issued to a VM shim. +type VMRequestOp string + +const ( + // OpCreateContainer is a create container request. + OpCreateContainer VMRequestOp = "create" + // OpSyncNamespace is a `cni.NamespaceTypeGuest` sync request with the UVM. + OpSyncNamespace VMRequestOp = "sync" + // OpUnmountContainer is a container unmount request. + OpUnmountContainer VMRequestOp = "unmount" + // OpUnmountContainerDiskOnly is a container unmount disk request. + OpUnmountContainerDiskOnly VMRequestOp = "unmount-disk" +) + +// VMRequest is an operation request that is issued to a VM shim. +type VMRequest struct { + ID string + Op VMRequestOp +} + +// IssueVMRequest issues a request to a shim at the given pipe. +func IssueVMRequest(pipepath string, req *VMRequest) error { + pipe, err := winio.DialPipe(pipepath, nil) + if err != nil { + return err + } + defer pipe.Close() + if err := json.NewEncoder(pipe).Encode(req); err != nil { + return err + } + if err := GetErrorFromPipe(pipe, nil); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go b/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go index 6fa3bbc73d..995433ace6 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go @@ -3,6 +3,8 @@ package schema1 import ( "encoding/json" "time" + + "github.com/Microsoft/hcsshim/internal/schema2" ) // ProcessConfig is used as both the input of Container.CreateProcess @@ -115,9 +117,10 @@ type ComputeSystemQuery struct { type PropertyType string const ( - PropertyTypeStatistics PropertyType = "Statistics" - PropertyTypeProcessList = "ProcessList" - PropertyTypeMappedVirtualDisk = "MappedVirtualDisk" + PropertyTypeStatistics PropertyType = "Statistics" // V1 and V2 + PropertyTypeProcessList = "ProcessList" // V1 and V2 + PropertyTypeMappedVirtualDisk = "MappedVirtualDisk" // Not supported in V2 schema call + PropertyTypeGuestConnection = "GuestConnection" // V1 and V2. Nil return from HCS before RS5 ) type PropertyQuery struct { @@ -142,6 +145,7 @@ type ContainerProperties struct { Statistics Statistics `json:",omitempty"` ProcessList []ProcessListItem `json:",omitempty"` MappedVirtualDiskControllers map[int]MappedVirtualDiskController `json:",omitempty"` + GuestConnectionInfo GuestConnectionInfo `json:",omitempty"` } // MemoryStats holds the memory statistics for a container @@ -206,6 +210,19 @@ type MappedVirtualDiskController struct { MappedVirtualDisks map[int]MappedVirtualDisk `json:",omitempty"` } +// GuestDefinedCapabilities is part of the GuestConnectionInfo returned by a GuestConnection call on a utility VM +type GuestDefinedCapabilities struct { + NamespaceAddRequestSupported bool `json:",omitempty"` + SignalProcessSupported bool `json:",omitempty"` +} + +// GuestConnectionInfo is the structure of an iterm return by a GuestConnection call on a utility VM +type GuestConnectionInfo struct { + SupportedSchemaVersions []hcsschema.Version `json:",omitempty"` + ProtocolVersion uint32 `json:",omitempty"` + GuestDefinedCapabilities GuestDefinedCapabilities `json:",omitempty"` +} + // Type of Request Support in ModifySystem type RequestType string diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go index 3fb24e2505..ca75277a3f 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/chipset.go @@ -10,7 +10,6 @@ package hcsschema type Chipset struct { - Uefi *Uefi `json:"Uefi,omitempty"` IsNumLockDisabled bool `json:"IsNumLockDisabled,omitempty"` @@ -22,4 +21,7 @@ type Chipset struct { ChassisAssetTag string `json:"ChassisAssetTag,omitempty"` UseUtc bool `json:"UseUtc,omitempty"` + + // LinuxKernelDirect - Added in v2.2 Builds >=181117 + LinuxKernelDirect *LinuxKernelDirect `json:"LinuxKernelDirect,omitempty"` } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/linux_kernel_direct.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/linux_kernel_direct.go new file mode 100644 index 0000000000..0ab6c280fc --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/linux_kernel_direct.go @@ -0,0 +1,18 @@ +/* + * HCS API + * + * No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) + * + * API version: 2.2 + * Generated by: Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git) + */ + +package hcsschema + +type LinuxKernelDirect struct { + KernelFilePath string `json:"KernelFilePath,omitempty"` + + InitRdPath string `json:"InitRdPath,omitempty"` + + KernelCmdLine string `json:"KernelCmdLine,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go index fd766f41a3..27d0b8c483 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/memory_2.go @@ -10,7 +10,6 @@ package hcsschema type Memory2 struct { - SizeInMB int32 `json:"SizeInMB,omitempty"` AllowOvercommit bool `json:"AllowOvercommit,omitempty"` @@ -20,4 +19,7 @@ type Memory2 struct { EnableColdHint bool `json:"EnableColdHint,omitempty"` EnableEpf bool `json:"EnableEpf,omitempty"` + + // EnableDeferredCommit is private in the schema. If regenerated need to add back. + EnableDeferredCommit bool `json:"EnableDeferredCommit,omitempty"` } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go index 2ef1a47979..f5b7f3e38c 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema2/virtual_p_mem_controller.go @@ -10,12 +10,11 @@ package hcsschema type VirtualPMemController struct { - Devices map[string]VirtualPMemDevice `json:"Devices,omitempty"` - MaximumCount int32 `json:"MaximumCount,omitempty"` + MaximumCount uint32 `json:"MaximumCount,omitempty"` - MaximumSizeBytes int32 `json:"MaximumSizeBytes,omitempty"` + MaximumSizeBytes uint64 `json:"MaximumSizeBytes,omitempty"` Backing string `json:"Backing,omitempty"` } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schemaversion/schemaversion.go b/vendor/github.com/Microsoft/hcsshim/internal/schemaversion/schemaversion.go index c7da956181..8bade81a82 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/schemaversion/schemaversion.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/schemaversion/schemaversion.go @@ -6,8 +6,8 @@ import ( "encoding/json" "fmt" - "github.com/Microsoft/hcsshim/internal/osversion" "github.com/Microsoft/hcsshim/internal/schema2" + "github.com/Microsoft/hcsshim/osversion" "github.com/sirupsen/logrus" ) @@ -68,7 +68,7 @@ func String(sv *hcsschema.Version) string { func DetermineSchemaVersion(requestedSV *hcsschema.Version) *hcsschema.Version { sv := SchemaV10() if osversion.Get().Build >= osversion.RS5 { - sv = SchemaV10() // TODO: When do we flip this to V2 for RS5? Answer - when functionally complete. Templating. CredSpecs. Networking. LCOW... + sv = SchemaV21() } if requestedSV != nil { if err := IsSupported(requestedSV); err == nil { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schemaversion/schemaversion_test.go b/vendor/github.com/Microsoft/hcsshim/internal/schemaversion/schemaversion_test.go index 9d7223eff9..63c2ceb027 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/schemaversion/schemaversion_test.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/schemaversion/schemaversion_test.go @@ -1,19 +1,25 @@ package schemaversion import ( + "io/ioutil" "testing" _ "github.com/Microsoft/hcsshim/functional/manifest" - "github.com/Microsoft/hcsshim/internal/osversion" "github.com/Microsoft/hcsshim/internal/schema2" + "github.com/Microsoft/hcsshim/osversion" + "github.com/sirupsen/logrus" ) +func init() { + logrus.SetOutput(ioutil.Discard) +} + func TestDetermineSchemaVersion(t *testing.T) { osv := osversion.Get() if osv.Build >= osversion.RS5 { - if sv := DetermineSchemaVersion(nil); !IsV10(sv) { // TODO: Toggle this at some point so default is 2.0 - t.Fatalf("expected v1") + if sv := DetermineSchemaVersion(nil); !IsV21(sv) { + t.Fatalf("expected v2") } if sv := DetermineSchemaVersion(SchemaV21()); !IsV21(sv) { t.Fatalf("expected requested v2") @@ -21,8 +27,8 @@ func TestDetermineSchemaVersion(t *testing.T) { if sv := DetermineSchemaVersion(SchemaV10()); !IsV10(sv) { t.Fatalf("expected requested v1") } - if sv := DetermineSchemaVersion(&hcsschema.Version{}); !IsV10(sv) { // Logs a warning that 0.0 is ignored // TODO: Toggle this too - t.Fatalf("expected requested v1") + if sv := DetermineSchemaVersion(&hcsschema.Version{}); !IsV21(sv) { + t.Fatalf("expected requested v2") } if err := IsSupported(SchemaV21()); err != nil { @@ -37,13 +43,13 @@ func TestDetermineSchemaVersion(t *testing.T) { t.Fatalf("expected v1") } // Pre RS5 will downgrade to v1 even if request v2 - if sv := DetermineSchemaVersion(SchemaV21()); !IsV10(sv) { // Logs a warning that 2.0 is ignored. + if sv := DetermineSchemaVersion(SchemaV21()); !IsV10(sv) { t.Fatalf("expected requested v1") } if sv := DetermineSchemaVersion(SchemaV10()); !IsV10(sv) { t.Fatalf("expected requested v1") } - if sv := DetermineSchemaVersion(&hcsschema.Version{}); !IsV10(sv) { // Log a warning that 0.0 is ignored + if sv := DetermineSchemaVersion(&hcsschema.Version{}); !IsV10(sv) { t.Fatalf("expected requested v1") } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/constants.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/constants.go index c3593e3e15..cf0e668a67 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/constants.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/constants.go @@ -3,13 +3,17 @@ package uvm import "fmt" const ( - // MaxVPMEM is the maximum number of VPMem devices that may be added to an LCOW + // MaxVPMEMCount is the maximum number of VPMem devices that may be added to an LCOW // utility VM - MaxVPMEM = 128 + MaxVPMEMCount = 128 - // DefaultVPMEM is the default number of VPMem devices that may be added to an LCOW + // DefaultVPMEMCount is the default number of VPMem devices that may be added to an LCOW // utility VM if the create request doesn't specify how many. - DefaultVPMEM = 64 + DefaultVPMEMCount = 64 + + // DefaultVPMemSizeBytes is the default size of a VPMem device if the create request + // doesn't specify. + DefaultVPMemSizeBytes = 4 * 1024 * 1024 * 1024 // 4GB ) var errNotSupported = fmt.Errorf("not supported") diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/counter.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/counter.go index 0ce1858c6f..4339ef8dd0 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/counter.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/counter.go @@ -1,17 +1,11 @@ package uvm -import "sync" - -var ( - m sync.Mutex - counter uint64 +import ( + "sync/atomic" ) // ContainerCounter is used for where we layout things for a container in // a utility VM. For WCOW it'll be C:\c\N\. For LCOW it'll be /run/gcs/c/N/. func (uvm *UtilityVM) ContainerCounter() uint64 { - m.Lock() - defer m.Unlock() - counter++ - return counter + return atomic.AddUint64(&uvm.containerCounter, 1) } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/create.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/create.go index 180374fe52..dc7cb249c0 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/create.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/create.go @@ -1,430 +1,30 @@ package uvm import ( - "encoding/binary" - "fmt" - "net" - "os" - "path/filepath" "runtime" - "strings" - - "github.com/Microsoft/hcsshim/internal/guid" - "github.com/Microsoft/hcsshim/internal/hcs" - "github.com/Microsoft/hcsshim/internal/mergemaps" - "github.com/Microsoft/hcsshim/internal/schema2" - "github.com/Microsoft/hcsshim/internal/schemaversion" - "github.com/Microsoft/hcsshim/internal/uvmfolder" - "github.com/Microsoft/hcsshim/internal/wclayer" - "github.com/Microsoft/hcsshim/internal/wcow" - "github.com/linuxkit/virtsock/pkg/hvsock" - specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/sirupsen/logrus" ) -type PreferredRootFSType int - -const ( - PreferredRootFSTypeInitRd = 0 - PreferredRootFSTypeVHD = 1 +// Options are the set of options passed to Create() to create a utility vm. +type Options struct { + ID string // Identifier for the uvm. Defaults to generated GUID. + Owner string // Specifies the owner. Defaults to executable name. + AdditionHCSDocumentJSON string // Optional additional JSON to merge into the HCS document prior - initrdFile = "initrd.img" - vhdFile = "rootfs.vhd" -) + // MemorySizeInMB sets the UVM memory. If `0` will default to platform + // default. + MemorySizeInMB int32 -// UVMOptions are the set of options passed to Create() to create a utility vm. -type UVMOptions struct { - ID string // Identifier for the uvm. Defaults to generated GUID. - Owner string // Specifies the owner. Defaults to executable name. - OperatingSystem string // "windows" or "linux". - Resources *specs.WindowsResources // Optional resources for the utility VM. Supports Memory.limit and CPU.Count only currently. // TODO consider extending? - AdditionHCSDocumentJSON string // Optional additional JSON to merge into the HCS document prior + // Memory for UVM. Defaults to true. For physical backed memory, set to + // false. + AllowOvercommit *bool - // WCOW specific parameters - LayerFolders []string // Set of folders for base layers and scratch. Ordered from top most read-only through base read-only layer, followed by scratch + // Memory for UVM. Defaults to false. For virtual memory with deferred + // commit, set to true. + EnableDeferredCommit *bool - // LCOW specific parameters - BootFilesPath string // Folder in which kernel and root file system reside. Defaults to \Program Files\Linux Containers - KernelFile string // Filename under BootFilesPath for the kernel. Defaults to `kernel` - RootFSFile string // Filename under BootFilesPath for the UVMs root file system. Defaults are `initrd.img` or `rootfs.vhd`. - PreferredRootFSType *PreferredRootFSType // Controls searching for the RootFSFile. - KernelBootOptions string // Additional boot options for the kernel - EnableGraphicsConsole bool // If true, enable a graphics console for the utility VM - ConsolePipe string // The named pipe path to use for the serial console. eg \\.\pipe\vmpipe - VPMemDeviceCount *int32 // Number of VPMem devices. Limit at 128. If booting UVM from VHD, device 0 is taken. LCOW Only. - SCSIControllerCount *int // The number of SCSI controllers. Defaults to 1 if omitted. Currently we only support 0 or 1. -} - -const linuxLogVsockPort = 109 - -// Create creates an HCS compute system representing a utility VM. -// -// WCOW Notes: -// - If the scratch folder does not exist, it will be created -// - If the scratch folder does not contain `sandbox.vhdx` it will be created based on the system template located in the layer folders. -// - The scratch is always attached to SCSI 0:0 -// -func Create(opts *UVMOptions) (_ *UtilityVM, err error) { - logrus.Debugf("uvm::Create %+v", opts) - - if opts == nil { - return nil, fmt.Errorf("no options supplied to create") - } - - uvm := &UtilityVM{ - id: opts.ID, - owner: opts.Owner, - operatingSystem: opts.OperatingSystem, - } - - uvmFolder := "" // Windows - - if opts.OperatingSystem != "linux" && opts.OperatingSystem != "windows" { - logrus.Debugf("uvm::Create Unsupported OS") - return nil, fmt.Errorf("unsupported operating system %q", opts.OperatingSystem) - } - - // Defaults if omitted by caller. - // TODO: Change this. Don't auto generate ID if omitted. Avoids the chicken-and-egg problem - if uvm.id == "" { - uvm.id = guid.New().String() - } - if uvm.owner == "" { - uvm.owner = filepath.Base(os.Args[0]) - } - - attachments := make(map[string]hcsschema.Attachment) - scsi := make(map[string]hcsschema.Scsi) - uvm.scsiControllerCount = 1 - var actualRootFSType PreferredRootFSType = PreferredRootFSTypeInitRd // TODO Should we switch to VPMem/VHD as default? - - if uvm.operatingSystem == "windows" { - if len(opts.LayerFolders) < 2 { - return nil, fmt.Errorf("at least 2 LayerFolders must be supplied") - } - if opts.VPMemDeviceCount != nil { - return nil, fmt.Errorf("cannot specify VPMemDeviceCount for Windows utility VMs") - } - - var err error - uvmFolder, err = uvmfolder.LocateUVMFolder(opts.LayerFolders) - if err != nil { - return nil, fmt.Errorf("failed to locate utility VM folder from layer folders: %s", err) - } - - // TODO: BUGBUG Remove this. @jhowardmsft - // It should be the responsiblity of the caller to do the creation and population. - // - Update runhcs too (vm.go). - // - Remove comment in function header - // - Update tests that rely on this current behaviour. - // Create the RW scratch in the top-most layer folder, creating the folder if it doesn't already exist. - scratchFolder := opts.LayerFolders[len(opts.LayerFolders)-1] - logrus.Debugf("uvm::createWCOW scratch folder: %s", scratchFolder) - - // Create the directory if it doesn't exist - if _, err := os.Stat(scratchFolder); os.IsNotExist(err) { - logrus.Debugf("uvm::createWCOW Creating folder: %s ", scratchFolder) - if err := os.MkdirAll(scratchFolder, 0777); err != nil { - return nil, fmt.Errorf("failed to create utility VM scratch folder: %s", err) - } - } - - // Create sandbox.vhdx in the scratch folder based on the template, granting the correct permissions to it - if _, err := os.Stat(filepath.Join(scratchFolder, `sandbox.vhdx`)); os.IsNotExist(err) { - if err := wcow.CreateUVMScratch(uvmFolder, scratchFolder, uvm.id); err != nil { - return nil, fmt.Errorf("failed to create scratch: %s", err) - } - } - - // We attach the scratch to SCSI 0:0 - attachments["0"] = hcsschema.Attachment{ - Path: filepath.Join(scratchFolder, "sandbox.vhdx"), - Type_: "VirtualDisk", - } - scsi["0"] = hcsschema.Scsi{Attachments: attachments} - uvm.scsiLocations[0][0].hostPath = attachments["0"].Path - } else { - uvm.vpmemMax = DefaultVPMEM - if opts.VPMemDeviceCount != nil { - if *opts.VPMemDeviceCount > MaxVPMEM || *opts.VPMemDeviceCount < 0 { - return nil, fmt.Errorf("vpmem device count must between 0 and %d", MaxVPMEM) - } - uvm.vpmemMax = *opts.VPMemDeviceCount - logrus.Debugln("uvm::Create:: uvm.vpmemMax=", uvm.vpmemMax) - } - - scsi["0"] = hcsschema.Scsi{Attachments: attachments} - uvm.scsiControllerCount = 1 - if opts.SCSIControllerCount != nil { - if *opts.SCSIControllerCount < 0 || *opts.SCSIControllerCount > 1 { - return nil, fmt.Errorf("SCSI controller count must be 0 or 1") // Future extension here for up to 4 - } - uvm.scsiControllerCount = *opts.SCSIControllerCount - if uvm.scsiControllerCount == 0 { - scsi = nil - } - logrus.Debugln("uvm::Create:: uvm.scsiControllerCount=", uvm.scsiControllerCount) - } - if opts.BootFilesPath == "" { - opts.BootFilesPath = filepath.Join(os.Getenv("ProgramFiles"), "Linux Containers") - } - if opts.KernelFile == "" { - opts.KernelFile = "kernel" - } - if _, err := os.Stat(filepath.Join(opts.BootFilesPath, opts.KernelFile)); os.IsNotExist(err) { - return nil, fmt.Errorf("kernel '%s' not found", filepath.Join(opts.BootFilesPath, opts.KernelFile)) - } - - if opts.RootFSFile == "" { - if opts.PreferredRootFSType != nil { - actualRootFSType = *opts.PreferredRootFSType - if actualRootFSType != PreferredRootFSTypeInitRd && actualRootFSType != PreferredRootFSTypeVHD { - return nil, fmt.Errorf("invalid PreferredRootFSType") - } - } - - switch actualRootFSType { - case PreferredRootFSTypeInitRd: - if _, err := os.Stat(filepath.Join(opts.BootFilesPath, initrdFile)); os.IsNotExist(err) { - return nil, fmt.Errorf("initrd not found") - } - opts.RootFSFile = initrdFile - case PreferredRootFSTypeVHD: - if _, err := os.Stat(filepath.Join(opts.BootFilesPath, vhdFile)); os.IsNotExist(err) { - return nil, fmt.Errorf("rootfs.vhd not found") - } - opts.RootFSFile = vhdFile - } - } else { - // Determine the root FS type by the extension of the explicitly supplied RootFSFile - if _, err := os.Stat(filepath.Join(opts.BootFilesPath, opts.RootFSFile)); os.IsNotExist(err) { - return nil, fmt.Errorf("%s not found under %s", opts.RootFSFile, opts.BootFilesPath) - } - switch strings.ToLower(filepath.Ext(opts.RootFSFile)) { - case "vhd", "vhdx": - actualRootFSType = PreferredRootFSTypeVHD - case "img": - actualRootFSType = PreferredRootFSTypeInitRd - default: - return nil, fmt.Errorf("unsupported filename extension for RootFSFile") - } - } - } - - memory := int32(1024) - processors := int32(2) - if runtime.NumCPU() == 1 { - processors = 1 - } - if opts.Resources != nil { - if opts.Resources.Memory != nil && opts.Resources.Memory.Limit != nil { - memory = int32(*opts.Resources.Memory.Limit / 1024 / 1024) // OCI spec is in bytes. HCS takes MB - } - if opts.Resources.CPU != nil && opts.Resources.CPU.Count != nil { - processors = int32(*opts.Resources.CPU.Count) - } - } - - vm := &hcsschema.VirtualMachine{ - Chipset: &hcsschema.Chipset{ - Uefi: &hcsschema.Uefi{}, - }, - - ComputeTopology: &hcsschema.Topology{ - Memory: &hcsschema.Memory2{ - AllowOvercommit: true, - SizeInMB: memory, - }, - Processor: &hcsschema.Processor2{ - Count: processors, - }, - }, - - GuestConnection: &hcsschema.GuestConnection{}, - - Devices: &hcsschema.Devices{ - Scsi: scsi, - HvSocket: &hcsschema.HvSocket2{ - HvSocketConfig: &hcsschema.HvSocketSystemConfig{ - // Allow administrators and SYSTEM to bind to vsock sockets - // so that we can create a GCS log socket. - DefaultBindSecurityDescriptor: "D:P(A;;FA;;;SY)(A;;FA;;;BA)", - }, - }, - }, - } - - hcsDocument := &hcsschema.ComputeSystem{ - Owner: uvm.owner, - SchemaVersion: schemaversion.SchemaV21(), - VirtualMachine: vm, - } - - if uvm.operatingSystem == "windows" { - vm.Chipset.Uefi.BootThis = &hcsschema.UefiBootEntry{ - DevicePath: `\EFI\Microsoft\Boot\bootmgfw.efi`, - DeviceType: "VmbFs", - } - vm.Devices.VirtualSmb = &hcsschema.VirtualSmb{ - DirectFileMappingInMB: 1024, // Sensible default, but could be a tuning parameter somewhere - Shares: []hcsschema.VirtualSmbShare{ - { - Name: "os", - Path: filepath.Join(uvmFolder, `UtilityVM\Files`), - Options: &hcsschema.VirtualSmbShareOptions{ - ReadOnly: true, - PseudoOplocks: true, - TakeBackupPrivilege: true, - CacheIo: true, - ShareRead: true, - }, - }, - }, - } - } else { - vmDebugging := false - vm.GuestConnection.UseVsock = true - vm.GuestConnection.UseConnectedSuspend = true - vm.Devices.VirtualSmb = &hcsschema.VirtualSmb{ - Shares: []hcsschema.VirtualSmbShare{ - { - Name: "os", - Path: opts.BootFilesPath, - Options: &hcsschema.VirtualSmbShareOptions{ - ReadOnly: true, - TakeBackupPrivilege: true, - CacheIo: true, - ShareRead: true, - }, - }, - }, - } - - if uvm.vpmemMax > 0 { - vm.Devices.VirtualPMem = &hcsschema.VirtualPMemController{ - MaximumCount: uvm.vpmemMax, - } - } - - kernelArgs := "initrd=/" + opts.RootFSFile - if actualRootFSType == PreferredRootFSTypeVHD { - kernelArgs = "root=/dev/pmem0 init=/init" - } - - // Support for VPMem VHD(X) booting rather than initrd.. - if actualRootFSType == PreferredRootFSTypeVHD { - if uvm.vpmemMax == 0 { - return nil, fmt.Errorf("PreferredRootFSTypeVHD requess at least one VPMem device") - } - imageFormat := "Vhd1" - if strings.ToLower(filepath.Ext(opts.RootFSFile)) == "vhdx" { - imageFormat = "Vhdx" - } - vm.Devices.VirtualPMem.Devices = map[string]hcsschema.VirtualPMemDevice{ - "0": { - HostPath: filepath.Join(opts.BootFilesPath, opts.RootFSFile), - ReadOnly: true, - ImageFormat: imageFormat, - }, - } - if err := wclayer.GrantVmAccess(uvm.id, filepath.Join(opts.BootFilesPath, opts.RootFSFile)); err != nil { - return nil, fmt.Errorf("faied to grantvmaccess to %s: %s", filepath.Join(opts.BootFilesPath, opts.RootFSFile), err) - } - // Add to our internal structure - uvm.vpmemDevices[0] = vpmemInfo{ - hostPath: opts.RootFSFile, - uvmPath: "/", - refCount: 1, - } - } - - if opts.ConsolePipe != "" { - vmDebugging = true - kernelArgs += " console=ttyS0,115200" - vm.Devices.ComPorts = map[string]hcsschema.ComPort{ - "0": { // Which is actually COM1 - NamedPipe: opts.ConsolePipe, - }, - } - } - - if opts.EnableGraphicsConsole { - vmDebugging = true - kernelArgs += " console=tty" - vm.Devices.Keyboard = &hcsschema.Keyboard{} - vm.Devices.EnhancedModeVideo = &hcsschema.EnhancedModeVideo{} - vm.Devices.VideoMonitor = &hcsschema.VideoMonitor{} - } - - if !vmDebugging { - // Terminate the VM if there is a kernel panic. - kernelArgs += " panic=-1" - } - - if opts.KernelBootOptions != "" { - kernelArgs += " " + opts.KernelBootOptions - } - - // Start GCS with stderr pointing to the vsock port created below in - // order to forward guest logs to logrus. - initArgs := fmt.Sprintf("/bin/vsockexec -e %d /bin/gcs -log-format json -loglevel %s", - linuxLogVsockPort, - logrus.StandardLogger().Level.String()) - - if vmDebugging { - // Launch a shell on the console. - initArgs = `sh -c "` + initArgs + ` & exec sh"` - } - - kernelArgs += ` -- ` + initArgs - vm.Chipset.Uefi.BootThis = &hcsschema.UefiBootEntry{ - DevicePath: `\` + opts.KernelFile, - DeviceType: "VmbFs", - OptionalData: kernelArgs, - } - } - - fullDoc, err := mergemaps.MergeJSON(hcsDocument, ([]byte)(opts.AdditionHCSDocumentJSON)) - if err != nil { - return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", opts.AdditionHCSDocumentJSON, err) - } - - hcsSystem, err := hcs.CreateComputeSystem(uvm.id, fullDoc) - if err != nil { - logrus.Debugln("failed to create UVM: ", err) - return nil, err - } - - uvm.hcsSystem = hcsSystem - defer func() { - if err != nil { - uvm.Close() - } - }() - - if uvm.operatingSystem == "linux" { - // Create a socket that the GCS can send logrus log data to. - uvm.gcslog, err = uvm.listenVsock(linuxLogVsockPort) - if err != nil { - return nil, err - } - } - - return uvm, nil -} - -func (uvm *UtilityVM) listenVsock(port uint32) (net.Listener, error) { - properties, err := uvm.hcsSystem.Properties() - if err != nil { - return nil, err - } - vmID, err := hvsock.GUIDFromString(properties.RuntimeID) - if err != nil { - return nil, err - } - serviceID, _ := hvsock.GUIDFromString("00000000-facb-11e6-bd58-64006a7986d3") - binary.LittleEndian.PutUint32(serviceID[0:4], port) - return hvsock.Listen(hvsock.Addr{VMID: vmID, ServiceID: serviceID}) + // ProcessorCount sets the number of vCPU's. If `0` will default to platform + // default. + ProcessorCount int32 } // ID returns the ID of the VM's compute system. @@ -440,11 +40,33 @@ func (uvm *UtilityVM) OS() string { // Close terminates and releases resources associated with the utility VM. func (uvm *UtilityVM) Close() error { uvm.Terminate() - if uvm.gcslog != nil { - uvm.gcslog.Close() - uvm.gcslog = nil + + // outputListener will only be nil for a Create -> Stop without a Start. In + // this case we have no goroutine processing output so its safe to close the + // channel here. + if uvm.outputListener != nil { + close(uvm.outputProcessingDone) + uvm.outputListener.Close() + uvm.outputListener = nil } err := uvm.hcsSystem.Close() uvm.hcsSystem = nil return err } + +func normalizeMemory(m int32) int32 { + if m == 0 { + return 1024 // 1GB By Default. + } + return m +} + +func normalizeProcessors(p int32) int32 { + if p == 0 { + if runtime.NumCPU() == 1 { + return 1 + } + return 2 + } + return p +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/create_lcow.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/create_lcow.go new file mode 100644 index 0000000000..3869c1e972 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/create_lcow.go @@ -0,0 +1,389 @@ +package uvm + +import ( + "encoding/binary" + "fmt" + "io" + "net" + "os" + "path/filepath" + "strings" + + "github.com/Microsoft/hcsshim/internal/guid" + "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/mergemaps" + "github.com/Microsoft/hcsshim/internal/schema2" + "github.com/Microsoft/hcsshim/internal/schemaversion" + "github.com/Microsoft/hcsshim/internal/wclayer" + "github.com/Microsoft/hcsshim/osversion" + "github.com/linuxkit/virtsock/pkg/hvsock" + "github.com/sirupsen/logrus" +) + +type PreferredRootFSType int + +const ( + PreferredRootFSTypeInitRd PreferredRootFSType = iota + PreferredRootFSTypeVHD +) + +// OutputHandler is used to process the output from the program run in the UVM. +type OutputHandler func(io.Reader) + +const ( + initrdFile = "initrd.img" + vhdFile = "rootfs.vhd" +) + +// OptionsLCOW are the set of options passed to CreateLCOW() to create a utility vm. +type OptionsLCOW struct { + *Options + + BootFilesPath string // Folder in which kernel and root file system reside. Defaults to \Program Files\Linux Containers + KernelFile string // Filename under BootFilesPath for the kernel. Defaults to `kernel` + KernelDirect bool // Skip UEFI and boot directly to `kernel` + RootFSFile string // Filename under BootFilesPath for the UVMs root file system. Defaults are `initrd.img` or `rootfs.vhd` based on `PreferredRootFSType`. + KernelBootOptions string // Additional boot options for the kernel + EnableGraphicsConsole bool // If true, enable a graphics console for the utility VM + ConsolePipe string // The named pipe path to use for the serial console. eg \\.\pipe\vmpipe + SCSIControllerCount *uint32 // The number of SCSI controllers. Defaults to 1 if omitted. Currently we only support 0 or 1. + UseGuestConnection *bool // Whether the HCS should connect to the UVM's GCS. Defaults to true + ExecCommandLine string // The command line to exec from init. Defaults to GCS + ForwardStdout *bool // Whether stdout will be forwarded from the executed program. Defaults to false + ForwardStderr *bool // Whether stderr will be forwarded from the executed program. Defaults to true + OutputHandler *OutputHandler // Controls how output received over HVSocket from the UVM is handled. Defaults to parsing output as logrus messages + + // Number of VPMem devices. Limit at 128. If booting UVM from VHD, device 0 is taken. LCOW Only. io.microsoft.virtualmachine.devices.virtualpmem.maximumcount + VPMemDeviceCount *uint32 + + // Size of the VPMem devices. LCOW Only. Defaults to 4GB. io.microsoft.virtualmachine.devices.virtualpmem.maximumsizebytes + VPMemSizeBytes *uint64 + + // Controls searching for the RootFSFile. Defaults to initrd (0). Can be set to VHD (1). io.microsoft.virtualmachine.lcow.preferredrootfstype + // Note this uses an arbitrary annotation strict which has no direct mapping to the HCS schema. + PreferredRootFSType *PreferredRootFSType +} + +const linuxLogVsockPort = 109 + +// CreateLCOW creates an HCS compute system representing a utility VM. +func CreateLCOW(opts *OptionsLCOW) (_ *UtilityVM, err error) { + logrus.Debugf("uvm::CreateLCOW %+v", opts) + + if opts.Options == nil { + opts.Options = &Options{} + } + + uvm := &UtilityVM{ + id: opts.ID, + owner: opts.Owner, + operatingSystem: "linux", + scsiControllerCount: 1, + vpmemMaxCount: DefaultVPMEMCount, + vpmemMaxSizeBytes: DefaultVPMemSizeBytes, + } + + // Defaults if omitted by caller. + // TODO: Change this. Don't auto generate ID if omitted. Avoids the chicken-and-egg problem + if uvm.id == "" { + uvm.id = guid.New().String() + } + if uvm.owner == "" { + uvm.owner = filepath.Base(os.Args[0]) + } + if opts.UseGuestConnection == nil { + val := true + opts.UseGuestConnection = &val + } + + if opts.BootFilesPath == "" { + opts.BootFilesPath = filepath.Join(os.Getenv("ProgramFiles"), "Linux Containers") + } + if opts.KernelFile == "" { + opts.KernelFile = "kernel" + } + if _, err := os.Stat(filepath.Join(opts.BootFilesPath, opts.KernelFile)); os.IsNotExist(err) { + return nil, fmt.Errorf("kernel '%s' not found", filepath.Join(opts.BootFilesPath, opts.KernelFile)) + } + if opts.PreferredRootFSType == nil { + v := PreferredRootFSTypeInitRd + opts.PreferredRootFSType = &v + } + if opts.RootFSFile == "" { + switch *opts.PreferredRootFSType { + case PreferredRootFSTypeInitRd: + opts.RootFSFile = initrdFile + case PreferredRootFSTypeVHD: + opts.RootFSFile = "rootfs.vhd" + } + } + if opts.ForwardStdout == nil { + val := false + opts.ForwardStdout = &val + } + if opts.ForwardStderr == nil { + val := true + opts.ForwardStderr = &val + } + if opts.OutputHandler == nil { + val := OutputHandler(parseLogrus) + opts.OutputHandler = &val + } + + if _, err := os.Stat(filepath.Join(opts.BootFilesPath, opts.RootFSFile)); os.IsNotExist(err) { + return nil, fmt.Errorf("%s not found under %s", opts.RootFSFile, opts.BootFilesPath) + } + + if opts.SCSIControllerCount != nil { + if *opts.SCSIControllerCount > 1 { + return nil, fmt.Errorf("SCSI controller count must be 0 or 1") // Future extension here for up to 4 + } + uvm.scsiControllerCount = *opts.SCSIControllerCount + } + if opts.VPMemDeviceCount != nil { + if *opts.VPMemDeviceCount > MaxVPMEMCount { + return nil, fmt.Errorf("vpmem device count cannot be greater than %d", MaxVPMEMCount) + } + uvm.vpmemMaxCount = *opts.VPMemDeviceCount + } + if uvm.vpmemMaxCount > 0 { + if opts.VPMemSizeBytes != nil { + if *opts.VPMemSizeBytes%4096 != 0 { + return nil, fmt.Errorf("opts.VPMemSizeBytes must be a multiple of 4096") + } + uvm.vpmemMaxSizeBytes = *opts.VPMemSizeBytes + } + } else { + if *opts.PreferredRootFSType == PreferredRootFSTypeVHD { + return nil, fmt.Errorf("PreferredRootFSTypeVHD requires at least one VPMem device") + } + } + if opts.KernelDirect && osversion.Get().Build < 18286 { + return nil, fmt.Errorf("KernelDirectBoot is not support on builds older than 18286") + } + + doc := &hcsschema.ComputeSystem{ + Owner: uvm.owner, + SchemaVersion: schemaversion.SchemaV21(), + ShouldTerminateOnLastHandleClosed: true, + VirtualMachine: &hcsschema.VirtualMachine{ + Chipset: &hcsschema.Chipset{}, + ComputeTopology: &hcsschema.Topology{ + Memory: &hcsschema.Memory2{ + SizeInMB: normalizeMemory(opts.MemorySizeInMB), + // AllowOvercommit `true` by default if not passed. + AllowOvercommit: opts.AllowOvercommit == nil || *opts.AllowOvercommit, + // EnableDeferredCommit `false` by default if not passed. + EnableDeferredCommit: opts.EnableDeferredCommit != nil && *opts.EnableDeferredCommit, + }, + Processor: &hcsschema.Processor2{ + Count: normalizeProcessors(opts.ProcessorCount), + }, + }, + Devices: &hcsschema.Devices{ + HvSocket: &hcsschema.HvSocket2{ + HvSocketConfig: &hcsschema.HvSocketSystemConfig{ + // Allow administrators and SYSTEM to bind to vsock sockets + // so that we can create a GCS log socket. + DefaultBindSecurityDescriptor: "D:P(A;;FA;;;SY)(A;;FA;;;BA)", + }, + }, + }, + }, + } + + if *opts.UseGuestConnection { + doc.VirtualMachine.GuestConnection = &hcsschema.GuestConnection{ + UseVsock: true, + UseConnectedSuspend: true, + } + } + + if !opts.KernelDirect { + doc.VirtualMachine.Devices.VirtualSmb = &hcsschema.VirtualSmb{ + Shares: []hcsschema.VirtualSmbShare{ + { + Name: "os", + Path: opts.BootFilesPath, + Options: &hcsschema.VirtualSmbShareOptions{ + ReadOnly: true, + TakeBackupPrivilege: true, + CacheIo: true, + ShareRead: true, + }, + }, + }, + } + } + + if uvm.scsiControllerCount > 0 { + // TODO: JTERRY75 - this should enumerate scsicount and add an entry per value. + doc.VirtualMachine.Devices.Scsi = map[string]hcsschema.Scsi{ + "0": { + Attachments: make(map[string]hcsschema.Attachment), + }, + } + } + if uvm.vpmemMaxCount > 0 { + doc.VirtualMachine.Devices.VirtualPMem = &hcsschema.VirtualPMemController{ + MaximumCount: uvm.vpmemMaxCount, + MaximumSizeBytes: uvm.vpmemMaxSizeBytes, + } + } + + var kernelArgs string + switch *opts.PreferredRootFSType { + case PreferredRootFSTypeInitRd: + if !opts.KernelDirect { + kernelArgs = "initrd=/" + opts.RootFSFile + } + case PreferredRootFSTypeVHD: + // Support for VPMem VHD(X) booting rather than initrd.. + kernelArgs = "root=/dev/pmem0 ro init=/init" + imageFormat := "Vhd1" + if strings.ToLower(filepath.Ext(opts.RootFSFile)) == "vhdx" { + imageFormat = "Vhdx" + } + doc.VirtualMachine.Devices.VirtualPMem.Devices = map[string]hcsschema.VirtualPMemDevice{ + "0": { + HostPath: filepath.Join(opts.BootFilesPath, opts.RootFSFile), + ReadOnly: true, + ImageFormat: imageFormat, + }, + } + if err := wclayer.GrantVmAccess(uvm.id, filepath.Join(opts.BootFilesPath, opts.RootFSFile)); err != nil { + return nil, fmt.Errorf("failed to grantvmaccess to %s: %s", filepath.Join(opts.BootFilesPath, opts.RootFSFile), err) + } + // Add to our internal structure + uvm.vpmemDevices[0] = vpmemInfo{ + hostPath: opts.RootFSFile, + uvmPath: "/", + refCount: 1, + } + } + + vmDebugging := false + if opts.ConsolePipe != "" { + vmDebugging = true + kernelArgs += " 8250_core.nr_uarts=1 8250_core.skip_txen_test=1 console=ttyS0,115200" + doc.VirtualMachine.Devices.ComPorts = map[string]hcsschema.ComPort{ + "0": { // Which is actually COM1 + NamedPipe: opts.ConsolePipe, + }, + } + } else { + kernelArgs += " 8250_core.nr_uarts=0" + } + + if opts.EnableGraphicsConsole { + vmDebugging = true + kernelArgs += " console=tty" + doc.VirtualMachine.Devices.Keyboard = &hcsschema.Keyboard{} + doc.VirtualMachine.Devices.EnhancedModeVideo = &hcsschema.EnhancedModeVideo{} + doc.VirtualMachine.Devices.VideoMonitor = &hcsschema.VideoMonitor{} + } + + if !vmDebugging { + // Terminate the VM if there is a kernel panic. + kernelArgs += " panic=-1 quiet" + } + + if opts.KernelBootOptions != "" { + kernelArgs += " " + opts.KernelBootOptions + } + + // With default options, run GCS with stderr pointing to the vsock port + // created below in order to forward guest logs to logrus. + initArgs := "/bin/vsockexec" + + if *opts.ForwardStdout { + initArgs += fmt.Sprintf(" -o %d", linuxLogVsockPort) + } + + if *opts.ForwardStderr { + initArgs += fmt.Sprintf(" -e %d", linuxLogVsockPort) + } + + initArgs += " " + if opts.ExecCommandLine != "" { + initArgs += opts.ExecCommandLine + } else { + // Default to running GCS when another command isn't specified. + initArgs += fmt.Sprintf("/bin/gcs -log-format json -loglevel %s", logrus.StandardLogger().Level.String()) + } + + if vmDebugging { + // Launch a shell on the console. + initArgs = `sh -c "` + initArgs + ` & exec sh"` + } + + kernelArgs += ` pci=off brd.rd_nr=0 pmtmr=0 -- ` + initArgs + + if !opts.KernelDirect { + doc.VirtualMachine.Chipset.Uefi = &hcsschema.Uefi{ + BootThis: &hcsschema.UefiBootEntry{ + DevicePath: `\` + opts.KernelFile, + DeviceType: "VmbFs", + OptionalData: kernelArgs, + }, + } + } else { + doc.VirtualMachine.Chipset.LinuxKernelDirect = &hcsschema.LinuxKernelDirect{ + KernelFilePath: filepath.Join(opts.BootFilesPath, opts.KernelFile), + KernelCmdLine: kernelArgs, + } + if *opts.PreferredRootFSType == PreferredRootFSTypeInitRd { + doc.VirtualMachine.Chipset.LinuxKernelDirect.InitRdPath = filepath.Join(opts.BootFilesPath, opts.RootFSFile) + } + } + + fullDoc, err := mergemaps.MergeJSON(doc, ([]byte)(opts.AdditionHCSDocumentJSON)) + if err != nil { + return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", opts.AdditionHCSDocumentJSON, err) + } + + hcsSystem, err := hcs.CreateComputeSystem(uvm.id, fullDoc) + if err != nil { + logrus.Debugln("failed to create UVM: ", err) + return nil, err + } + + uvm.hcsSystem = hcsSystem + defer func() { + if err != nil { + uvm.Close() + } + }() + + // Create a socket that the executed program can send to. This is usually + // used by GCS to send log data. + if *opts.ForwardStdout || *opts.ForwardStderr { + uvm.outputHandler = *opts.OutputHandler + uvm.outputProcessingDone = make(chan struct{}) + uvm.outputListener, err = uvm.listenVsock(linuxLogVsockPort) + if err != nil { + return nil, err + } + } + + return uvm, nil +} + +func (uvm *UtilityVM) listenVsock(port uint32) (net.Listener, error) { + properties, err := uvm.hcsSystem.Properties() + if err != nil { + return nil, err + } + vmID, err := hvsock.GUIDFromString(properties.RuntimeID) + if err != nil { + return nil, err + } + serviceID, _ := hvsock.GUIDFromString("00000000-facb-11e6-bd58-64006a7986d3") + binary.LittleEndian.PutUint32(serviceID[0:4], port) + return hvsock.Listen(hvsock.Addr{VMID: vmID, ServiceID: serviceID}) +} + +// PMemMaxSizeBytes returns the maximum size of a PMEM layer (LCOW) +func (uvm *UtilityVM) PMemMaxSizeBytes() uint64 { + return uvm.vpmemMaxSizeBytes +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/create_test.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/create_test.go index ed0eeaf01d..14402bdcb2 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/create_test.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/create_test.go @@ -6,32 +6,19 @@ import ( // Unit tests for negative testing of input to uvm.Create() -func TestCreateBadOS(t *testing.T) { - opts := &UVMOptions{ - OperatingSystem: "foobar", - } - _, err := Create(opts) - if err == nil || (err != nil && err.Error() != `unsupported operating system "foobar"`) { - t.Fatal(err) - } -} - func TestCreateBadBootFilesPath(t *testing.T) { - opts := &UVMOptions{ - OperatingSystem: "linux", - BootFilesPath: `c:\does\not\exist\I\hope`, + opts := &OptionsLCOW{ + BootFilesPath: `c:\does\not\exist\I\hope`, } - _, err := Create(opts) + _, err := CreateLCOW(opts) if err == nil || (err != nil && err.Error() != `kernel 'c:\does\not\exist\I\hope\kernel' not found`) { t.Fatal(err) } } func TestCreateWCOWBadLayerFolders(t *testing.T) { - opts := &UVMOptions{ - OperatingSystem: "windows", - } - _, err := Create(opts) + opts := &OptionsWCOW{} + _, err := CreateWCOW(opts) if err == nil || (err != nil && err.Error() != `at least 2 LayerFolders must be supplied`) { t.Fatal(err) } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/create_wcow.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/create_wcow.go new file mode 100644 index 0000000000..4b75c7051c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/create_wcow.go @@ -0,0 +1,167 @@ +package uvm + +import ( + "fmt" + "os" + "path/filepath" + + "github.com/Microsoft/hcsshim/internal/guid" + "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/mergemaps" + "github.com/Microsoft/hcsshim/internal/schema2" + "github.com/Microsoft/hcsshim/internal/schemaversion" + "github.com/Microsoft/hcsshim/internal/uvmfolder" + "github.com/Microsoft/hcsshim/internal/wcow" + "github.com/sirupsen/logrus" +) + +// OptionsWCOW are the set of options passed to CreateWCOW() to create a utility vm. +type OptionsWCOW struct { + *Options + + LayerFolders []string // Set of folders for base layers and scratch. Ordered from top most read-only through base read-only layer, followed by scratch +} + +// CreateWCOW creates an HCS compute system representing a utility VM. +// +// WCOW Notes: +// - The scratch is always attached to SCSI 0:0 +// +func CreateWCOW(opts *OptionsWCOW) (_ *UtilityVM, err error) { + logrus.Debugf("uvm::CreateWCOW %+v", opts) + + if opts.Options == nil { + opts.Options = &Options{} + } + + uvm := &UtilityVM{ + id: opts.ID, + owner: opts.Owner, + operatingSystem: "windows", + scsiControllerCount: 1, + vsmbShares: make(map[string]*vsmbShare), + } + + // Defaults if omitted by caller. + // TODO: Change this. Don't auto generate ID if omitted. Avoids the chicken-and-egg problem + if uvm.id == "" { + uvm.id = guid.New().String() + } + if uvm.owner == "" { + uvm.owner = filepath.Base(os.Args[0]) + } + + if len(opts.LayerFolders) < 2 { + return nil, fmt.Errorf("at least 2 LayerFolders must be supplied") + } + uvmFolder, err := uvmfolder.LocateUVMFolder(opts.LayerFolders) + if err != nil { + return nil, fmt.Errorf("failed to locate utility VM folder from layer folders: %s", err) + } + + // TODO: BUGBUG Remove this. @jhowardmsft + // It should be the responsiblity of the caller to do the creation and population. + // - Update runhcs too (vm.go). + // - Remove comment in function header + // - Update tests that rely on this current behaviour. + // Create the RW scratch in the top-most layer folder, creating the folder if it doesn't already exist. + scratchFolder := opts.LayerFolders[len(opts.LayerFolders)-1] + logrus.Debugf("uvm::CreateWCOW scratch folder: %s", scratchFolder) + + // Create the directory if it doesn't exist + if _, err := os.Stat(scratchFolder); os.IsNotExist(err) { + logrus.Debugf("uvm::CreateWCOW creating folder: %s ", scratchFolder) + if err := os.MkdirAll(scratchFolder, 0777); err != nil { + return nil, fmt.Errorf("failed to create utility VM scratch folder: %s", err) + } + } + + // Create sandbox.vhdx in the scratch folder based on the template, granting the correct permissions to it + scratchPath := filepath.Join(scratchFolder, "sandbox.vhdx") + if _, err := os.Stat(scratchPath); os.IsNotExist(err) { + if err := wcow.CreateUVMScratch(uvmFolder, scratchFolder, uvm.id); err != nil { + return nil, fmt.Errorf("failed to create scratch: %s", err) + } + } + + doc := &hcsschema.ComputeSystem{ + Owner: uvm.owner, + SchemaVersion: schemaversion.SchemaV21(), + ShouldTerminateOnLastHandleClosed: true, + VirtualMachine: &hcsschema.VirtualMachine{ + Chipset: &hcsschema.Chipset{ + Uefi: &hcsschema.Uefi{ + BootThis: &hcsschema.UefiBootEntry{ + DevicePath: `\EFI\Microsoft\Boot\bootmgfw.efi`, + DeviceType: "VmbFs", + }, + }, + }, + ComputeTopology: &hcsschema.Topology{ + Memory: &hcsschema.Memory2{ + SizeInMB: normalizeMemory(opts.MemorySizeInMB), + // AllowOvercommit `true` by default if not passed. + AllowOvercommit: opts.AllowOvercommit == nil || *opts.AllowOvercommit, + // EnableHotHint is not compatible with physical. Only virtual, and only Windows. + EnableHotHint: opts.AllowOvercommit == nil || *opts.AllowOvercommit, + // EnableDeferredCommit `false` by default if not passed. + EnableDeferredCommit: opts.EnableDeferredCommit != nil && *opts.EnableDeferredCommit, + }, + Processor: &hcsschema.Processor2{ + Count: normalizeProcessors(opts.ProcessorCount), + }, + }, + GuestConnection: &hcsschema.GuestConnection{}, + Devices: &hcsschema.Devices{ + Scsi: map[string]hcsschema.Scsi{ + "0": { + Attachments: map[string]hcsschema.Attachment{ + "0": { + Path: scratchPath, + Type_: "VirtualDisk", + }, + }, + }, + }, + HvSocket: &hcsschema.HvSocket2{ + HvSocketConfig: &hcsschema.HvSocketSystemConfig{ + // Allow administrators and SYSTEM to bind to vsock sockets + // so that we can create a GCS log socket. + DefaultBindSecurityDescriptor: "D:P(A;;FA;;;SY)(A;;FA;;;BA)", + }, + }, + VirtualSmb: &hcsschema.VirtualSmb{ + DirectFileMappingInMB: 1024, // Sensible default, but could be a tuning parameter somewhere + Shares: []hcsschema.VirtualSmbShare{ + { + Name: "os", + Path: filepath.Join(uvmFolder, `UtilityVM\Files`), + Options: &hcsschema.VirtualSmbShareOptions{ + ReadOnly: true, + PseudoOplocks: true, + TakeBackupPrivilege: true, + CacheIo: true, + ShareRead: true, + }, + }, + }, + }, + }, + }, + } + + uvm.scsiLocations[0][0].hostPath = doc.VirtualMachine.Devices.Scsi["0"].Attachments["0"].Path + + fullDoc, err := mergemaps.MergeJSON(doc, ([]byte)(opts.AdditionHCSDocumentJSON)) + if err != nil { + return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", opts.AdditionHCSDocumentJSON, err) + } + + hcsSystem, err := hcs.CreateComputeSystem(uvm.id, fullDoc) + if err != nil { + logrus.Debugln("failed to create UVM: ", err) + return nil, err + } + uvm.hcsSystem = hcsSystem + return uvm, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/network.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/network.go index 868a82b8a6..6fdde43cc3 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/network.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/network.go @@ -4,20 +4,45 @@ import ( "fmt" "path" + "github.com/Microsoft/hcsshim/hcn" "github.com/Microsoft/hcsshim/internal/guestrequest" "github.com/Microsoft/hcsshim/internal/guid" "github.com/Microsoft/hcsshim/internal/hns" "github.com/Microsoft/hcsshim/internal/requesttype" + "github.com/Microsoft/hcsshim/internal/schema1" "github.com/Microsoft/hcsshim/internal/schema2" + "github.com/Microsoft/hcsshim/osversion" "github.com/sirupsen/logrus" ) +// AddNetNS adds network namespace inside the guest & adds endpoints to the guest on that namepace func (uvm *UtilityVM) AddNetNS(id string, endpoints []*hns.HNSEndpoint) (err error) { uvm.m.Lock() defer uvm.m.Unlock() ns := uvm.namespaces[id] if ns == nil { ns = &namespaceInfo{} + + if uvm.isNetworkNamespaceSupported() { + // Add a Guest Network namespace. Remove windows check when LCOW supports it + if uvm.operatingSystem == "windows" { + hcnNamespace, err := hcn.GetNamespaceByID(id) + if err != nil { + return err + } + guestNamespace := hcsschema.ModifySettingRequest{ + GuestRequest: guestrequest.GuestRequest{ + ResourceType: guestrequest.ResourceTypeNetworkNamespace, + RequestType: requesttype.Add, + Settings: hcnNamespace, + }, + } + if err := uvm.Modify(&guestNamespace); err != nil { + return err + } + } + } + defer func() { if err != nil { if e := uvm.removeNamespaceNICs(ns); e != nil { @@ -42,6 +67,7 @@ func (uvm *UtilityVM) AddNetNS(id string, endpoints []*hns.HNSEndpoint) (err err return nil } +//RemoveNetNS removes the namespace information func (uvm *UtilityVM) RemoveNetNS(id string) error { uvm.m.Lock() defer uvm.m.Unlock() @@ -49,15 +75,48 @@ func (uvm *UtilityVM) RemoveNetNS(id string) error { if ns == nil || ns.refCount <= 0 { panic(fmt.Errorf("removed a namespace that was not added: %s", id)) } + ns.refCount-- + + // Remove the Guest Network namespace + if uvm.isNetworkNamespaceSupported() { + if uvm.operatingSystem == "windows" { + hcnNamespace, err := hcn.GetNamespaceByID(id) + if err != nil { + return err + } + guestNamespace := hcsschema.ModifySettingRequest{ + GuestRequest: guestrequest.GuestRequest{ + ResourceType: guestrequest.ResourceTypeNetworkNamespace, + RequestType: requesttype.Remove, + Settings: hcnNamespace, + }, + } + if err := uvm.Modify(&guestNamespace); err != nil { + return err + } + } + } + var err error if ns.refCount == 0 { err = uvm.removeNamespaceNICs(ns) delete(uvm.namespaces, id) } + return err } +// IsNetworkNamespaceSupported returns bool value specifying if network namespace is supported inside the guest +func (uvm *UtilityVM) isNetworkNamespaceSupported() bool { + p, err := uvm.ComputeSystem().Properties(schema1.PropertyTypeGuestConnection) + if err == nil { + return p.GuestConnectionInfo.GuestDefinedCapabilities.NamespaceAddRequestSupported + } + + return false +} + func (uvm *UtilityVM) removeNamespaceNICs(ns *namespaceInfo) error { for len(ns.nics) != 0 { nic := ns.nics[len(ns.nics)-1] @@ -70,6 +129,21 @@ func (uvm *UtilityVM) removeNamespaceNICs(ns *namespaceInfo) error { return nil } +func getNetworkModifyRequest(adapterID string, requestType string, settings interface{}) interface{} { + if osversion.Get().Build >= osversion.RS5 { + return guestrequest.NetworkModifyRequest{ + AdapterId: adapterID, + RequestType: requestType, + Settings: settings, + } + } + return guestrequest.RS4NetworkModifyRequest{ + AdapterInstanceId: adapterID, + RequestType: requestType, + Settings: settings, + } +} + func (uvm *UtilityVM) addNIC(id guid.GUID, endpoint *hns.HNSEndpoint) error { // First a pre-add. This is a guest-only request and is only done on Windows. @@ -78,11 +152,10 @@ func (uvm *UtilityVM) addNIC(id guid.GUID, endpoint *hns.HNSEndpoint) error { GuestRequest: guestrequest.GuestRequest{ ResourceType: guestrequest.ResourceTypeNetwork, RequestType: requesttype.Add, - Settings: guestrequest.NetworkModifyRequest{ - AdapterInstanceId: id.String(), - RequestType: requesttype.PreAdd, - Settings: endpoint, - }, + Settings: getNetworkModifyRequest( + id.String(), + requesttype.PreAdd, + endpoint), }, } if err := uvm.Modify(&preAddRequest); err != nil { @@ -104,17 +177,18 @@ func (uvm *UtilityVM) addNIC(id guid.GUID, endpoint *hns.HNSEndpoint) error { request.GuestRequest = guestrequest.GuestRequest{ ResourceType: guestrequest.ResourceTypeNetwork, RequestType: requesttype.Add, - Settings: guestrequest.NetworkModifyRequest{ - AdapterInstanceId: id.String(), - RequestType: requesttype.Add, - }, - } - } else { - request.GuestRequest = guestrequest.GuestRequest{ - ResourceType: guestrequest.ResourceTypeNetwork, - RequestType: requesttype.Add, - Settings: endpoint, + Settings: getNetworkModifyRequest( + id.String(), + requesttype.Add, + nil), } + // Uncomment this once we have GuestRequest support for Linux + //} else { + // request.GuestRequest = guestrequest.GuestRequest{ + // ResourceType: guestrequest.ResourceTypeNetwork, + // RequestType: requesttype.Add, + // Settings: endpoint, + // } } if err := uvm.Modify(&request); err != nil { @@ -137,10 +211,10 @@ func (uvm *UtilityVM) removeNIC(id guid.GUID, endpoint *hns.HNSEndpoint) error { if uvm.operatingSystem == "windows" { request.GuestRequest = hcsschema.ModifySettingRequest{ RequestType: requesttype.Remove, - Settings: guestrequest.NetworkModifyRequest{ - AdapterInstanceId: id.String(), - RequestType: requesttype.Remove, - }, + Settings: getNetworkModifyRequest( + id.String(), + requesttype.Remove, + nil), } } else { request.GuestRequest = guestrequest.GuestRequest{ diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/plan9.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/plan9.go index a0a4c5c97e..c2e3a7e9ad 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/plan9.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/plan9.go @@ -4,6 +4,7 @@ import ( "fmt" "github.com/Microsoft/hcsshim/internal/guestrequest" + "github.com/Microsoft/hcsshim/internal/logfields" "github.com/Microsoft/hcsshim/internal/requesttype" "github.com/Microsoft/hcsshim/internal/schema2" "github.com/sirupsen/logrus" @@ -12,6 +13,13 @@ import ( // AddPlan9 adds a Plan9 share to a utility VM. Each Plan9 share is ref-counted and // only added if it isn't already. func (uvm *UtilityVM) AddPlan9(hostPath string, uvmPath string, readOnly bool) error { + logrus.WithFields(logrus.Fields{ + logfields.UVMID: uvm.id, + "host-path": hostPath, + "uvm-path": uvmPath, + "readOnly": readOnly, + }).Debug("uvm::AddPlan9") + if uvm.operatingSystem != "linux" { return errNotSupported } @@ -19,7 +27,6 @@ func (uvm *UtilityVM) AddPlan9(hostPath string, uvmPath string, readOnly bool) e return fmt.Errorf("uvmPath must be passed to AddPlan9") } - logrus.Debugf("uvm::AddPlan9 %s %s %t id:%s", hostPath, uvmPath, readOnly, uvm.id) uvm.m.Lock() defer uvm.m.Unlock() if uvm.plan9Shares == nil { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/scsi.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/scsi.go index 663e912b2a..7788878f8a 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/scsi.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/scsi.go @@ -4,30 +4,35 @@ import ( "fmt" "github.com/Microsoft/hcsshim/internal/guestrequest" + "github.com/Microsoft/hcsshim/internal/logfields" "github.com/Microsoft/hcsshim/internal/requesttype" "github.com/Microsoft/hcsshim/internal/schema2" + "github.com/Microsoft/hcsshim/internal/wclayer" "github.com/sirupsen/logrus" ) var ( - ErrNoAvailableLocation = fmt.Errorf("no available location") - ErrNotAttached = fmt.Errorf("not attached") - ErrAlreadyAttached = fmt.Errorf("already attached") - ErrNoSCSIControllers = fmt.Errorf("no SCSI controllers configured for this utility VM") - ErrNoUvmParameter = fmt.Errorf("invalid parameters - uvm parameter missing") - ErrTooManyAttachments = fmt.Errorf("too many SCSI attachments") + ErrNoAvailableLocation = fmt.Errorf("no available location") + ErrNotAttached = fmt.Errorf("not attached") + ErrAlreadyAttached = fmt.Errorf("already attached") + ErrNoSCSIControllers = fmt.Errorf("no SCSI controllers configured for this utility VM") + ErrTooManyAttachments = fmt.Errorf("too many SCSI attachments") + ErrSCSILayerWCOWUnsupported = fmt.Errorf("SCSI attached layers are not supported for WCOW") ) // allocateSCSI finds the next available slot on the // SCSI controllers associated with a utility VM to use. -func (uvm *UtilityVM) allocateSCSI(hostPath string, uvmPath string) (int, int32, error) { - uvm.m.Lock() - defer uvm.m.Unlock() +// Lock must be held when calling this function +func (uvm *UtilityVM) allocateSCSI(hostPath string, uvmPath string, isLayer bool) (int, int32, error) { for controller, luns := range uvm.scsiLocations { for lun, si := range luns { if si.hostPath == "" { uvm.scsiLocations[controller][lun].hostPath = hostPath uvm.scsiLocations[controller][lun].uvmPath = uvmPath + uvm.scsiLocations[controller][lun].isLayer = isLayer + if isLayer { + uvm.scsiLocations[controller][lun].refCount = 1 + } logrus.Debugf("uvm::allocateSCSI %d:%d %q %q", controller, lun, hostPath, uvmPath) return controller, int32(lun), nil @@ -42,11 +47,10 @@ func (uvm *UtilityVM) deallocateSCSI(controller int, lun int32) error { defer uvm.m.Unlock() logrus.Debugf("uvm::deallocateSCSI %d:%d %+v", controller, lun, uvm.scsiLocations[controller][lun]) uvm.scsiLocations[controller][lun] = scsiInfo{} - return nil } -// Lock must be held when calling this function +// Lock must be held when calling this function. func (uvm *UtilityVM) findSCSIAttachment(findThisHostPath string) (int, int32, string, error) { for controller, luns := range uvm.scsiLocations { for lun, si := range luns { @@ -59,47 +63,140 @@ func (uvm *UtilityVM) findSCSIAttachment(findThisHostPath string) (int, int32, s return -1, -1, "", ErrNotAttached } -// AddSCSI adds a SCSI disk to a utility VM at the next available location. +// AddSCSI adds a SCSI disk to a utility VM at the next available location. This +// function should be called for a RW/scratch layer or a passthrough vhd/vhdx. +// For read-only layers on LCOW as an alternate to PMEM for large layers, use +// AddSCSILayer instead. // -// We are in control of everything ourselves. Hence we have ref- -// counting and so-on tracking what SCSI locations are available or used. +// `hostPath` is required and must point to a vhd/vhdx path. // -// hostPath is required -// uvmPath is optional. +// `uvmPath` is optional. // -// Returns the controller ID (0..3) and LUN (0..63) where the disk is attached. -func (uvm *UtilityVM) AddSCSI(hostPath string, uvmPath string) (int, int32, error) { - if uvm == nil { - return -1, -1, ErrNoUvmParameter +// `readOnly` set to `true` if the vhd/vhdx should be attached read only. +func (uvm *UtilityVM) AddSCSI(hostPath string, uvmPath string, readOnly bool) (int, int32, error) { + logrus.WithFields(logrus.Fields{ + logfields.UVMID: uvm.id, + "host-path": hostPath, + "uvm-path": uvmPath, + "readOnly": readOnly, + }).Debug("uvm::AddSCSI") + + return uvm.addSCSIActual(hostPath, uvmPath, "VirtualDisk", false, readOnly) +} + +// AddSCSIPhysicalDisk attaches a physical disk from the host directly to the +// Utility VM at the next available location. +// +// `hostPath` is required and `likely` start's with `\\.\PHYSICALDRIVE`. +// +// `uvmPath` is optional if a guest mount is not requested. +// +// `readOnly` set to `true` if the physical disk should be attached read only. +func (uvm *UtilityVM) AddSCSIPhysicalDisk(hostPath, uvmPath string, readOnly bool) (int, int32, error) { + logrus.WithFields(logrus.Fields{ + logfields.UVMID: uvm.id, + "host-path": hostPath, + "uvm-path": uvmPath, + "readOnly": readOnly, + }).Debug("uvm::AddSCSIPhysicalDisk") + + return uvm.addSCSIActual(hostPath, uvmPath, "PassThru", false, readOnly) +} + +// AddSCSILayer adds a read-only layer disk to a utility VM at the next available +// location. This function is used by LCOW as an alternate to PMEM for large layers. +// The UVMPath will always be /tmp/S/. +func (uvm *UtilityVM) AddSCSILayer(hostPath string) (int, int32, error) { + logrus.WithFields(logrus.Fields{ + logfields.UVMID: uvm.id, + "host-path": hostPath, + }).Debug("uvm::AddSCSILayer") + + if uvm.operatingSystem == "windows" { + return -1, -1, ErrSCSILayerWCOWUnsupported } - logrus.Debugf("uvm::AddSCSI id:%s hostPath:%s uvmPath:%s", uvm.id, hostPath, uvmPath) + return uvm.addSCSIActual(hostPath, "", "VirtualDisk", true, true) +} + +// addSCSIActual is the implementation behind the external functions AddSCSI and +// AddSCSILayer. +// +// We are in control of everything ourselves. Hence we have ref- counting and +// so-on tracking what SCSI locations are available or used. +// +// `hostPath` is required and may be a vhd/vhdx or physical disk path. +// +// `uvmPath` is optional, and `must` be empty for layers. If `!isLayer` and +// `uvmPath` is empty no guest modify will take place. +// +// `attachmentType` is required and `must` be `VirtualDisk` for vhd/vhdx +// attachments and `PassThru` for physical disk. +// +// `isLayer` indicates that this is a read-only (LCOW) layer VHD. This parameter +// `must not` be used for Windows. +// +// `readOnly` indicates the attachment should be added read only. +// +// Returns the controller ID (0..3) and LUN (0..63) where the disk is attached. +func (uvm *UtilityVM) addSCSIActual(hostPath, uvmPath, attachmentType string, isLayer, readOnly bool) (int, int32, error) { if uvm.scsiControllerCount == 0 { return -1, -1, ErrNoSCSIControllers } + // Ensure the utility VM has access + if err := wclayer.GrantVmAccess(uvm.ID(), hostPath); err != nil { + return -1, -1, err + } + + // We must hold the lock throughout the lookup (findSCSIAttachment) until + // after the possible allocation (allocateSCSI) has been completed to ensure + // there isn't a race condition for it being attached by another thread between + // these two operations. All failure paths between these two must release + // the lock. uvm.m.Lock() - if _, _, _, err := uvm.findSCSIAttachment(hostPath); err == nil { + if controller, lun, _, err := uvm.findSCSIAttachment(hostPath); err == nil { + // So is attached + if isLayer { + // Increment the refcount + uvm.scsiLocations[controller][lun].refCount++ + logrus.Debugf("uvm::AddSCSI id:%s hostPath:%s refCount now %d", uvm.id, hostPath, uvm.scsiLocations[controller][lun].refCount) + uvm.m.Unlock() + return controller, int32(lun), nil + } + uvm.m.Unlock() return -1, -1, ErrAlreadyAttached } - uvm.m.Unlock() - controller, lun, err := uvm.allocateSCSI(hostPath, uvmPath) + // At this point, we know it's not attached, regardless of whether it's a + // ref-counted layer VHD, or not. + controller, lun, err := uvm.allocateSCSI(hostPath, uvmPath, isLayer) if err != nil { + uvm.m.Unlock() return -1, -1, err } + // Auto-generate the UVM path for LCOW layers + if isLayer { + uvmPath = fmt.Sprintf("/tmp/S%d/%d", controller, lun) + } + + // See comment higher up. Now safe to release the lock. + uvm.m.Unlock() + // Note: Can remove this check post-RS5 if multiple controllers are supported if controller > 0 { + uvm.deallocateSCSI(controller, lun) return -1, -1, ErrTooManyAttachments } SCSIModification := &hcsschema.ModifySettingRequest{ RequestType: requesttype.Add, Settings: hcsschema.Attachment{ - Path: hostPath, - Type_: "VirtualDisk", + Path: hostPath, + Type_: attachmentType, + ReadOnly: readOnly, }, ResourcePath: fmt.Sprintf("VirtualMachine/Devices/Scsi/%d/Attachments/%d", controller, lun), } @@ -122,7 +219,7 @@ func (uvm *UtilityVM) AddSCSI(hostPath string, uvmPath string) (int, int32, erro MountPath: uvmPath, Lun: uint8(lun), Controller: uint8(controller), - ReadOnly: false, + ReadOnly: readOnly, }, } } @@ -153,6 +250,14 @@ func (uvm *UtilityVM) RemoveSCSI(hostPath string) error { return err } + if uvm.scsiLocations[controller][lun].isLayer { + uvm.scsiLocations[controller][lun].refCount-- + if uvm.scsiLocations[controller][lun].refCount > 0 { + logrus.Debugf("uvm::RemoveSCSI: refCount now %d: %s %s %d:%d", uvm.scsiLocations[controller][lun].refCount, hostPath, uvm.id, controller, lun) + return nil + } + } + if err := uvm.removeSCSI(hostPath, uvmPath, controller, lun); err != nil { return fmt.Errorf("failed to remove SCSI disk %s from container %s: %s", hostPath, uvm.id, err) @@ -200,3 +305,14 @@ func (uvm *UtilityVM) removeSCSI(hostPath string, uvmPath string, controller int logrus.Debugf("uvm::RemoveSCSI: Success %s removed from %s %d:%d", hostPath, uvm.id, controller, lun) return nil } + +// GetScsiUvmPath returns the guest mounted path of a SCSI drive. +// +// If `hostPath` is not mounted returns `ErrNotAttached`. +func (uvm *UtilityVM) GetScsiUvmPath(hostPath string) (string, error) { + uvm.m.Lock() + defer uvm.m.Unlock() + + _, _, uvmPath, err := uvm.findSCSIAttachment(hostPath) + return uvmPath, err +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/start.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/start.go index 142704d275..612242f768 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/start.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/start.go @@ -12,18 +12,12 @@ import ( const _ERROR_CONNECTION_ABORTED syscall.Errno = 1236 -func forwardGcsLogs(l net.Listener) { - c, err := l.Accept() - l.Close() - if err != nil { - logrus.Error("accepting log socket: ", err) - return - } - j := json.NewDecoder(c) +func parseLogrus(r io.Reader) { + j := json.NewDecoder(r) logger := logrus.StandardLogger() for { e := logrus.Entry{Logger: logger} - err = j.Decode(&e.Data) + err := j.Decode(&e.Data) if err == io.EOF || err == _ERROR_CONNECTION_ABORTED { break } @@ -31,7 +25,7 @@ func forwardGcsLogs(l net.Listener) { // Something went wrong. Read the rest of the data as a single // string and log it at once -- it's probably a GCS panic stack. logrus.Error("gcs log read: ", err) - rest, _ := ioutil.ReadAll(io.MultiReader(j.Buffered(), c)) + rest, _ := ioutil.ReadAll(io.MultiReader(j.Buffered(), r)) if len(rest) != 0 { logrus.Error("gcs stderr: ", string(rest)) } @@ -58,11 +52,25 @@ func forwardGcsLogs(l net.Listener) { } } +func processOutput(l net.Listener, doneChan chan struct{}, handler OutputHandler) { + defer close(doneChan) + + c, err := l.Accept() + l.Close() + if err != nil { + logrus.Error("accepting log socket: ", err) + return + } + defer c.Close() + + handler(c) +} + // Start synchronously starts the utility VM. func (uvm *UtilityVM) Start() error { - if uvm.gcslog != nil { - go forwardGcsLogs(uvm.gcslog) - uvm.gcslog = nil + if uvm.outputListener != nil { + go processOutput(uvm.outputListener, uvm.outputProcessingDone, uvm.outputHandler) + uvm.outputListener = nil } return uvm.hcsSystem.Start() } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/types.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/types.go index 617fcdda54..d629be20a8 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/types.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/types.go @@ -29,6 +29,12 @@ type vsmbShare struct { type scsiInfo struct { hostPath string uvmPath string + + // While most VHDs attached to SCSI are scratch spaces, in the case of LCOW + // when the size is over the size possible to attach to PMEM, we use SCSI for + // read-only layers. As RO layers are shared, we perform ref-counting. + isLayer bool + refCount uint32 } // vpmemInfo is an internal structure used for determining VPMem devices mapped to @@ -64,6 +70,12 @@ type UtilityVM struct { hcsSystem *hcs.System // The handle to the compute system m sync.Mutex // Lock for adding/removing devices + // containerCounter is the current number of containers that have been + // created. This is never decremented in the life of the UVM. + // + // NOTE: All accesses to this MUST be done atomically. + containerCounter uint64 + // VSMB shares that are mapped into a Windows UVM. These are used for read-only // layers and mapped directories vsmbShares map[string]*vsmbShare @@ -71,12 +83,13 @@ type UtilityVM struct { // VPMEM devices that are mapped into a Linux UVM. These are used for read-only layers, or for // booting from VHD. - vpmemDevices [MaxVPMEM]vpmemInfo // Limited by ACPI size. - vpmemMax int32 // Actual number of VPMem devices + vpmemDevices [MaxVPMEMCount]vpmemInfo // Limited by ACPI size. + vpmemMaxCount uint32 // Actual number of VPMem devices + vpmemMaxSizeBytes uint64 // Actual size of VPMem devices // SCSI devices that are mapped into a Windows or Linux utility VM scsiLocations [4][64]scsiInfo // Hyper-V supports 4 controllers, 64 slots per controller. Limited to 1 controller for now though. - scsiControllerCount int // Number of SCSI controllers in the utility VM + scsiControllerCount uint32 // Number of SCSI controllers in the utility VM // Plan9 are directories mapped into a Linux utility VM plan9Shares map[string]*plan9Info @@ -84,5 +97,7 @@ type UtilityVM struct { namespaces map[string]*namespaceInfo - gcslog net.Listener + outputListener net.Listener + outputProcessingDone chan struct{} + outputHandler OutputHandler } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/vpmem.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/vpmem.go index a735c90653..13f59ce8b0 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/vpmem.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/vpmem.go @@ -6,6 +6,7 @@ import ( "github.com/Microsoft/hcsshim/internal/guestrequest" "github.com/Microsoft/hcsshim/internal/requesttype" "github.com/Microsoft/hcsshim/internal/schema2" + "github.com/Microsoft/hcsshim/internal/wclayer" "github.com/sirupsen/logrus" ) @@ -60,6 +61,11 @@ func (uvm *UtilityVM) AddVPMEM(hostPath string, expose bool) (uint32, string, er deviceNumber, uvmPath, err = uvm.findVPMEMDevice(hostPath) if err != nil { + // Ensure the utility VM has access + if err := wclayer.GrantVmAccess(uvm.ID(), hostPath); err != nil { + return 0, "", err + } + // It doesn't exist, so we're going to allocate and hot-add it deviceNumber, err = uvm.allocateVPMEM(hostPath) if err != nil { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/vsmb.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/vsmb.go index 833f89c945..e8fcae6be0 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/vsmb.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/vsmb.go @@ -9,6 +9,15 @@ import ( "github.com/sirupsen/logrus" ) +// findVSMBShare finds a share by `hostPath`. If not found returns `ErrNotAttached`. +func (uvm *UtilityVM) findVSMBShare(hostPath string) (*vsmbShare, error) { + share, ok := uvm.vsmbShares[hostPath] + if !ok { + return nil, ErrNotAttached + } + return share, nil +} + func (share *vsmbShare) GuestPath() string { return `\\?\VMSMB\VSMB-{dcc079ae-60ba-4d07-847c-3493609c0870}\` + share.name } @@ -24,11 +33,8 @@ func (uvm *UtilityVM) AddVSMB(hostPath string, guestRequest interface{}, options logrus.Debugf("uvm::AddVSMB %s %+v %+v id:%s", hostPath, guestRequest, options, uvm.id) uvm.m.Lock() defer uvm.m.Unlock() - if uvm.vsmbShares == nil { - uvm.vsmbShares = make(map[string]*vsmbShare) - } - share := uvm.vsmbShares[hostPath] - if share == nil { + share, err := uvm.findVSMBShare(hostPath) + if err == ErrNotAttached { uvm.vsmbCounter++ shareName := "s" + strconv.FormatUint(uvm.vsmbCounter, 16) @@ -65,8 +71,8 @@ func (uvm *UtilityVM) RemoveVSMB(hostPath string) error { logrus.Debugf("uvm::RemoveVSMB %s id:%s", hostPath, uvm.id) uvm.m.Lock() defer uvm.m.Unlock() - share := uvm.vsmbShares[hostPath] - if share == nil { + share, err := uvm.findVSMBShare(hostPath) + if err != nil { return fmt.Errorf("%s is not present as a VSMB share in %s, cannot remove", hostPath, uvm.id) } @@ -96,9 +102,9 @@ func (uvm *UtilityVM) GetVSMBUvmPath(hostPath string) (string, error) { } uvm.m.Lock() defer uvm.m.Unlock() - share := uvm.vsmbShares[hostPath] - if share == nil { - return "", fmt.Errorf("%s not found as VSMB share in %s", hostPath, uvm.id) + share, err := uvm.findVSMBShare(hostPath) + if err != nil { + return "", err } path := share.GuestPath() logrus.Debugf("uvm::GetVSMBUvmPath Success %s id:%s path:%s", hostPath, uvm.id, path) diff --git a/vendor/github.com/Microsoft/hcsshim/internal/uvm/wait.go b/vendor/github.com/Microsoft/hcsshim/internal/uvm/wait.go index 7f6778c422..e9d3477cb4 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/uvm/wait.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/uvm/wait.go @@ -1,6 +1,27 @@ package uvm +import ( + "github.com/Microsoft/hcsshim/internal/logfields" + "github.com/sirupsen/logrus" +) + +func (uvm *UtilityVM) waitForOutput() { + logrus.WithField(logfields.UVMID, uvm.ID()). + Debug("UVM exited, waiting for output processing to complete") + if uvm.outputProcessingDone != nil { + <-uvm.outputProcessingDone + } +} + // Waits synchronously waits for a utility VM to terminate. func (uvm *UtilityVM) Wait() error { - return uvm.hcsSystem.Wait() + err := uvm.hcsSystem.Wait() + uvm.waitForOutput() + return err +} + +func (uvm *UtilityVM) WaitExpectedError(expected error) error { + err := uvm.hcsSystem.WaitExpectedError(expected) + uvm.waitForOutput() + return err } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go index 3a0d4bc58e..dcb9192685 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go @@ -9,17 +9,24 @@ import ( // For a read/write layer, the mounted filesystem will appear as a volume on the // host, while a read-only layer is generally expected to be a no-op. // An activated layer must later be deactivated via DeactivateLayer. -func ActivateLayer(path string) error { - title := "hcsshim::ActivateLayer " - logrus.Debugf(title+"path %s", path) +func ActivateLayer(path string) (err error) { + title := "hcsshim::ActivateLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() - err := activateLayer(&stdDriverInfo, path) + err = activateLayer(&stdDriverInfo, path) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+" - succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go index d158177308..be2bc3fd65 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go @@ -7,17 +7,25 @@ import ( // CreateLayer creates a new, empty, read-only layer on the filesystem based on // the parent layer provided. -func CreateLayer(path, parent string) error { - title := "hcsshim::CreateLayer " - logrus.Debugf(title+"ID %s parent %s", path, parent) +func CreateLayer(path, parent string) (err error) { + title := "hcsshim::CreateLayer" + fields := logrus.Fields{ + "parent": parent, + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() - err := createLayer(&stdDriverInfo, path, parent) + err = createLayer(&stdDriverInfo, path, parent) if err != nil { - err = hcserror.Errorf(err, title, "path=%s parent=%s", path, parent) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"- succeeded path=%s parent=%s", path, parent) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go index bf2fece198..7e3351289e 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go @@ -9,9 +9,20 @@ import ( // This requires both the id of the direct parent layer, as well as the full list // of paths to all parent layers up to the base (and including the direct parent // whose id was provided). -func CreateScratchLayer(path string, parentLayerPaths []string) error { - title := "hcsshim::CreateScratchLayer " - logrus.Debugf(title+"path %s", path) +func CreateScratchLayer(path string, parentLayerPaths []string) (err error) { + title := "hcsshim::CreateScratchLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() // Generate layer descriptors layers, err := layerPathsToDescriptors(parentLayerPaths) @@ -21,11 +32,7 @@ func CreateScratchLayer(path string, parentLayerPaths []string) error { err = createSandboxLayer(&stdDriverInfo, path, 0, layers) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"- succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go index b998f8a193..2dd5d57159 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go @@ -6,17 +6,24 @@ import ( ) // DeactivateLayer will dismount a layer that was mounted via ActivateLayer. -func DeactivateLayer(path string) error { - title := "hcsshim::DeactivateLayer " - logrus.Debugf(title+"path %s", path) +func DeactivateLayer(path string) (err error) { + title := "hcsshim::DeactivateLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() - err := deactivateLayer(&stdDriverInfo, path) + err = deactivateLayer(&stdDriverInfo, path) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+"- failed", "") } - - logrus.Debugf(title+"succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go index dc14cecc47..4da690c203 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go @@ -7,17 +7,24 @@ import ( // DestroyLayer will remove the on-disk files representing the layer with the given // path, including that layer's containing folder, if any. -func DestroyLayer(path string) error { - title := "hcsshim::DestroyLayer " - logrus.Debugf(title+"path %s", path) +func DestroyLayer(path string) (err error) { + title := "hcsshim::DestroyLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() - err := destroyLayer(&stdDriverInfo, path) + err = destroyLayer(&stdDriverInfo, path) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go index 7832bb452e..651676fb25 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go @@ -6,17 +6,25 @@ import ( ) // ExpandScratchSize expands the size of a layer to at least size bytes. -func ExpandScratchSize(path string, size uint64) error { - title := "hcsshim::ExpandScratchSize " - logrus.Debugf(title+"path=%s size=%d", path, size) +func ExpandScratchSize(path string, size uint64) (err error) { + title := "hcsshim::ExpandScratchSize" + fields := logrus.Fields{ + "path": path, + "size": size, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() - err := expandSandboxSize(&stdDriverInfo, path, size) + err = expandSandboxSize(&stdDriverInfo, path, size) if err != nil { - err = hcserror.Errorf(err, title, "path=%s size=%d", path, size) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"- succeeded path=%s size=%d", path, size) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go index c6b3480ce7..0425b33955 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go @@ -1,14 +1,11 @@ package wclayer import ( - "io" "io/ioutil" "os" - "syscall" "github.com/Microsoft/go-winio" "github.com/Microsoft/hcsshim/internal/hcserror" - "github.com/Microsoft/hcsshim/internal/interop" "github.com/sirupsen/logrus" ) @@ -17,9 +14,21 @@ import ( // format includes any metadata required for later importing the layer (using // ImportLayer), and requires the full list of parent layer paths in order to // perform the export. -func ExportLayer(path string, exportFolderPath string, parentLayerPaths []string) error { - title := "hcsshim::ExportLayer " - logrus.Debugf(title+"path %s folder %s", path, exportFolderPath) +func ExportLayer(path string, exportFolderPath string, parentLayerPaths []string) (err error) { + title := "hcsshim::ExportLayer" + fields := logrus.Fields{ + "path": path, + "exportFolderPath": exportFolderPath, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() // Generate layer descriptors layers, err := layerPathsToDescriptors(parentLayerPaths) @@ -29,12 +38,8 @@ func ExportLayer(path string, exportFolderPath string, parentLayerPaths []string err = exportLayer(&stdDriverInfo, path, exportFolderPath, layers) if err != nil { - err = hcserror.Errorf(err, title, "path=%s folder=%s", path, exportFolderPath) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s folder=%s", path, exportFolderPath) return nil } @@ -44,96 +49,20 @@ type LayerReader interface { Close() error } -// FilterLayerReader provides an interface for extracting the contents of an on-disk layer. -type FilterLayerReader struct { - context uintptr -} - -// Next reads the next available file from a layer, ensuring that parent directories are always read -// before child files and directories. -// -// Next returns the file's relative path, size, and basic file metadata. Read() should be used to -// extract a Win32 backup stream with the remainder of the metadata and the data. -func (r *FilterLayerReader) Next() (string, int64, *winio.FileBasicInfo, error) { - var fileNamep *uint16 - fileInfo := &winio.FileBasicInfo{} - var deleted uint32 - var fileSize int64 - err := exportLayerNext(r.context, &fileNamep, fileInfo, &fileSize, &deleted) - if err != nil { - if err == syscall.ERROR_NO_MORE_FILES { - err = io.EOF - } else { - err = hcserror.New(err, "ExportLayerNext", "") - } - return "", 0, nil, err - } - fileName := interop.ConvertAndFreeCoTaskMemString(fileNamep) - if deleted != 0 { - fileInfo = nil - } - if fileName[0] == '\\' { - fileName = fileName[1:] - } - return fileName, fileSize, fileInfo, nil -} - -// Read reads from the current file's Win32 backup stream. -func (r *FilterLayerReader) Read(b []byte) (int, error) { - var bytesRead uint32 - err := exportLayerRead(r.context, b, &bytesRead) - if err != nil { - return 0, hcserror.New(err, "ExportLayerRead", "") - } - if bytesRead == 0 { - return 0, io.EOF - } - return int(bytesRead), nil -} - -// Close frees resources associated with the layer reader. It will return an -// error if there was an error while reading the layer or of the layer was not -// completely read. -func (r *FilterLayerReader) Close() (err error) { - if r.context != 0 { - err = exportLayerEnd(r.context) - if err != nil { - err = hcserror.New(err, "ExportLayerEnd", "") - } - r.context = 0 - } - return -} - // NewLayerReader returns a new layer reader for reading the contents of an on-disk layer. // The caller must have taken the SeBackupPrivilege privilege // to call this and any methods on the resulting LayerReader. func NewLayerReader(path string, parentLayerPaths []string) (LayerReader, error) { - if procExportLayerBegin.Find() != nil { - // The new layer reader is not available on this Windows build. Fall back to the - // legacy export code path. - exportPath, err := ioutil.TempDir("", "hcs") - if err != nil { - return nil, err - } - err = ExportLayer(path, exportPath, parentLayerPaths) - if err != nil { - os.RemoveAll(exportPath) - return nil, err - } - return &legacyLayerReaderWrapper{newLegacyLayerReader(exportPath)}, nil - } - - layers, err := layerPathsToDescriptors(parentLayerPaths) + exportPath, err := ioutil.TempDir("", "hcs") if err != nil { return nil, err } - r := &FilterLayerReader{} - err = exportLayerBegin(&stdDriverInfo, path, layers, &r.context) + err = ExportLayer(path, exportPath, parentLayerPaths) if err != nil { - return nil, hcserror.New(err, "ExportLayerBegin", "") + os.RemoveAll(exportPath) + return nil, err } - return r, err + return &legacyLayerReaderWrapper{newLegacyLayerReader(exportPath)}, nil } type legacyLayerReaderWrapper struct { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go index 8c37549a0e..d60b6ed531 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go @@ -11,20 +11,29 @@ import ( // the path at which that layer can be accessed. This path may be a volume path // if the layer is a mounted read-write layer, otherwise it is expected to be the // folder path at which the layer is stored. -func GetLayerMountPath(path string) (string, error) { - title := "hcsshim::GetLayerMountPath " - logrus.Debugf(title+"path %s", path) +func GetLayerMountPath(path string) (_ string, err error) { + title := "hcsshim::GetLayerMountPath" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() var mountPathLength uintptr mountPathLength = 0 // Call the procedure itself. - logrus.Debugf("Calling proc (1)") - err := getLayerMountPath(&stdDriverInfo, path, &mountPathLength, nil) + logrus.WithFields(fields).Debug("Calling proc (1)") + err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, nil) if err != nil { - err = hcserror.Errorf(err, title, "(first call) path=%s", path) - logrus.Error(err) - return "", err + return "", hcserror.New(err, title+" - failed", "(first call)") } // Allocate a mount path of the returned length. @@ -35,15 +44,13 @@ func GetLayerMountPath(path string) (string, error) { mountPathp[0] = 0 // Call the procedure again - logrus.Debugf("Calling proc (2)") + logrus.WithFields(fields).Debug("Calling proc (2)") err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, &mountPathp[0]) if err != nil { - err = hcserror.Errorf(err, title, "(second call) path=%s", path) - logrus.Error(err) - return "", err + return "", hcserror.New(err, title+" - failed", "(second call)") } mountPath := syscall.UTF16ToString(mountPathp[0:]) - logrus.Debugf(title+"succeeded path=%s mountPath=%s", path, mountPath) + fields["mountPath"] = mountPath return mountPath, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go index 10899c68af..dbd83ef2bc 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go @@ -10,17 +10,20 @@ import ( // image store and return descriptive info about those images for the purpose // of registering them with the graphdriver, graph, and tagstore. func GetSharedBaseImages() (imageData string, err error) { - title := "hcsshim::GetSharedBaseImages " + title := "hcsshim::GetSharedBaseImages" + logrus.Debug(title) + defer func() { + if err != nil { + logrus.WithError(err).Error(err) + } else { + logrus.WithField("imageData", imageData).Debug(title + " - succeeded") + } + }() - logrus.Debugf("Calling proc") var buffer *uint16 err = getBaseImages(&buffer) if err != nil { - err = hcserror.New(err, title, "") - logrus.Error(err) - return + return "", hcserror.New(err, title+" - failed", "") } - imageData = interop.ConvertAndFreeCoTaskMemString(buffer) - logrus.Debugf(title+" - succeeded output=%s", imageData) - return + return interop.ConvertAndFreeCoTaskMemString(buffer), nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go index d86e678275..05735df6cd 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go @@ -1,24 +1,30 @@ package wclayer import ( - "fmt" - "github.com/Microsoft/hcsshim/internal/hcserror" "github.com/sirupsen/logrus" ) // GrantVmAccess adds access to a file for a given VM -func GrantVmAccess(vmid string, filepath string) error { - title := fmt.Sprintf("hcsshim::GrantVmAccess id:%s path:%s ", vmid, filepath) - logrus.Debugf(title) +func GrantVmAccess(vmid string, filepath string) (err error) { + title := "hcsshim::GrantVmAccess" + fields := logrus.Fields{ + "vm-id": vmid, + "path": filepath, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() - err := grantVmAccess(vmid, filepath) + err = grantVmAccess(vmid, filepath) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", filepath) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title + " - succeeded") return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go index c978450f82..76a804f2af 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go @@ -1,7 +1,6 @@ package wclayer import ( - "errors" "io/ioutil" "os" "path/filepath" @@ -16,9 +15,21 @@ import ( // that into a layer with the id layerId. Note that in order to correctly populate // the layer and interperet the transport format, all parent layers must already // be present on the system at the paths provided in parentLayerPaths. -func ImportLayer(path string, importFolderPath string, parentLayerPaths []string) error { - title := "hcsshim::ImportLayer " - logrus.Debugf(title+"path %s folder %s", path, importFolderPath) +func ImportLayer(path string, importFolderPath string, parentLayerPaths []string) (err error) { + title := "hcsshim::ImportLayer" + fields := logrus.Fields{ + "path": path, + "importFolderPath": importFolderPath, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() // Generate layer descriptors layers, err := layerPathsToDescriptors(parentLayerPaths) @@ -28,12 +39,8 @@ func ImportLayer(path string, importFolderPath string, parentLayerPaths []string err = importLayer(&stdDriverInfo, path, importFolderPath, layers) if err != nil { - err = hcserror.Errorf(err, title, "path=%s folder=%s", path, importFolderPath) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s folder=%s", path, importFolderPath) return nil } @@ -52,69 +59,6 @@ type LayerWriter interface { Close() error } -// FilterLayerWriter provides an interface to write the contents of a layer to the file system. -type FilterLayerWriter struct { - context uintptr -} - -// Add adds a file or directory to the layer. The file's parent directory must have already been added. -// -// name contains the file's relative path. fileInfo contains file times and file attributes; the rest -// of the file metadata and the file data must be written as a Win32 backup stream to the Write() method. -// winio.BackupStreamWriter can be used to facilitate this. -func (w *FilterLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error { - if name[0] != '\\' { - name = `\` + name - } - err := importLayerNext(w.context, name, fileInfo) - if err != nil { - return hcserror.New(err, "ImportLayerNext", "") - } - return nil -} - -// AddLink adds a hard link to the layer. The target of the link must have already been added. -func (w *FilterLayerWriter) AddLink(name string, target string) error { - return errors.New("hard links not yet supported") -} - -// Remove removes a file from the layer. The file must have been present in the parent layer. -// -// name contains the file's relative path. -func (w *FilterLayerWriter) Remove(name string) error { - if name[0] != '\\' { - name = `\` + name - } - err := importLayerNext(w.context, name, nil) - if err != nil { - return hcserror.New(err, "ImportLayerNext", "") - } - return nil -} - -// Write writes more backup stream data to the current file. -func (w *FilterLayerWriter) Write(b []byte) (int, error) { - err := importLayerWrite(w.context, b) - if err != nil { - err = hcserror.New(err, "ImportLayerWrite", "") - return 0, err - } - return len(b), err -} - -// Close completes the layer write operation. The error must be checked to ensure that the -// operation was successful. -func (w *FilterLayerWriter) Close() (err error) { - if w.context != 0 { - err = importLayerEnd(w.context) - if err != nil { - err = hcserror.New(err, "ImportLayerEnd", "") - } - w.context = 0 - } - return -} - type legacyLayerWriterWrapper struct { *legacyLayerWriter path string @@ -175,32 +119,17 @@ func NewLayerWriter(path string, parentLayerPaths []string) (LayerWriter, error) }, nil } - if procImportLayerBegin.Find() != nil { - // The new layer reader is not available on this Windows build. Fall back to the - // legacy export code path. - importPath, err := ioutil.TempDir("", "hcs") - if err != nil { - return nil, err - } - w, err := newLegacyLayerWriter(importPath, parentLayerPaths, path) - if err != nil { - return nil, err - } - return &legacyLayerWriterWrapper{ - legacyLayerWriter: w, - path: importPath, - parentLayerPaths: parentLayerPaths, - }, nil - } - layers, err := layerPathsToDescriptors(parentLayerPaths) + importPath, err := ioutil.TempDir("", "hcs") if err != nil { return nil, err } - - w := &FilterLayerWriter{} - err = importLayerBegin(&stdDriverInfo, path, layers, &w.context) + w, err := newLegacyLayerWriter(importPath, parentLayerPaths, path) if err != nil { - return nil, hcserror.New(err, "ImportLayerStart", "") + return nil, err } - return w, nil + return &legacyLayerWriterWrapper{ + legacyLayerWriter: w, + path: importPath, + parentLayerPaths: parentLayerPaths, + }, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go index 71287ff8a7..258167a579 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go @@ -7,19 +7,27 @@ import ( // LayerExists will return true if a layer with the given id exists and is known // to the system. -func LayerExists(path string) (bool, error) { - title := "hcsshim::LayerExists " - logrus.Debugf(title+"path %s", path) +func LayerExists(path string) (_ bool, err error) { + title := "hcsshim::LayerExists" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() // Call the procedure itself. var exists uint32 - err := layerExists(&stdDriverInfo, path, &exists) + err = layerExists(&stdDriverInfo, path, &exists) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return false, err + return false, hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s exists=%d", path, exists) + fields["layer-exists"] = exists != 0 return exists != 0, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go index a1b8b98826..6d0ae8a074 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go @@ -75,13 +75,13 @@ func layerPathsToDescriptors(parentLayerPaths []string) ([]WC_LAYER_DESCRIPTOR, for i := 0; i < len(parentLayerPaths); i++ { g, err := LayerID(parentLayerPaths[i]) if err != nil { - logrus.Debugf("Failed to convert name to guid %s", err) + logrus.WithError(err).Debug("Failed to convert name to guid") return nil, err } p, err := syscall.UTF16PtrFromString(parentLayerPaths[i]) if err != nil { - logrus.Debugf("Failed conversion of parentLayerPath to pointer %s", err) + logrus.WithError(err).Debug("Failed conversion of parentLayerPath to pointer") return nil, err } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go index 741994ba4d..45a63cf65f 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go @@ -10,15 +10,25 @@ import ( // Host Compute Service, ensuring GUIDs generated with the same string are common // across all clients. func NameToGuid(name string) (id guid.GUID, err error) { - title := "hcsshim::NameToGuid " + title := "hcsshim::NameToGuid" + fields := logrus.Fields{ + "name": name, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() err = nameToGuid(name, &id) if err != nil { - err = hcserror.Errorf(err, title, "name=%s", name) - logrus.Error(err) + err = hcserror.New(err, title+" - failed", "") return } - - logrus.Debugf(title+"name:%s guid:%s", name, id.String()) + fields["guid"] = id.String() return } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go index bd4005dc4f..2b65b01862 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go @@ -14,9 +14,20 @@ var prepareLayerLock sync.Mutex // parent layers, and is necessary in order to view or interact with the layer // as an actual filesystem (reading and writing files, creating directories, etc). // Disabling the filter must be done via UnprepareLayer. -func PrepareLayer(path string, parentLayerPaths []string) error { - title := "hcsshim::PrepareLayer " - logrus.Debugf(title+"path %s", path) +func PrepareLayer(path string, parentLayerPaths []string) (err error) { + title := "hcsshim::PrepareLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() // Generate layer descriptors layers, err := layerPathsToDescriptors(parentLayerPaths) @@ -30,11 +41,7 @@ func PrepareLayer(path string, parentLayerPaths []string) error { defer prepareLayerLock.Unlock() err = prepareLayer(&stdDriverInfo, path, layers) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go index 5f1b4f4f4e..bccd459691 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go @@ -7,17 +7,24 @@ import ( // UnprepareLayer disables the filesystem filter for the read-write layer with // the given id. -func UnprepareLayer(path string) error { - title := "hcsshim::UnprepareLayer " - logrus.Debugf(title+"path %s", path) +func UnprepareLayer(path string) (err error) { + title := "hcsshim::UnprepareLayer" + fields := logrus.Fields{ + "path": path, + } + logrus.WithFields(fields).Debug(title) + defer func() { + if err != nil { + fields[logrus.ErrorKey] = err + logrus.WithFields(fields).Error(err) + } else { + logrus.WithFields(fields).Debug(title + " - succeeded") + } + }() - err := unprepareLayer(&stdDriverInfo, path) + err = unprepareLayer(&stdDriverInfo, path) if err != nil { - err = hcserror.Errorf(err, title, "path=%s", path) - logrus.Error(err) - return err + return hcserror.New(err, title+" - failed", "") } - - logrus.Debugf(title+"succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go index 768a6f2f16..78f2aacd8c 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go @@ -2,7 +2,7 @@ package wclayer import "github.com/Microsoft/hcsshim/internal/guid" -//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go -winio wclayer.go +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go wclayer.go //sys activateLayer(info *driverInfo, id string) (hr error) = vmcompute.ActivateLayer? //sys copyLayer(info *driverInfo, srcId string, dstId string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.CopyLayer? @@ -22,16 +22,6 @@ import "github.com/Microsoft/hcsshim/internal/guid" //sys processBaseImage(path string) (hr error) = vmcompute.ProcessBaseImage? //sys processUtilityImage(path string) (hr error) = vmcompute.ProcessUtilityImage? -//sys importLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) = vmcompute.ImportLayerBegin? -//sys importLayerNext(context uintptr, fileName string, fileInfo *winio.FileBasicInfo) (hr error) = vmcompute.ImportLayerNext? -//sys importLayerWrite(context uintptr, buffer []byte) (hr error) = vmcompute.ImportLayerWrite? -//sys importLayerEnd(context uintptr) (hr error) = vmcompute.ImportLayerEnd? - -//sys exportLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) = vmcompute.ExportLayerBegin? -//sys exportLayerNext(context uintptr, fileName **uint16, fileInfo *winio.FileBasicInfo, fileSize *int64, deleted *uint32) (hr error) = vmcompute.ExportLayerNext? -//sys exportLayerRead(context uintptr, buffer []byte, bytesRead *uint32) (hr error) = vmcompute.ExportLayerRead? -//sys exportLayerEnd(context uintptr) (hr error) = vmcompute.ExportLayerEnd? - //sys grantVmAccess(vmid string, filepath string) (hr error) = vmcompute.GrantVmAccess? type _guid = guid.GUID diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go index cb813aa3d4..d853ab2595 100644 --- a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated mksyscall_windows.exe DO NOT EDIT package wclayer @@ -6,8 +6,6 @@ import ( "syscall" "unsafe" - "github.com/Microsoft/go-winio" - "github.com/Microsoft/hcsshim/internal/interop" "golang.org/x/sys/windows" ) @@ -58,14 +56,6 @@ var ( procUnprepareLayer = modvmcompute.NewProc("UnprepareLayer") procProcessBaseImage = modvmcompute.NewProc("ProcessBaseImage") procProcessUtilityImage = modvmcompute.NewProc("ProcessUtilityImage") - procImportLayerBegin = modvmcompute.NewProc("ImportLayerBegin") - procImportLayerNext = modvmcompute.NewProc("ImportLayerNext") - procImportLayerWrite = modvmcompute.NewProc("ImportLayerWrite") - procImportLayerEnd = modvmcompute.NewProc("ImportLayerEnd") - procExportLayerBegin = modvmcompute.NewProc("ExportLayerBegin") - procExportLayerNext = modvmcompute.NewProc("ExportLayerNext") - procExportLayerRead = modvmcompute.NewProc("ExportLayerRead") - procExportLayerEnd = modvmcompute.NewProc("ExportLayerEnd") procGrantVmAccess = modvmcompute.NewProc("GrantVmAccess") ) @@ -84,7 +74,10 @@ func _activateLayer(info *driverInfo, id *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procActivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -113,7 +106,10 @@ func _copyLayer(info *driverInfo, srcId *uint16, dstId *uint16, descriptors []WC } r0, _, _ := syscall.Syscall6(procCopyLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(srcId)), uintptr(unsafe.Pointer(dstId)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -138,7 +134,10 @@ func _createLayer(info *driverInfo, id *uint16, parent *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procCreateLayer.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(parent))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -162,7 +161,10 @@ func _createSandboxLayer(info *driverInfo, id *uint16, parent uintptr, descripto } r0, _, _ := syscall.Syscall6(procCreateSandboxLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(parent), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -182,7 +184,10 @@ func _expandSandboxSize(info *driverInfo, id *uint16, size uint64) (hr error) { } r0, _, _ := syscall.Syscall(procExpandSandboxSize.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(size)) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -202,7 +207,10 @@ func _deactivateLayer(info *driverInfo, id *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procDeactivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -222,7 +230,10 @@ func _destroyLayer(info *driverInfo, id *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procDestroyLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -251,7 +262,10 @@ func _exportLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_L } r0, _, _ := syscall.Syscall6(procExportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -271,7 +285,10 @@ func _getLayerMountPath(info *driverInfo, id *uint16, length *uintptr, buffer *u } r0, _, _ := syscall.Syscall6(procGetLayerMountPath.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(length)), uintptr(unsafe.Pointer(buffer)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -282,7 +299,10 @@ func getBaseImages(buffer **uint16) (hr error) { } r0, _, _ := syscall.Syscall(procGetBaseImages.Addr(), 1, uintptr(unsafe.Pointer(buffer)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -311,7 +331,10 @@ func _importLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_L } r0, _, _ := syscall.Syscall6(procImportLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(path)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -331,7 +354,10 @@ func _layerExists(info *driverInfo, id *uint16, exists *uint32) (hr error) { } r0, _, _ := syscall.Syscall(procLayerExists.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(exists))) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -351,7 +377,10 @@ func _nameToGuid(name *uint16, guid *_guid) (hr error) { } r0, _, _ := syscall.Syscall(procNameToGuid.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(guid)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -375,7 +404,10 @@ func _prepareLayer(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPT } r0, _, _ := syscall.Syscall6(procPrepareLayer.Addr(), 4, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -395,7 +427,10 @@ func _unprepareLayer(info *driverInfo, id *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procUnprepareLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -415,7 +450,10 @@ func _processBaseImage(path *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procProcessBaseImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -435,138 +473,10 @@ func _processUtilityImage(path *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procProcessUtilityImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func importLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _importLayerBegin(info, _p0, descriptors, context) -} - -func _importLayerBegin(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { - var _p1 *WC_LAYER_DESCRIPTOR - if len(descriptors) > 0 { - _p1 = &descriptors[0] - } - if hr = procImportLayerBegin.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procImportLayerBegin.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), uintptr(unsafe.Pointer(context)), 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func importLayerNext(context uintptr, fileName string, fileInfo *winio.FileBasicInfo) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(fileName) - if hr != nil { - return - } - return _importLayerNext(context, _p0, fileInfo) -} - -func _importLayerNext(context uintptr, fileName *uint16, fileInfo *winio.FileBasicInfo) (hr error) { - if hr = procImportLayerNext.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procImportLayerNext.Addr(), 3, uintptr(context), uintptr(unsafe.Pointer(fileName)), uintptr(unsafe.Pointer(fileInfo))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func importLayerWrite(context uintptr, buffer []byte) (hr error) { - var _p0 *byte - if len(buffer) > 0 { - _p0 = &buffer[0] - } - if hr = procImportLayerWrite.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procImportLayerWrite.Addr(), 3, uintptr(context), uintptr(unsafe.Pointer(_p0)), uintptr(len(buffer))) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func importLayerEnd(context uintptr) (hr error) { - if hr = procImportLayerEnd.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procImportLayerEnd.Addr(), 1, uintptr(context), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func exportLayerBegin(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _exportLayerBegin(info, _p0, descriptors, context) -} - -func _exportLayerBegin(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR, context *uintptr) (hr error) { - var _p1 *WC_LAYER_DESCRIPTOR - if len(descriptors) > 0 { - _p1 = &descriptors[0] - } - if hr = procExportLayerBegin.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procExportLayerBegin.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(_p1)), uintptr(len(descriptors)), uintptr(unsafe.Pointer(context)), 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func exportLayerNext(context uintptr, fileName **uint16, fileInfo *winio.FileBasicInfo, fileSize *int64, deleted *uint32) (hr error) { - if hr = procExportLayerNext.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procExportLayerNext.Addr(), 5, uintptr(context), uintptr(unsafe.Pointer(fileName)), uintptr(unsafe.Pointer(fileInfo)), uintptr(unsafe.Pointer(fileSize)), uintptr(unsafe.Pointer(deleted)), 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func exportLayerRead(context uintptr, buffer []byte, bytesRead *uint32) (hr error) { - var _p0 *byte - if len(buffer) > 0 { - _p0 = &buffer[0] - } - if hr = procExportLayerRead.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procExportLayerRead.Addr(), 4, uintptr(context), uintptr(unsafe.Pointer(_p0)), uintptr(len(buffer)), uintptr(unsafe.Pointer(bytesRead)), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) - } - return -} - -func exportLayerEnd(context uintptr) (hr error) { - if hr = procExportLayerEnd.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procExportLayerEnd.Addr(), 1, uintptr(context), 0, 0) - if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } @@ -591,7 +501,10 @@ func _grantVmAccess(vmid *uint16, filepath *uint16) (hr error) { } r0, _, _ := syscall.Syscall(procGrantVmAccess.Addr(), 2, uintptr(unsafe.Pointer(vmid)), uintptr(unsafe.Pointer(filepath)), 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } diff --git a/vendor/github.com/Microsoft/hcsshim/layer.go b/vendor/github.com/Microsoft/hcsshim/layer.go index 8cdc247dcd..df0e63bbde 100644 --- a/vendor/github.com/Microsoft/hcsshim/layer.go +++ b/vendor/github.com/Microsoft/hcsshim/layer.go @@ -5,7 +5,6 @@ import ( "path/filepath" "github.com/Microsoft/hcsshim/internal/guid" - "github.com/Microsoft/hcsshim/internal/wclayer" ) @@ -19,6 +18,7 @@ func ActivateLayer(info DriverInfo, id string) error { func CreateLayer(info DriverInfo, id, parent string) error { return wclayer.CreateLayer(layerPath(&info, id), parent) } + // New clients should use CreateScratchLayer instead. Kept in to preserve API compatibility. func CreateSandboxLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error { return wclayer.CreateScratchLayer(layerPath(&info, layerId), parentLayerPaths) @@ -32,6 +32,7 @@ func DeactivateLayer(info DriverInfo, id string) error { func DestroyLayer(info DriverInfo, id string) error { return wclayer.DestroyLayer(layerPath(&info, id)) } + // New clients should use ExpandScratchSize instead. Kept in to preserve API compatibility. func ExpandSandboxSize(info DriverInfo, layerId string, size uint64) error { return wclayer.ExpandScratchSize(layerPath(&info, layerId), size) @@ -72,9 +73,6 @@ type DriverInfo struct { HomeDir string } -type FilterLayerReader = wclayer.FilterLayerReader -type FilterLayerWriter = wclayer.FilterLayerWriter - type GUID [16]byte func NameToGuid(name string) (id GUID, err error) { diff --git a/vendor/github.com/Microsoft/hcsshim/mksyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/mksyscall_windows.go index 8219793347..7647734de9 100644 --- a/vendor/github.com/Microsoft/hcsshim/mksyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/mksyscall_windows.go @@ -300,7 +300,10 @@ func (r *Rets) SetErrorCode() string { %s = %sErrno(r0) }` const hrCode = `if int32(r0) < 0 { - %s = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + %s = %sErrno(r0) }` if r.Name == "" && !r.ReturnsError { return "" @@ -310,7 +313,7 @@ func (r *Rets) SetErrorCode() string { } if r.Type == "error" { if r.Name == "hr" { - return fmt.Sprintf(hrCode, r.Name) + return fmt.Sprintf(hrCode, r.Name, syscalldot()) } else { return fmt.Sprintf(code, r.Name, syscalldot()) } @@ -773,7 +776,6 @@ func (src *Source) Generate(w io.Writer) error { src.ExternalImport("golang.org/x/sys/windows") } } - src.ExternalImport("github.com/Microsoft/hcsshim/internal/interop") if *winio { src.ExternalImport("github.com/Microsoft/go-winio") } @@ -788,6 +790,9 @@ func (src *Source) Generate(w io.Writer) error { if !*systemDLL { return syscalldot() + "NewLazyDLL(" + arg + ")" } + if strings.HasPrefix(dll, "api_") || strings.HasPrefix(dll, "ext_") { + arg = strings.Replace(arg, "_", "-", -1) + } switch pkgtype { case pkgStd: return syscalldot() + "NewLazyDLL(sysdll.Add(" + arg + "))" @@ -847,7 +852,7 @@ func main() { // TODO: use println instead to print in the following template const srcTemplate = ` -{{define "main"}}// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +{{define "main"}}// Code generated mksyscall_windows.exe DO NOT EDIT package {{packagename}} diff --git a/vendor/github.com/Microsoft/hcsshim/osversion/osversion.go b/vendor/github.com/Microsoft/hcsshim/osversion/osversion.go new file mode 100644 index 0000000000..916950c023 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/osversion/osversion.go @@ -0,0 +1,51 @@ +package osversion + +import ( + "fmt" + + "golang.org/x/sys/windows" +) + +// OSVersion is a wrapper for Windows version information +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724439(v=vs.85).aspx +type OSVersion struct { + Version uint32 + MajorVersion uint8 + MinorVersion uint8 + Build uint16 +} + +// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx +type osVersionInfoEx struct { + OSVersionInfoSize uint32 + MajorVersion uint32 + MinorVersion uint32 + BuildNumber uint32 + PlatformID uint32 + CSDVersion [128]uint16 + ServicePackMajor uint16 + ServicePackMinor uint16 + SuiteMask uint16 + ProductType byte + Reserve byte +} + +// Get gets the operating system version on Windows. +// The calling application must be manifested to get the correct version information. +func Get() OSVersion { + var err error + osv := OSVersion{} + osv.Version, err = windows.GetVersion() + if err != nil { + // GetVersion never fails. + panic(err) + } + osv.MajorVersion = uint8(osv.Version & 0xFF) + osv.MinorVersion = uint8(osv.Version >> 8 & 0xFF) + osv.Build = uint16(osv.Version >> 16) + return osv +} + +func (osv OSVersion) ToString() string { + return fmt.Sprintf("%d.%d.%d", osv.MajorVersion, osv.MinorVersion, osv.Build) +} diff --git a/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go b/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go new file mode 100644 index 0000000000..2d9567f6f0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/osversion/windowsbuilds.go @@ -0,0 +1,10 @@ +package osversion + +const ( + + // RS2 was a client-only release in case you're asking why it's not in the list. + RS1 = 14393 + RS3 = 16299 + RS4 = 17134 + RS5 = 17763 +) diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/LICENSE b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/LICENSE new file mode 100644 index 0000000000..261eeb9e9f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/NOTICE b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/NOTICE new file mode 100644 index 0000000000..5f9d59f13c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/NOTICE @@ -0,0 +1,22 @@ +go-runhcs is a fork of go-runc + +The following is runc's legal notice. + +--- + +runc + +Copyright 2012-2015 Docker, Inc. + +This product includes software developed at Docker, Inc. (http://www.docker.com). + +The following is courtesy of our legal counsel: + +Use and transfer of Docker may be subject to certain restrictions by the +United States and other governments. +It is your responsibility to ensure that your use and/or transfer does not +violate applicable laws. + +For more information, please see http://www.bis.doc.gov + +See also http://www.apache.org/dev/crypto.html and/or seek legal counsel. \ No newline at end of file diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs.go new file mode 100644 index 0000000000..5d5205abdb --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs.go @@ -0,0 +1,160 @@ +package runhcs + +import ( + "bytes" + "context" + "fmt" + "os" + "os/exec" + "path/filepath" + "strings" + "sync" + "sync/atomic" + + irunhcs "github.com/Microsoft/hcsshim/internal/runhcs" + "github.com/containerd/go-runc" +) + +// Format is the type of log formatting options available. +type Format string + +const ( + none Format = "" + // Text is the default text log ouput. + Text Format = "text" + // JSON is the JSON formatted log output. + JSON Format = "json" +) + +var runhcsPath atomic.Value + +func getCommandPath() string { + const command = "runhcs.exe" + + pathi := runhcsPath.Load() + if pathi == nil { + path, err := exec.LookPath(command) + if err != nil { + // Failed to look up command just use it directly and let the + // Windows loader find it. + path = command + runhcsPath.Store(path) + return path + } + apath, err := filepath.Abs(path) + if err != nil { + // We couldnt make `path` an `AbsPath`. Just use `path` directly and + // let the Windows loader find it. + apath = path + } + runhcsPath.Store(apath) + return apath + } + return pathi.(string) +} + +var bytesBufferPool = sync.Pool{ + New: func() interface{} { + return bytes.NewBuffer(nil) + }, +} + +func getBuf() *bytes.Buffer { + return bytesBufferPool.Get().(*bytes.Buffer) +} + +func putBuf(b *bytes.Buffer) { + b.Reset() + bytesBufferPool.Put(b) +} + +// Runhcs is the client to the runhcs cli +type Runhcs struct { + // Debug enables debug output for logging. + Debug bool + // Log sets the log file path or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs-log) where internal debug information is written. + Log string + // LogFormat sets the format used by logs. + LogFormat Format + // Owner sets the compute system owner property. + Owner string + // Root is the registry key root for storage of runhcs container state. + Root string +} + +func (r *Runhcs) args() []string { + var out []string + if r.Debug { + out = append(out, "--debug") + } + if r.Log != "" { + if strings.HasPrefix(r.Log, irunhcs.SafePipePrefix) { + out = append(out, "--log", r.Log) + } else { + abs, err := filepath.Abs(r.Log) + if err == nil { + out = append(out, "--log", abs) + } + } + } + if r.LogFormat != none { + out = append(out, "--log-format", string(r.LogFormat)) + } + if r.Owner != "" { + out = append(out, "--owner", r.Owner) + } + if r.Root != "" { + out = append(out, "--root", r.Root) + } + return out +} + +func (r *Runhcs) command(context context.Context, args ...string) *exec.Cmd { + cmd := exec.CommandContext(context, getCommandPath(), append(r.args(), args...)...) + cmd.Env = os.Environ() + return cmd +} + +// runOrError will run the provided command. If an error is +// encountered and neither Stdout or Stderr was set the error and the +// stderr of the command will be returned in the format of : +// +func (r *Runhcs) runOrError(cmd *exec.Cmd) error { + if cmd.Stdout != nil || cmd.Stderr != nil { + ec, err := runc.Monitor.Start(cmd) + if err != nil { + return err + } + status, err := runc.Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) + } + return err + } + data, err := cmdOutput(cmd, true) + if err != nil { + return fmt.Errorf("%s: %s", err, data) + } + return nil +} + +func cmdOutput(cmd *exec.Cmd, combined bool) ([]byte, error) { + b := getBuf() + defer putBuf(b) + + cmd.Stdout = b + if combined { + cmd.Stderr = b + } + ec, err := runc.Monitor.Start(cmd) + if err != nil { + return nil, err + } + + status, err := runc.Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) + } + + return b.Bytes(), err +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create-scratch.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create-scratch.go new file mode 100644 index 0000000000..3b53b39908 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create-scratch.go @@ -0,0 +1,10 @@ +package runhcs + +import ( + "context" +) + +// CreateScratch creates a scratch vhdx at 'destpath' that is ext4 formatted. +func (r *Runhcs) CreateScratch(context context.Context, destpath string) error { + return r.runOrError(r.command(context, "create-scratch", "--destpath", destpath)) +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create-scratch_test.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create-scratch_test.go new file mode 100644 index 0000000000..4ce8c3d020 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create-scratch_test.go @@ -0,0 +1,65 @@ +// +build integration + +package runhcs + +import ( + "context" + "io/ioutil" + "os" + "path/filepath" + "testing" +) + +func Test_CreateScratch_EmptyDestpath_Fail(t *testing.T) { + rhcs := Runhcs{ + Debug: true, + } + + ctx := context.TODO() + err := rhcs.CreateScratch(ctx, "") + if err == nil { + t.Fatal("Should have failed 'CreateScratch' command.") + } +} + +func Test_CreateScratch_DirDestpath_Failure(t *testing.T) { + rhcs := Runhcs{ + Debug: true, + } + + td, err := ioutil.TempDir("", "CreateScratch") + if err != nil { + t.Fatal(err) + } + defer os.Remove(td) + + ctx := context.TODO() + err = rhcs.CreateScratch(ctx, td) + if err == nil { + t.Fatal("Should have failed 'CreateScratch' command with dir destpath") + } +} + +func Test_CreateScratch_ValidDestpath_Success(t *testing.T) { + rhcs := Runhcs{ + Debug: true, + } + + td, err := ioutil.TempDir("", "CreateScratch") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(td) + + scratchPath := filepath.Join(td, "scratch.vhdx") + + ctx := context.TODO() + err = rhcs.CreateScratch(ctx, scratchPath) + if err != nil { + t.Fatalf("Failed 'CreateScratch' command with: %v", err) + } + _, err = os.Stat(scratchPath) + if err != nil { + t.Fatalf("Failed to stat scratch path with: %v", err) + } +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create.go new file mode 100644 index 0000000000..b10001e4b2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_create.go @@ -0,0 +1,101 @@ +package runhcs + +import ( + "context" + "fmt" + "path/filepath" + "strings" + + irunhcs "github.com/Microsoft/hcsshim/internal/runhcs" + runc "github.com/containerd/go-runc" +) + +// CreateOpts is set of options that can be used with the Create command. +type CreateOpts struct { + runc.IO + // PidFile is the path to the file to write the process id to. + PidFile string + // ShimLog is the path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs--shim-log) for the launched shim process. + ShimLog string + // VMLog is the path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs--vm-log) for the launched VM shim process. + VMLog string + // VMConsole is the path to the pipe for the VM's console (e.g. \\.\pipe\debugpipe) + VMConsole string +} + +func (opt *CreateOpts) args() ([]string, error) { + var out []string + if opt.PidFile != "" { + abs, err := filepath.Abs(opt.PidFile) + if err != nil { + return nil, err + } + out = append(out, "--pid-file", abs) + } + if opt.ShimLog != "" { + if strings.HasPrefix(opt.ShimLog, irunhcs.SafePipePrefix) { + out = append(out, "--shim-log", opt.ShimLog) + } else { + abs, err := filepath.Abs(opt.ShimLog) + if err != nil { + return nil, err + } + out = append(out, "--shim-log", abs) + } + } + if opt.VMLog != "" { + if strings.HasPrefix(opt.VMLog, irunhcs.SafePipePrefix) { + out = append(out, "--vm-log", opt.VMLog) + } else { + abs, err := filepath.Abs(opt.VMLog) + if err != nil { + return nil, err + } + out = append(out, "--vm-log", abs) + } + } + if opt.VMConsole != "" { + out = append(out, "--vm-console", opt.VMConsole) + } + return out, nil +} + +// Create creates a new container and returns its pid if it was created +// successfully. +func (r *Runhcs) Create(context context.Context, id, bundle string, opts *CreateOpts) error { + args := []string{"create", "--bundle", bundle} + if opts != nil { + oargs, err := opts.args() + if err != nil { + return err + } + args = append(args, oargs...) + } + cmd := r.command(context, append(args, id)...) + if opts != nil && opts.IO != nil { + opts.Set(cmd) + } + if cmd.Stdout == nil && cmd.Stderr == nil { + data, err := cmdOutput(cmd, true) + if err != nil { + return fmt.Errorf("%s: %s", err, data) + } + return nil + } + ec, err := runc.Monitor.Start(cmd) + if err != nil { + return err + } + if opts != nil && opts.IO != nil { + if c, ok := opts.IO.(runc.StartCloser); ok { + if err := c.CloseAfterStart(); err != nil { + return err + } + } + } + status, err := runc.Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) + } + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_delete.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_delete.go new file mode 100644 index 0000000000..08b82bbd9a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_delete.go @@ -0,0 +1,33 @@ +package runhcs + +import ( + "context" +) + +// DeleteOpts is set of options that can be used with the Delete command. +type DeleteOpts struct { + // Force forcibly deletes the container if it is still running (uses SIGKILL). + Force bool +} + +func (opt *DeleteOpts) args() ([]string, error) { + var out []string + if opt.Force { + out = append(out, "--force") + } + return out, nil +} + +// Delete any resources held by the container often used with detached +// containers. +func (r *Runhcs) Delete(context context.Context, id string, opts *DeleteOpts) error { + args := []string{"delete"} + if opts != nil { + oargs, err := opts.args() + if err != nil { + return err + } + args = append(args, oargs...) + } + return r.runOrError(r.command(context, append(args, id)...)) +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_e2e_matrix_test.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_e2e_matrix_test.go new file mode 100644 index 0000000000..bbe4657850 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_e2e_matrix_test.go @@ -0,0 +1,390 @@ +// +build integration + +package runhcs + +import ( + "bytes" + "context" + "encoding/json" + "io" + "io/ioutil" + "os" + "os/exec" + "path/filepath" + "strconv" + "syscall" + "testing" + + "github.com/Microsoft/go-winio/vhd" + testutilities "github.com/Microsoft/hcsshim/functional/utilities" + "github.com/Microsoft/hcsshim/osversion" + runc "github.com/containerd/go-runc" + "github.com/opencontainers/runtime-tools/generate" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" +) + +// These support matrix of runhcs.exe for end to end activations is quite +// complex. These tests attempt to codify a simple start test on each support +// host/guest/isolation type so that we can have at least minimal confidence +// when changing code that activations across all platforms still work. +// +// Host OS | Container OS | Isolation +// +// RS1 | RS1 | V1 - Argon, Xenon +// +// RS3 | RS1 | V1 - Xenon +// | RS3 | V1 - Argon, Xenon +// +// RS4 | RS1, RS3 | V1 - Xenon +// | RS4 | V1 - Argon, Xenon +// +// RS5 | RS1, RS3, RS4 | V2 - UVM + Argon +// | RS5 | V2 - Argon, UVM + Argon, UVM + Argon (s) (POD's) +// | LCOW | V2 - UVM + Linux Container, UVM + Linux Container (s) (POD's) + +var _ = (runc.IO)(&testIO{}) + +type testIO struct { + g *errgroup.Group + + or, ow *os.File + outBuff *bytes.Buffer + + er, ew *os.File + errBuff *bytes.Buffer +} + +func newTestIO(t *testing.T) *testIO { + var err error + tio := &testIO{ + outBuff: &bytes.Buffer{}, + errBuff: &bytes.Buffer{}, + } + defer func() { + if err != nil { + tio.Close() + } + }() + + r, w, err := os.Pipe() + if err != nil { + t.Fatalf("failed to create stdout pipes: %v", err) + } + tio.or, tio.ow = r, w + r, w, err = os.Pipe() + if err != nil { + t.Fatalf("failed to create stderr pipes: %v", err) + } + tio.er, tio.ew = r, w + + g, _ := errgroup.WithContext(context.TODO()) + tio.g = g + tio.g.Go(func() error { + _, err := io.Copy(tio.outBuff, tio.Stdout()) + return err + }) + tio.g.Go(func() error { + _, err := io.Copy(tio.errBuff, tio.Stderr()) + return err + }) + return tio +} + +func (t *testIO) Stdin() io.WriteCloser { + return nil +} + +func (t *testIO) Stdout() io.ReadCloser { + return t.or +} + +func (t *testIO) Stderr() io.ReadCloser { + return t.er +} + +func (t *testIO) Set(cmd *exec.Cmd) { + cmd.Stdout = t.ow + cmd.Stderr = t.ew +} + +func (t *testIO) Close() error { + var err error + for _, v := range []*os.File{ + t.ow, t.ew, + t.or, t.er, + } { + if cerr := v.Close(); err == nil { + err = cerr + } + } + return err +} + +func (t *testIO) CloseAfterStart() error { + t.ow.Close() + t.ew.Close() + return nil +} + +func (t *testIO) Wait() error { + return t.g.Wait() +} + +func getWindowsImageNameByVersion(t *testing.T, bv int) string { + switch bv { + case osversion.RS1: + return "mcr.microsoft.com/windows/nanoserver:sac2016" + case osversion.RS3: + return "mcr.microsoft.com/windows/nanoserver:1709" + case osversion.RS4: + return "mcr.microsoft.com/windows/nanoserver:1803" + case osversion.RS5: + // testImage = "mcr.microsoft.com/windows/nanoserver:1809" + return "mcr.microsoft.com/windows/nanoserver/insider:10.0.17763.55" + default: + t.Fatalf("unsupported build (%d) for Windows containers", bv) + } + // Won't hit because of t.Fatal + return "" +} + +func readPidFile(path string) (int, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return -1, errors.Wrap(err, "failed to read pidfile") + } + p, err := strconv.Atoi(string(data)) + if err != nil { + return -1, errors.Wrap(err, "pidfile failed to parse pid") + } + return p, nil +} + +func testWindows(t *testing.T, version int, isolated bool) { + var err error + + // Make the bundle + bundle := testutilities.CreateTempDir(t) + defer func() { + if err == nil { + os.RemoveAll(bundle) + } else { + t.Errorf("additional logs at bundle path: %v", bundle) + } + }() + scratch := testutilities.CreateTempDir(t) + defer func() { + vhd.DetachVhd(filepath.Join(scratch, "sandbox.vhdx")) + os.RemoveAll(scratch) + }() + + // Generate the Spec + g, err := generate.New("windows") + if err != nil { + t.Errorf("failed to generate Windows config with error: %v", err) + return + } + g.SetProcessArgs([]string{"cmd", "/c", "echo Hello World!"}) + if isolated { + g.SetWindowsHypervUntilityVMPath("") + } + g.Config.Windows.Network = nil + + // Get the LayerFolders + imageName := getWindowsImageNameByVersion(t, version) + layers := testutilities.LayerFolders(t, imageName) + for _, layer := range layers { + g.AddWindowsLayerFolders(layer) + } + g.AddWindowsLayerFolders(scratch) + + cf, err := os.Create(filepath.Join(bundle, "config.json")) + if err != nil { + t.Errorf("failed to create config.json with error: %v", err) + return + } + err = json.NewEncoder(cf).Encode(g.Config) + if err != nil { + cf.Close() + t.Errorf("failed to encode config.json with error: %v", err) + return + } + cf.Close() + + // Create the Argon, Xenon, or UVM + ctx := context.TODO() + rhcs := Runhcs{ + Debug: true, + } + tio := newTestIO(t) + defer func() { + if err != nil { + t.Errorf("additional info stdout: '%v', stderr: '%v'", tio.outBuff.String(), tio.errBuff.String()) + } + }() + defer func() { + tio.Close() + }() + copts := &CreateOpts{ + IO: tio, + PidFile: filepath.Join(bundle, "pid-file.txt"), + ShimLog: filepath.Join(bundle, "shim-log.txt"), + } + if isolated { + copts.VMLog = filepath.Join(bundle, "vm-log.txt") + } + err = rhcs.Create(ctx, t.Name(), bundle, copts) + if err != nil { + t.Errorf("failed to create container with error: %v", err) + return + } + defer func() { + rhcs.Delete(ctx, t.Name(), &DeleteOpts{Force: true}) + }() + + // Find the shim/vmshim process and begin exit wait + pid, err := readPidFile(copts.PidFile) + if err != nil { + t.Errorf("failed to read pidfile with error: %v", err) + return + } + p, err := os.FindProcess(pid) + if err != nil { + t.Errorf("failed to find container process by pid: %d, with error: %v", pid, err) + return + } + + // Start the container + err = rhcs.Start(ctx, t.Name()) + if err != nil { + t.Errorf("failed to start container with error: %v", err) + return + } + defer func() { + if err != nil { + rhcs.Kill(ctx, t.Name(), "CtrlC") + } + }() + + // Wait for process exit, verify the exited state + var exitStatus int + _, eerr := p.Wait() + if eerr != nil { + if exitErr, ok := eerr.(*exec.ExitError); ok { + if ws, ok := exitErr.Sys().(syscall.WaitStatus); ok { + exitStatus = ws.ExitStatus() + } + } + } + if exitStatus != 0 { + err = eerr + t.Errorf("container process failed with exit status: %d", exitStatus) + return + } + + // Wait for the relay to exit + tio.Wait() + outString := tio.outBuff.String() + if outString != "Hello World!\r\n" { + t.Errorf("stdout expected: 'Hello World!', got: '%v'", outString) + } + + errString := tio.errBuff.String() + if errString != "" { + t.Errorf("stderr expected: '', got: '%v'", errString) + } +} + +func testWindowsPod(t *testing.T, version int, isolated bool) { + t.Skip("not implemented") +} + +func testLCOW(t *testing.T) { + t.Skip("not implemented") +} + +func testLCOWPod(t *testing.T) { + t.Skip("not implemented") +} + +func Test_RS1_Argon(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS1) + + testWindows(t, osversion.RS1, false) +} + +func Test_RS1_Xenon(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS1) + + testWindows(t, osversion.RS1, true) +} + +func Test_RS3_Argon(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS3) + + testWindows(t, osversion.RS3, false) +} + +func Test_RS3_Xenon(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS3) + + guests := []int{osversion.RS1, osversion.RS3} + for _, g := range guests { + testWindows(t, g, true) + } +} + +func Test_RS4_Argon(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS4) + + testWindows(t, osversion.RS4, false) +} + +func Test_RS4_Xenon(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS4) + + guests := []int{osversion.RS1, osversion.RS3, osversion.RS4} + for _, g := range guests { + testWindows(t, g, true) + } +} + +func Test_RS5_Argon(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS5) + + testWindows(t, osversion.RS5, false) +} + +func Test_RS5_ArgonPods(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS5) + + testWindowsPod(t, osversion.RS5, false) +} + +func Test_RS5_UVMAndContainer(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS5) + + guests := []int{osversion.RS1, osversion.RS3, osversion.RS4, osversion.RS5} + for _, g := range guests { + testWindows(t, g, true) + } +} + +func Test_RS5_UVMPods(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS5) + + testWindowsPod(t, osversion.RS5, true) +} + +func Test_RS5_LCOW(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS5) + + testLCOW(t) +} + +func Test_RS5_LCOW_UVMPods(t *testing.T) { + testutilities.RequiresExactBuild(t, osversion.RS5) + + testLCOWPod(t) +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_exec.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_exec.go new file mode 100644 index 0000000000..090a0a31f8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_exec.go @@ -0,0 +1,88 @@ +package runhcs + +import ( + "context" + "fmt" + "path/filepath" + "strings" + + irunhcs "github.com/Microsoft/hcsshim/internal/runhcs" + "github.com/containerd/go-runc" +) + +// ExecOpts is set of options that can be used with the Exec command. +type ExecOpts struct { + runc.IO + // Detach from the container's process. + Detach bool + // PidFile is the path to the file to write the process id to. + PidFile string + // ShimLog is the path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs---log) for the launched shim process. + ShimLog string +} + +func (opt *ExecOpts) args() ([]string, error) { + var out []string + if opt.Detach { + out = append(out, "--detach") + } + if opt.PidFile != "" { + abs, err := filepath.Abs(opt.PidFile) + if err != nil { + return nil, err + } + out = append(out, "--pid-file", abs) + } + if opt.ShimLog != "" { + if strings.HasPrefix(opt.ShimLog, irunhcs.SafePipePrefix) { + out = append(out, "--shim-log", opt.ShimLog) + } else { + abs, err := filepath.Abs(opt.ShimLog) + if err != nil { + return nil, err + } + out = append(out, "--shim-log", abs) + } + } + return out, nil +} + +// Exec executes an additional process inside the container based on the +// oci.Process spec found at processFile. +func (r *Runhcs) Exec(context context.Context, id, processFile string, opts *ExecOpts) error { + args := []string{"exec", "--process", processFile} + if opts != nil { + oargs, err := opts.args() + if err != nil { + return err + } + args = append(args, oargs...) + } + cmd := r.command(context, append(args, id)...) + if opts != nil && opts.IO != nil { + opts.Set(cmd) + } + if cmd.Stdout == nil && cmd.Stderr == nil { + data, err := cmdOutput(cmd, true) + if err != nil { + return fmt.Errorf("%s: %s", err, data) + } + return nil + } + ec, err := runc.Monitor.Start(cmd) + if err != nil { + return err + } + if opts != nil && opts.IO != nil { + if c, ok := opts.IO.(runc.StartCloser); ok { + if err := c.CloseAfterStart(); err != nil { + return err + } + } + } + status, err := runc.Monitor.Wait(cmd, ec) + if err == nil && status != 0 { + err = fmt.Errorf("%s did not terminate sucessfully", cmd.Args[0]) + } + return err +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_integration_test.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_integration_test.go new file mode 100644 index 0000000000..12922e089a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_integration_test.go @@ -0,0 +1,7 @@ +// +build integration + +package runhcs + +import ( + _ "github.com/Microsoft/hcsshim/functional/manifest" +) diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_kill.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_kill.go new file mode 100644 index 0000000000..021e5b16fe --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_kill.go @@ -0,0 +1,11 @@ +package runhcs + +import ( + "context" +) + +// Kill sends the specified signal (default: SIGTERM) to the container's init +// process. +func (r *Runhcs) Kill(context context.Context, id, signal string) error { + return r.runOrError(r.command(context, "kill", id, signal)) +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_list.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_list.go new file mode 100644 index 0000000000..3b9208017f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_list.go @@ -0,0 +1,28 @@ +package runhcs + +import ( + "context" + "encoding/json" + + irunhcs "github.com/Microsoft/hcsshim/internal/runhcs" +) + +// ContainerState is the representation of the containers state at the moment of +// query. +type ContainerState = irunhcs.ContainerState + +// List containers started by runhcs. +// +// Note: This is specific to the Runhcs.Root namespace provided in the global +// settings. +func (r *Runhcs) List(context context.Context) ([]*ContainerState, error) { + data, err := cmdOutput(r.command(context, "list", "--format=json"), false) + if err != nil { + return nil, err + } + var out []*ContainerState + if err := json.Unmarshal(data, &out); err != nil { + return nil, err + } + return out, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_list_test.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_list_test.go new file mode 100644 index 0000000000..c393f40c54 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_list_test.go @@ -0,0 +1,23 @@ +// +build integration + +package runhcs + +import ( + "context" + "testing" +) + +func Test_List_NoContainers(t *testing.T) { + rhcs := Runhcs{ + Debug: true, + } + + ctx := context.TODO() + cs, err := rhcs.List(ctx) + if err != nil { + t.Fatalf("Failed 'List' command with: %v", err) + } + if len(cs) != 0 { + t.Fatalf("Length of ContainerState array expected: 0, actual: %d", len(cs)) + } +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_pause.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_pause.go new file mode 100644 index 0000000000..56392fa43e --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_pause.go @@ -0,0 +1,10 @@ +package runhcs + +import ( + "context" +) + +// Pause suspends all processes inside the container. +func (r *Runhcs) Pause(context context.Context, id string) error { + return r.runOrError(r.command(context, "pause", id)) +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_ps.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_ps.go new file mode 100644 index 0000000000..4dc9f144fe --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_ps.go @@ -0,0 +1,20 @@ +package runhcs + +import ( + "context" + "encoding/json" + "fmt" +) + +// Ps displays the processes running inside a container. +func (r *Runhcs) Ps(context context.Context, id string) ([]int, error) { + data, err := cmdOutput(r.command(context, "ps", "--format=json", id), true) + if err != nil { + return nil, fmt.Errorf("%s: %s", err, data) + } + var out []int + if err := json.Unmarshal(data, &out); err != nil { + return nil, err + } + return out, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resize-tty.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resize-tty.go new file mode 100644 index 0000000000..b9f90491d3 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resize-tty.go @@ -0,0 +1,33 @@ +package runhcs + +import ( + "context" + "strconv" +) + +// ResizeTTYOpts is set of options that can be used with the ResizeTTY command. +type ResizeTTYOpts struct { + // Pid is the process pid (defaults to init pid). + Pid *int +} + +func (opt *ResizeTTYOpts) args() ([]string, error) { + var out []string + if opt.Pid != nil { + out = append(out, "--pid", strconv.Itoa(*opt.Pid)) + } + return out, nil +} + +// ResizeTTY updates the terminal size for a container process. +func (r *Runhcs) ResizeTTY(context context.Context, id string, width, height uint16, opts *ResizeTTYOpts) error { + args := []string{"resize-tty"} + if opts != nil { + oargs, err := opts.args() + if err != nil { + return err + } + args = append(args, oargs...) + } + return r.runOrError(r.command(context, append(args, id, strconv.FormatUint(uint64(width), 10), strconv.FormatUint(uint64(height), 10))...)) +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resume.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resume.go new file mode 100644 index 0000000000..1fdeb87d9b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_resume.go @@ -0,0 +1,10 @@ +package runhcs + +import ( + "context" +) + +// Resume resumes all processes that have been previously paused. +func (r *Runhcs) Resume(context context.Context, id string) error { + return r.runOrError(r.command(context, "resume", id)) +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_start.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_start.go new file mode 100644 index 0000000000..ad3df746a8 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_start.go @@ -0,0 +1,10 @@ +package runhcs + +import ( + "context" +) + +// Start will start an already created container. +func (r *Runhcs) Start(context context.Context, id string) error { + return r.runOrError(r.command(context, "start", id)) +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_state.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_state.go new file mode 100644 index 0000000000..b22bb079cc --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_state.go @@ -0,0 +1,20 @@ +package runhcs + +import ( + "context" + "encoding/json" + "fmt" +) + +// State outputs the state of a container. +func (r *Runhcs) State(context context.Context, id string) (*ContainerState, error) { + data, err := cmdOutput(r.command(context, "state", id), true) + if err != nil { + return nil, fmt.Errorf("%s: %s", err, data) + } + var out ContainerState + if err := json.Unmarshal(data, &out); err != nil { + return nil, err + } + return &out, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_test.go b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_test.go new file mode 100644 index 0000000000..bdbae1bffb --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/pkg/go-runhcs/runhcs_test.go @@ -0,0 +1,68 @@ +package runhcs + +import ( + "os" + "path/filepath" + "sync/atomic" + "testing" +) + +func resetRunhcsPath() { + runhcsPath = atomic.Value{} +} + +func TestGetCommandPath_NoLookPath(t *testing.T) { + resetRunhcsPath() + + path := getCommandPath() + if path != "runhcs.exe" { + t.Fatalf("expected path 'runhcs.exe' got '%s'", path) + } + pathi := runhcsPath.Load() + if pathi == nil { + t.Fatal("cache state should be set after first query") + } + if path != pathi.(string) { + t.Fatalf("expected: '%s' in cache got '%s'", path, pathi.(string)) + } +} + +func TestGetCommandPath_WithLookPath(t *testing.T) { + resetRunhcsPath() + + wd, err := os.Getwd() + if err != nil { + t.Fatalf("failed to get cwd with err: %v", err) + } + fakePath := filepath.Join(wd, "runhcs.exe") + f, err := os.Create(fakePath) + if err != nil { + t.Fatalf("failed to create fake runhcs.exe in path with err: %v", err) + } + f.Close() + defer os.Remove(fakePath) + + path := getCommandPath() + if path != fakePath { + t.Fatalf("expected fake path '%s' got '%s'", fakePath, path) + } + pathi := runhcsPath.Load() + if pathi == nil { + t.Fatal("cache state should be set after first query") + } + if path != pathi.(string) { + t.Fatalf("expected: '%s' in cache got '%s'", fakePath, pathi.(string)) + } +} + +func TestGetCommandPath_WithCache(t *testing.T) { + resetRunhcsPath() + + value := "this is a test" + runhcsPath.Store(value) + + path := getCommandPath() + if path != value { + t.Fatalf("expected fake cached path: '%s' got '%s'", value, path) + } +} diff --git a/vendor/github.com/Microsoft/hcsshim/tools/uvmboot/main.go b/vendor/github.com/Microsoft/hcsshim/tools/uvmboot/main.go new file mode 100644 index 0000000000..627e004bfe --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/tools/uvmboot/main.go @@ -0,0 +1,269 @@ +package main + +import ( + "fmt" + "io" + "os" + "strings" + "sync" + "time" + + "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/uvm" + "github.com/sirupsen/logrus" + "github.com/urfave/cli" +) + +const ( + kernelArgsArgName = "kernel-args" + rootFSTypeArgName = "root-fs-type" + vpMemMaxCountArgName = "vpmem-max-count" + vpMemMaxSizeArgName = "vpmem-max-size" + cpusArgName = "cpus" + memoryArgName = "memory" + allowOvercommitArgName = "allow-overcommit" + enableDeferredCommitArgName = "enable-deferred-commit" + measureArgName = "measure" + parallelArgName = "parallel" + countArgName = "count" + kernelDirectArgName = "kernel-direct" + execCommandLineArgName = "exec" + forwardStdoutArgName = "fwd-stdout" + forwardStderrArgName = "fwd-stderr" + debugArgName = "debug" + outputHandlingArgName = "output-handling" +) + +func main() { + app := cli.NewApp() + app.Name = "uvmboot" + app.Usage = "Boot a utility VM" + + app.Flags = []cli.Flag{ + cli.Uint64Flag{ + Name: cpusArgName, + Usage: "Number of CPUs on the UVM. Uses hcsshim default if not specified", + }, + cli.UintFlag{ + Name: memoryArgName, + Usage: "Amount of memory on the UVM, in MB. Uses hcsshim default if not specified", + }, + cli.BoolFlag{ + Name: measureArgName, + Usage: "Measure wall clock time of the UVM run", + }, + cli.IntFlag{ + Name: parallelArgName, + Value: 1, + Usage: "Number of UVMs to boot in parallel", + }, + cli.IntFlag{ + Name: countArgName, + Value: 1, + Usage: "Total number of UVMs to run", + }, + cli.BoolFlag{ + Name: allowOvercommitArgName, + Usage: "Allow memory overcommit on the UVM", + }, + cli.BoolFlag{ + Name: enableDeferredCommitArgName, + Usage: "Enable deferred commit on the UVM", + }, + cli.BoolFlag{ + Name: debugArgName, + Usage: "Enable debug level logging in HCSShim", + }, + } + + app.Commands = []cli.Command{ + { + Name: "lcow", + Usage: "Boot an LCOW UVM", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: kernelArgsArgName, + Value: "", + Usage: "Additional arguments to pass to the kernel", + }, + cli.StringFlag{ + Name: rootFSTypeArgName, + Usage: "Either 'initrd' or 'vhd'. Uses hcsshim default if not specified", + }, + cli.UintFlag{ + Name: vpMemMaxCountArgName, + Usage: "Number of VPMem devices on the UVM. Uses hcsshim default if not specified", + }, + cli.Uint64Flag{ + Name: vpMemMaxSizeArgName, + Usage: "Size of each VPMem device, in MB. Uses hcsshim default if not specified", + }, + cli.BoolFlag{ + Name: kernelDirectArgName, + Usage: "Use kernel direct booting for UVM", + }, + cli.StringFlag{ + Name: execCommandLineArgName, + Usage: "Command to execute in the UVM.", + }, + cli.BoolFlag{ + Name: forwardStdoutArgName, + Usage: "Whether stdout from the process in the UVM should be forwarded", + }, + cli.BoolFlag{ + Name: forwardStderrArgName, + Usage: "Whether stderr from the process in the UVM should be forwarded", + }, + cli.StringFlag{ + Name: outputHandlingArgName, + Usage: "Controls how output from UVM is handled. Use 'stdout' to print all output to stdout", + }, + }, + Action: func(c *cli.Context) error { + if c.GlobalBool("debug") { + logrus.SetLevel(logrus.DebugLevel) + } + + parallelCount := c.GlobalInt(parallelArgName) + + var wg sync.WaitGroup + wg.Add(parallelCount) + + workChan := make(chan int) + + runFunc := func(workChan <-chan int) { + for { + i, ok := <-workChan + + if !ok { + wg.Done() + return + } + + id := fmt.Sprintf("uvmboot-%d", i) + + options := uvm.OptionsLCOW{ + Options: &uvm.Options{ + ID: id, + }, + } + + { + val := false + options.UseGuestConnection = &val + } + + if c.GlobalIsSet(cpusArgName) { + options.ProcessorCount = int32(c.GlobalUint64(cpusArgName)) + } + if c.GlobalIsSet(memoryArgName) { + options.MemorySizeInMB = int32(c.GlobalUint64(memoryArgName)) + } + if c.GlobalIsSet(allowOvercommitArgName) { + val := c.GlobalBool(allowOvercommitArgName) + options.AllowOvercommit = &val + } + if c.GlobalIsSet(enableDeferredCommitArgName) { + val := c.GlobalBool(enableDeferredCommitArgName) + options.EnableDeferredCommit = &val + } + + if c.IsSet(kernelDirectArgName) { + options.KernelDirect = c.Bool(kernelDirectArgName) + } + if c.IsSet(rootFSTypeArgName) { + switch strings.ToLower(c.String(rootFSTypeArgName)) { + case "initrd": + val := uvm.PreferredRootFSTypeInitRd + options.PreferredRootFSType = &val + case "vhd": + val := uvm.PreferredRootFSTypeVHD + options.PreferredRootFSType = &val + default: + logrus.Fatalf("Unrecognized value '%s' for option %s", c.String(rootFSTypeArgName), rootFSTypeArgName) + } + } + if c.IsSet(kernelArgsArgName) { + options.KernelBootOptions = c.String(kernelArgsArgName) + } + if c.IsSet(vpMemMaxCountArgName) { + val := uint32(c.Uint(vpMemMaxCountArgName)) + options.VPMemDeviceCount = &val + } + if c.IsSet(vpMemMaxSizeArgName) { + val := c.Uint64(vpMemMaxSizeArgName) * 1024 * 1024 // convert from MB to bytes + options.VPMemSizeBytes = &val + } + if c.IsSet(execCommandLineArgName) { + options.ExecCommandLine = c.String(execCommandLineArgName) + } + if c.IsSet(forwardStdoutArgName) { + val := c.Bool(forwardStdoutArgName) + options.ForwardStdout = &val + } + if c.IsSet(forwardStderrArgName) { + val := c.Bool(forwardStderrArgName) + options.ForwardStderr = &val + } + if c.IsSet(outputHandlingArgName) { + switch strings.ToLower(c.String(outputHandlingArgName)) { + case "stdout": + val := uvm.OutputHandler(func(r io.Reader) { io.Copy(os.Stdout, r) }) + options.OutputHandler = &val + default: + logrus.Fatalf("Unrecognized value '%s' for option %s", c.String(outputHandlingArgName), outputHandlingArgName) + } + } + + if err := run(&options); err != nil { + logrus.WithField("uvm-id", id).Error(err) + } + } + } + + for i := 0; i < parallelCount; i++ { + go runFunc(workChan) + } + + start := time.Now() + + for i := 0; i < c.GlobalInt(countArgName); i++ { + workChan <- i + } + + close(workChan) + + wg.Wait() + + if c.GlobalBool(measureArgName) { + fmt.Println("Elapsed time:", time.Since(start)) + } + + return nil + }, + }, + } + + err := app.Run(os.Args) + if err != nil { + logrus.Fatal(err) + } +} + +func run(options *uvm.OptionsLCOW) error { + uvm, err := uvm.CreateLCOW(options) + if err != nil { + return err + } + defer uvm.Close() + + if err := uvm.Start(); err != nil { + return err + } + + if err := uvm.WaitExpectedError(hcs.ErrVmcomputeUnexpectedExit); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/tools/uvmboot/resource_windows_386.syso b/vendor/github.com/Microsoft/hcsshim/tools/uvmboot/resource_windows_386.syso new file mode 100644 index 0000000000000000000000000000000000000000..b4320575e4c5c95bfe35813298ae82a61e87fa35 GIT binary patch literal 968 zcma)5!HU#C5UuPE$O?joJ$P(`*Pcmq1~W@$M?@C}#9iEVJW40&>@+0ZA)OgrMA%R8 z3&bxF5wCuee;~dj6UaIqtfacCt6sgTbdnd2L$!S0Cu+TcGc0WxH_>*9kPG2{hOK1} z`lPw*T_<~V7cL{Zz6W~-3)2&);Sc^1KN#!;QP<*EJ1K{1{3js3Ko8rY;Sv3S|FyS2 zbjFA->73qTeNM07E$Eb9V!yz80>74@gI~Yfr|*heJ8!Z?16*BZ8=?x11HOt%Ql#k; zY>G0HVAhn9K^|uSj7MYWw6RaI5(RXC_!%kewVoU(Vdu7biS$ znXQCQL}s*{xmVJ<^W!_WLt(AVqwI=-K--zSF7=U(S27ot=PE9ZHEG3TlOGA24>!Kc za-mf!ZME!o0~LdWiL`O4iV9;KmutO>Z8+-3wlMG&qEeB{RP`UNZ@}vzw(Xjhq9`&I zgAFZP`Z`=oc%O6jM(M=7v&{Dx=YIq2x+qLp$>j8$Es~l0A&_4BSojZlltz#EIF$j9 zreea!L7GfF;ZMX=d~`=Wo^Z4~KsdTFG0;G54;q_myw6&0HQ#&d_5aTLtK4ti`X87o Bw2uG) literal 0 HcmV?d00001 diff --git a/vendor/github.com/Microsoft/hcsshim/tools/uvmboot/resource_windows_amd64.syso b/vendor/github.com/Microsoft/hcsshim/tools/uvmboot/resource_windows_amd64.syso new file mode 100644 index 0000000000000000000000000000000000000000..d6100a85b94087599ac086568b810e2fba5e51c1 GIT binary patch literal 968 zcma)5!EVz)5FIzU6bXq#4;&YZ>n1TI!l|65LbOsYw4x;((b`@&E7`l2*9lapQcs-u z0>l>}gt+oe`~l+GjwP3HVC>nMoq6+Sytd*-|z1PQP<#CJ1K`M{3js3Ko6Rs;Sv3S|FyF} zaK?x(>4M&3J)u|d7Ia20v0q?4ggKbXJIQnrzWRYuQTZ!mJ1)RBpmzM8qOFHgDe zGFu8Ai_~a2bFZa!=STPN1;SdHh3Pc|fwnVuUFbs_tz;%F&s0ZDHWcMX5rSs`5Wt-+@;{Y}?f>MV_ZB z0vlR3^i{Z$;1TESjnc7sXPGx*oc|55>pV9_DdV#Xwuoo$2VahoXTp2J!z6sl$BFcL zI298<_LF!r65d2i#f>}c@C3u%0fOP3iGc=cd(hb2;(b=qtH1Zg>;9e9SJ|uI`XA_n BwIl!l literal 0 HcmV?d00001 diff --git a/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go index cd471295b8..8bed848573 100644 --- a/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go +++ b/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go @@ -1,4 +1,4 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT +// Code generated mksyscall_windows.exe DO NOT EDIT package hcsshim @@ -6,7 +6,6 @@ import ( "syscall" "unsafe" - "github.com/Microsoft/hcsshim/internal/interop" "golang.org/x/sys/windows" ) @@ -46,7 +45,10 @@ var ( func SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) { r0, _, _ := syscall.Syscall(procSetCurrentThreadCompartmentId.Addr(), 1, uintptr(compartmentId), 0, 0) if int32(r0) < 0 { - hr = interop.Win32FromHresult(r0) + if r0&0x1fff0000 == 0x00070000 { + r0 &= 0xffff + } + hr = syscall.Errno(r0) } return } From ee48092c5e124886287cdda3bf6f02c378fe5665 Mon Sep 17 00:00:00 2001 From: ksubrmnn Date: Fri, 14 Dec 2018 09:58:25 -0800 Subject: [PATCH 2/2] Use remote subnet feature --- backend/vxlan/vxlan_network_windows.go | 86 +++++++++++++++++++------- backend/vxlan/vxlan_windows.go | 65 ++++++++++++++++++- 2 files changed, 125 insertions(+), 26 deletions(-) diff --git a/backend/vxlan/vxlan_network_windows.go b/backend/vxlan/vxlan_network_windows.go index 74c4248a81..c4f1b5e6c4 100644 --- a/backend/vxlan/vxlan_network_windows.go +++ b/backend/vxlan/vxlan_network_windows.go @@ -22,7 +22,10 @@ import ( "github.com/coreos/flannel/backend" "github.com/coreos/flannel/subnet" + "encoding/json" + "github.com/Microsoft/hcsshim/hcn" "github.com/coreos/flannel/pkg/ip" + "net" "strings" ) @@ -32,6 +35,11 @@ type network struct { subnetMgr subnet.Manager } +type vxlanLeaseAttrs struct { + VNI uint16 + VtepMAC hardwareAddr +} + const ( encapOverhead = 50 ) @@ -87,36 +95,66 @@ func (nw *network) handleSubnetEvents(batch []subnet.Event) { continue } - publicIP := leaseAttrs.PublicIP.String() - remoteIP := leaseSubnet.IP + 2 - lastIP := leaseSubnet.Next().IP - 1 + var vxlanAttrs vxlanLeaseAttrs + if err := json.Unmarshal(leaseAttrs.BackendData, &vxlanAttrs); err != nil { + log.Error("error decoding subnet lease JSON: ", err) + continue + } + + hnsnetwork, err := hcn.GetNetworkByName(nw.dev.link.Name) + if err != nil { + log.Errorf("Unable to find network %v, error: %v", nw.dev.link.Name, err) + continue + } + managementIp := event.Lease.Attrs.PublicIP.String() + + networkPolicySettings := hcn.RemoteSubnetRoutePolicySetting{ + IsolationId: 4096, + DistributedRouterMacAddress: net.HardwareAddr(vxlanAttrs.VtepMAC).String(), + ProviderAddress: managementIp, + DestinationPrefix: event.Lease.Subnet.String(), + } + rawJSON, err := json.Marshal(networkPolicySettings) + networkPolicy := hcn.NetworkPolicy{ + Type: hcn.RemoteSubnetRoute, + Settings: rawJSON, + } + + policyNetworkRequest := hcn.PolicyNetworkRequest{ + Policies: []hcn.NetworkPolicy{networkPolicy}, + } switch event.Type { case subnet.EventAdded: - for ; remoteIP < lastIP; remoteIP++ { - n := &neighbor{ - IP: remoteIP, - MAC: nw.dev.ConjureMac(remoteIP), - ManagementAddress: publicIP, - } - - log.V(2).Infof("adding subnet: %v publicIP: %s vtepMAC: %s", leaseSubnet, n.ManagementAddress, n.MAC) - if err := nw.dev.AddEndpoint(n); err != nil { - log.Error(err) + for _, policy := range hnsnetwork.Policies { + if policy.Type == hcn.RemoteSubnetRoute { + existingPolicySettings := hcn.RemoteSubnetRoutePolicySetting{} + err = json.Unmarshal(policy.Settings, &existingPolicySettings) + if err != nil { + log.Error("Failed to unmarshal settings") + } + if existingPolicySettings.DestinationPrefix == networkPolicySettings.DestinationPrefix { + existingJson, err := json.Marshal(existingPolicySettings) + if err != nil { + log.Error("Failed to marshal settings") + } + existingPolicy := hcn.NetworkPolicy{ + Type: hcn.RemoteSubnetRoute, + Settings: existingJson, + } + existingPolicyNetworkRequest := hcn.PolicyNetworkRequest{ + Policies: []hcn.NetworkPolicy{existingPolicy}, + } + hnsnetwork.RemovePolicy(existingPolicyNetworkRequest) + } } } + if networkPolicySettings.DistributedRouterMacAddress != "" { + hnsnetwork.AddPolicy(policyNetworkRequest) + } case subnet.EventRemoved: - for ; remoteIP < lastIP; remoteIP++ { - n := &neighbor{ - IP: remoteIP, - MAC: nw.dev.ConjureMac(remoteIP), - ManagementAddress: publicIP, - } - - log.V(2).Infof("removing subnet: %v publicIP: %s vtepMAC: %s", leaseSubnet, n.ManagementAddress, n.MAC) - if err := nw.dev.DelEndpoint(n); err != nil { - log.Error(err) - } + if networkPolicySettings.DistributedRouterMacAddress != "" { + hnsnetwork.RemovePolicy(policyNetworkRequest) } default: log.Error("internal error: unknown event type: ", int(event.Type)) diff --git a/backend/vxlan/vxlan_windows.go b/backend/vxlan/vxlan_windows.go index b35d09f407..fd72f4c9f3 100644 --- a/backend/vxlan/vxlan_windows.go +++ b/backend/vxlan/vxlan_windows.go @@ -33,6 +33,8 @@ import ( "golang.org/x/net/context" + "github.com/Microsoft/hcsshim" + "github.com/Microsoft/hcsshim/hcn" "github.com/coreos/flannel/backend" "github.com/coreos/flannel/pkg/ip" "github.com/coreos/flannel/subnet" @@ -62,10 +64,20 @@ func New(sm subnet.Manager, extIface *backend.ExternalInterface) (backend.Backen return backend, nil } -func newSubnetAttrs(publicIP net.IP) (*subnet.LeaseAttrs, error) { +func newSubnetAttrs(publicIP net.IP, vnid uint16, mac net.HardwareAddr) (*subnet.LeaseAttrs, error) { + leaseAttrs := &vxlanLeaseAttrs{ + VNI: vnid, + VtepMAC: hardwareAddr(mac), + } + data, err := json.Marshal(&leaseAttrs) + if err != nil { + return nil, err + } + return &subnet.LeaseAttrs{ PublicIP: ip.FromIP(publicIP), BackendType: "vxlan", + BackendData: json.RawMessage(data), }, nil } @@ -111,7 +123,33 @@ func (be *VXLANBackend) RegisterNetwork(ctx context.Context, wg sync.WaitGroup, } log.Infof("VXLAN config: Name=%s MacPrefix=%s VNI=%d Port=%d GBP=%v DirectRouting=%v", cfg.Name, cfg.MacPrefix, cfg.VNI, cfg.Port, cfg.GBP, cfg.DirectRouting) - subnetAttrs, err := newSubnetAttrs(be.extIface.ExtAddr) + hnsNetworks, err := hcsshim.HNSListNetworkRequest("GET", "", "") + if err != nil { + log.Infof("Cannot get HNS networks [%+v]", err) + } + + var remoteDrMac string + for _, hnsnetwork := range hnsNetworks { + if hnsnetwork.ManagementIP == be.extIface.ExtAddr.String() { + hcnnetwork, err := hcn.GetNetworkByID(hnsnetwork.Id) + policies := hcnnetwork.Policies + for _, policy := range policies { + if policy.Type == hcn.DrMacAddress { + policySettings := hcn.DrMacAddressNetworkPolicySetting{} + err = json.Unmarshal(policy.Settings, &policySettings) + if err != nil { + return nil, fmt.Errorf("Failed to unmarshal settings") + } + remoteDrMac = policySettings.Address + } + } + } + } + mac, err := net.ParseMAC(string(remoteDrMac)) + if err != nil { + return nil, err + } + subnetAttrs, err := newSubnetAttrs(be.extIface.ExtAddr, uint16(cfg.VNI), mac) if err != nil { return nil, err } @@ -140,3 +178,26 @@ func (be *VXLANBackend) RegisterNetwork(ctx context.Context, wg sync.WaitGroup, return newNetwork(be.subnetMgr, be.extIface, dev, ip.IP4Net{}, lease) } + +// So we can make it JSON (un)marshalable +type hardwareAddr net.HardwareAddr + +func (hw hardwareAddr) MarshalJSON() ([]byte, error) { + return []byte(fmt.Sprintf("%q", net.HardwareAddr(hw))), nil +} + +func (hw *hardwareAddr) UnmarshalJSON(bytes []byte) error { + if len(bytes) < 2 || bytes[0] != '"' || bytes[len(bytes)-1] != '"' { + return fmt.Errorf("error parsing hardware addr") + } + + bytes = bytes[1 : len(bytes)-1] + + mac, err := net.ParseMAC(string(bytes)) + if err != nil { + return err + } + + *hw = hardwareAddr(mac) + return nil +}