diff --git a/vendor.conf b/vendor.conf index 558065447..a9ffca873 100644 --- a/vendor.conf +++ b/vendor.conf @@ -3,12 +3,12 @@ github.com/blang/semver v3.1.0 github.com/boltdb/bolt v1.3.1 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 -github.com/containerd/console 4d8a41f4ce5b9bae77c41786ea2458330f43f081 -github.com/containerd/containerd b9eeaa1ce83dd9970605ddbd0b35d4d3fa5f87bd -github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b +github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 +github.com/containerd/containerd 1950f791d9225ffe061c77e74e292bcb3c428a04 +github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/go-cni 6d7b509a054a3cb1c35ed1865d4fde2f0cb547cd -github.com/containerd/go-runc edcf3de1f4971445c42d61f20d506b30612aa031 +github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containernetworking/cni v0.6.0 @@ -34,13 +34,13 @@ github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f github.com/json-iterator/go 1.1.5 github.com/matttproud/golang_protobuf_extensions v1.0.0 -github.com/Microsoft/go-winio v0.4.7 -github.com/Microsoft/hcsshim v0.6.11 +github.com/Microsoft/go-winio v0.4.10 +github.com/Microsoft/hcsshim 44c060121b68e8bdc40b411beba551f3b4ee9e55 github.com/modern-go/concurrent 1.0.3 github.com/modern-go/reflect2 1.0.1 github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7 github.com/opencontainers/image-spec v1.0.1 -github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340 +github.com/opencontainers/runc 20aff4f0488c6d4b8df4d85b4f63f1f704c11abd github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce github.com/opencontainers/runtime-tools v0.6.0 github.com/opencontainers/selinux b6fa367ed7f534f9ba25391cc2d467085dbb445a diff --git a/vendor/github.com/Microsoft/go-winio/fileinfo.go b/vendor/github.com/Microsoft/go-winio/fileinfo.go index b1d60abb8..ada2fbab6 100644 --- a/vendor/github.com/Microsoft/go-winio/fileinfo.go +++ b/vendor/github.com/Microsoft/go-winio/fileinfo.go @@ -20,7 +20,8 @@ const ( // FileBasicInfo contains file access time and file attributes information. type FileBasicInfo struct { CreationTime, LastAccessTime, LastWriteTime, ChangeTime syscall.Filetime - FileAttributes uintptr // includes padding + FileAttributes uint32 + pad uint32 // padding } // GetFileBasicInfo retrieves times and attributes for a file. diff --git a/vendor/github.com/Microsoft/go-winio/pipe.go b/vendor/github.com/Microsoft/go-winio/pipe.go index 82cbe7af4..d99eedb64 100644 --- a/vendor/github.com/Microsoft/go-winio/pipe.go +++ b/vendor/github.com/Microsoft/go-winio/pipe.go @@ -15,7 +15,6 @@ import ( //sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe //sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW //sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW -//sys waitNamedPipe(name string, timeout uint32) (err error) = WaitNamedPipeW //sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo //sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW //sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc @@ -121,6 +120,11 @@ func (f *win32MessageBytePipe) Read(b []byte) (int, error) { // zero-byte message, ensure that all future Read() calls // also return EOF. f.readEOF = true + } else if err == syscall.ERROR_MORE_DATA { + // ERROR_MORE_DATA indicates that the pipe's read mode is message mode + // and the message still has more bytes. Treat this as a success, since + // this package presents all named pipes as byte streams. + err = nil } return n, err } @@ -134,12 +138,14 @@ func (s pipeAddress) String() string { } // DialPipe connects to a named pipe by path, timing out if the connection -// takes longer than the specified duration. If timeout is nil, then the timeout -// is the default timeout established by the pipe server. +// takes longer than the specified duration. If timeout is nil, then we use +// a default timeout of 5 seconds. (We do not use WaitNamedPipe.) func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { var absTimeout time.Time if timeout != nil { absTimeout = time.Now().Add(*timeout) + } else { + absTimeout = time.Now().Add(time.Second * 2) } var err error var h syscall.Handle @@ -148,22 +154,13 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { if err != cERROR_PIPE_BUSY { break } - now := time.Now() - var ms uint32 - if absTimeout.IsZero() { - ms = cNMPWAIT_USE_DEFAULT_WAIT - } else if now.After(absTimeout) { - ms = cNMPWAIT_NOWAIT - } else { - ms = uint32(absTimeout.Sub(now).Nanoseconds() / 1000 / 1000) - } - err = waitNamedPipe(path, ms) - if err != nil { - if err == cERROR_SEM_TIMEOUT { - return nil, ErrTimeout - } - break + if time.Now().After(absTimeout) { + return nil, ErrTimeout } + + // Wait 10 msec and try again. This is a rather simplistic + // view, as we always try each 10 milliseconds. + time.Sleep(time.Millisecond * 10) } if err != nil { return nil, &os.PathError{Op: "open", Path: path, Err: err} @@ -175,16 +172,6 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { return nil, err } - var state uint32 - err = getNamedPipeHandleState(h, &state, nil, nil, nil, nil, 0) - if err != nil { - return nil, err - } - - if state&cPIPE_READMODE_MESSAGE != 0 { - return nil, &os.PathError{Op: "open", Path: path, Err: errors.New("message readmode pipes not supported")} - } - f, err := makeWin32File(h) if err != nil { syscall.Close(h) @@ -354,13 +341,23 @@ func ListenPipe(path string, c *PipeConfig) (net.Listener, error) { if err != nil { return nil, err } - // Immediately open and then close a client handle so that the named pipe is - // created but not currently accepting connections. + // Create a client handle and connect it. This results in the pipe + // instance always existing, so that clients see ERROR_PIPE_BUSY + // rather than ERROR_FILE_NOT_FOUND. This ties the first instance + // up so that no other instances can be used. This would have been + // cleaner if the Win32 API matched CreateFile with ConnectNamedPipe + // instead of CreateNamedPipe. (Apparently created named pipes are + // considered to be in listening state regardless of whether any + // active calls to ConnectNamedPipe are outstanding.) h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) if err != nil { syscall.Close(h) return nil, err } + // Close the client handle. The server side of the instance will + // still be busy, leading to ERROR_PIPE_BUSY instead of + // ERROR_NOT_FOUND, as long as we don't close the server handle, + // or disconnect the client with DisconnectNamedPipe. syscall.Close(h2) l := &win32PipeListener{ firstHandle: h, diff --git a/vendor/github.com/Microsoft/hcsshim/README.md b/vendor/github.com/Microsoft/hcsshim/README.md index deca9a97e..15b39181a 100644 --- a/vendor/github.com/Microsoft/hcsshim/README.md +++ b/vendor/github.com/Microsoft/hcsshim/README.md @@ -1,12 +1,13 @@ # hcsshim -This package supports launching Windows Server containers from Go. It is -primarily used in the [Docker Engine](https://github.com/docker/docker) project, -but it can be freely used by other projects as well. +[![Build status](https://ci.appveyor.com/api/projects/status/nbcw28mnkqml0loa/branch/master?svg=true)](https://ci.appveyor.com/project/WindowsVirtualization/hcsshim/branch/master) +This package contains the Golang interface for using the Windows [Host Compute Service](https://blogs.technet.microsoft.com/virtualization/2017/01/27/introducing-the-host-compute-service-hcs/) (HCS) to launch and manage [Windows Containers](https://docs.microsoft.com/en-us/virtualization/windowscontainers/about/). It also contains other helpers and functions for managing Windows Containers such as the Golang interface for the Host Network Service (HNS). + +It is primarily used in the [Moby Project](https://github.com/moby/moby), but it can be freely used by other projects as well. ## Contributing ---------------- + This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.microsoft.com. @@ -19,6 +20,11 @@ This project has adopted the [Microsoft Open Source Code of Conduct](https://ope For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +## Dependencies + +This project requires Golang 1.9 or newer to build. + +For system requirements to run this project, see the Microsoft docs on [Windows Container requirements](https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/system-requirements). ## Reporting Security Issues @@ -29,5 +35,7 @@ email to ensure we received your original message. Further information, includin [MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in the [Security TechCenter](https://technet.microsoft.com/en-us/security/default). -------------------------------------------- +For additional details, see [Report a Computer Security Vulnerability](https://technet.microsoft.com/en-us/security/ff852094.aspx) on Technet + +--------------- Copyright (c) 2018 Microsoft Corp. All rights reserved. diff --git a/vendor/github.com/Microsoft/hcsshim/activatelayer.go b/vendor/github.com/Microsoft/hcsshim/activatelayer.go deleted file mode 100644 index 6d824d7a7..000000000 --- a/vendor/github.com/Microsoft/hcsshim/activatelayer.go +++ /dev/null @@ -1,28 +0,0 @@ -package hcsshim - -import "github.com/sirupsen/logrus" - -// ActivateLayer will find the layer with the given id and mount it's filesystem. -// 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(info DriverInfo, id string) error { - title := "hcsshim::ActivateLayer " - logrus.Debugf(title+"Flavour %d ID %s", info.Flavour, id) - - infop, err := convertDriverInfo(info) - if err != nil { - logrus.Error(err) - return err - } - - err = activateLayer(&infop, id) - if err != nil { - err = makeErrorf(err, title, "id=%s flavour=%d", id, info.Flavour) - logrus.Error(err) - return err - } - - logrus.Debugf(title+" - succeeded id=%s flavour=%d", id, info.Flavour) - return nil -} diff --git a/vendor/github.com/Microsoft/hcsshim/container.go b/vendor/github.com/Microsoft/hcsshim/container.go index 3354f70ef..e142c3154 100644 --- a/vendor/github.com/Microsoft/hcsshim/container.go +++ b/vendor/github.com/Microsoft/hcsshim/container.go @@ -1,800 +1,192 @@ package hcsshim import ( - "encoding/json" "fmt" "os" - "sync" - "syscall" "time" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/mergemaps" + "github.com/Microsoft/hcsshim/internal/schema1" ) -var ( - defaultTimeout = time.Minute * 4 -) - -const ( - pendingUpdatesQuery = `{ "PropertyTypes" : ["PendingUpdates"]}` - statisticsQuery = `{ "PropertyTypes" : ["Statistics"]}` - processListQuery = `{ "PropertyTypes" : ["ProcessList"]}` - mappedVirtualDiskQuery = `{ "PropertyTypes" : ["MappedVirtualDisk"]}` -) - -type container struct { - handleLock sync.RWMutex - handle hcsSystem - id string - callbackNumber uintptr -} - // ContainerProperties holds the properties for a container and the processes running in that container -type ContainerProperties struct { - ID string `json:"Id"` - Name string - SystemType string - Owner string - SiloGUID string `json:"SiloGuid,omitempty"` - RuntimeID string `json:"RuntimeId,omitempty"` - IsRuntimeTemplate bool `json:",omitempty"` - RuntimeImagePath string `json:",omitempty"` - Stopped bool `json:",omitempty"` - ExitType string `json:",omitempty"` - AreUpdatesPending bool `json:",omitempty"` - ObRoot string `json:",omitempty"` - Statistics Statistics `json:",omitempty"` - ProcessList []ProcessListItem `json:",omitempty"` - MappedVirtualDiskControllers map[int]MappedVirtualDiskController `json:",omitempty"` -} +type ContainerProperties = schema1.ContainerProperties // MemoryStats holds the memory statistics for a container -type MemoryStats struct { - UsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"` - UsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"` - UsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"` -} +type MemoryStats = schema1.MemoryStats // ProcessorStats holds the processor statistics for a container -type ProcessorStats struct { - TotalRuntime100ns uint64 `json:",omitempty"` - RuntimeUser100ns uint64 `json:",omitempty"` - RuntimeKernel100ns uint64 `json:",omitempty"` -} +type ProcessorStats = schema1.ProcessorStats // StorageStats holds the storage statistics for a container -type StorageStats struct { - ReadCountNormalized uint64 `json:",omitempty"` - ReadSizeBytes uint64 `json:",omitempty"` - WriteCountNormalized uint64 `json:",omitempty"` - WriteSizeBytes uint64 `json:",omitempty"` -} +type StorageStats = schema1.StorageStats // NetworkStats holds the network statistics for a container -type NetworkStats struct { - BytesReceived uint64 `json:",omitempty"` - BytesSent uint64 `json:",omitempty"` - PacketsReceived uint64 `json:",omitempty"` - PacketsSent uint64 `json:",omitempty"` - DroppedPacketsIncoming uint64 `json:",omitempty"` - DroppedPacketsOutgoing uint64 `json:",omitempty"` - EndpointId string `json:",omitempty"` - InstanceId string `json:",omitempty"` -} +type NetworkStats = schema1.NetworkStats // Statistics is the structure returned by a statistics call on a container -type Statistics struct { - Timestamp time.Time `json:",omitempty"` - ContainerStartTime time.Time `json:",omitempty"` - Uptime100ns uint64 `json:",omitempty"` - Memory MemoryStats `json:",omitempty"` - Processor ProcessorStats `json:",omitempty"` - Storage StorageStats `json:",omitempty"` - Network []NetworkStats `json:",omitempty"` -} +type Statistics = schema1.Statistics // ProcessList is the structure of an item returned by a ProcessList call on a container -type ProcessListItem struct { - CreateTimestamp time.Time `json:",omitempty"` - ImageName string `json:",omitempty"` - KernelTime100ns uint64 `json:",omitempty"` - MemoryCommitBytes uint64 `json:",omitempty"` - MemoryWorkingSetPrivateBytes uint64 `json:",omitempty"` - MemoryWorkingSetSharedBytes uint64 `json:",omitempty"` - ProcessId uint32 `json:",omitempty"` - UserTime100ns uint64 `json:",omitempty"` -} +type ProcessListItem = schema1.ProcessListItem // MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container -type MappedVirtualDiskController struct { - MappedVirtualDisks map[int]MappedVirtualDisk `json:",omitempty"` -} +type MappedVirtualDiskController = schema1.MappedVirtualDiskController // Type of Request Support in ModifySystem -type RequestType string +type RequestType = schema1.RequestType // Type of Resource Support in ModifySystem -type ResourceType string +type ResourceType = schema1.ResourceType // RequestType const const ( - Add RequestType = "Add" - Remove RequestType = "Remove" - Network ResourceType = "Network" + Add = schema1.Add + Remove = schema1.Remove + Network = schema1.Network ) // ResourceModificationRequestResponse is the structure used to send request to the container to modify the system // Supported resource types are Network and Request Types are Add/Remove -type ResourceModificationRequestResponse struct { - Resource ResourceType `json:"ResourceType"` - Data interface{} `json:"Settings"` - Request RequestType `json:"RequestType,omitempty"` +type ResourceModificationRequestResponse = schema1.ResourceModificationRequestResponse + +type container struct { + system *hcs.System } -// createContainerAdditionalJSON is read from the environment at initialisation +// createComputeSystemAdditionalJSON is read from the environment at initialisation // time. It allows an environment variable to define additional JSON which -// is merged in the CreateContainer call to HCS. -var createContainerAdditionalJSON string +// is merged in the CreateComputeSystem call to HCS. +var createContainerAdditionalJSON []byte func init() { - createContainerAdditionalJSON = os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON") + createContainerAdditionalJSON = ([]byte)(os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON")) } // CreateContainer creates a new container with the given configuration but does not start it. func CreateContainer(id string, c *ContainerConfig) (Container, error) { - return createContainerWithJSON(id, c, "") -} - -// CreateContainerWithJSON creates a new container with the given configuration but does not start it. -// It is identical to CreateContainer except that optional additional JSON can be merged before passing to HCS. -func CreateContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) { - return createContainerWithJSON(id, c, additionalJSON) -} - -func createContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) { - operation := "CreateContainer" - title := "HCSShim::" + operation - - container := &container{ - id: id, + fullConfig, err := mergemaps.MergeJSON(c, createContainerAdditionalJSON) + if err != nil { + return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", createContainerAdditionalJSON, err) } - configurationb, err := json.Marshal(c) + system, err := hcs.CreateComputeSystem(id, fullConfig) if err != nil { return nil, err } - - configuration := string(configurationb) - logrus.Debugf(title+" id=%s config=%s", id, configuration) - - // Merge any additional JSON. Priority is given to what is passed in explicitly, - // falling back to what's set in the environment. - if additionalJSON == "" && createContainerAdditionalJSON != "" { - additionalJSON = createContainerAdditionalJSON - } - if additionalJSON != "" { - configurationMap := map[string]interface{}{} - if err := json.Unmarshal([]byte(configuration), &configurationMap); err != nil { - return nil, fmt.Errorf("failed to unmarshal %s: %s", configuration, err) - } - - additionalMap := map[string]interface{}{} - if err := json.Unmarshal([]byte(additionalJSON), &additionalMap); err != nil { - return nil, fmt.Errorf("failed to unmarshal %s: %s", additionalJSON, err) - } - - mergedMap := mergeMaps(additionalMap, configurationMap) - mergedJSON, err := json.Marshal(mergedMap) - if err != nil { - return nil, fmt.Errorf("failed to marshal merged configuration map %+v: %s", mergedMap, err) - } - - configuration = string(mergedJSON) - logrus.Debugf(title+" id=%s merged config=%s", id, configuration) - } - - var ( - resultp *uint16 - identity syscall.Handle - ) - createError := hcsCreateComputeSystem(id, configuration, identity, &container.handle, &resultp) - - if createError == nil || IsPending(createError) { - if err := container.registerCallback(); err != nil { - // Terminate the container if it still exists. We're okay to ignore a failure here. - container.Terminate() - return nil, makeContainerError(container, operation, "", err) - } - } - - err = processAsyncHcsResult(createError, resultp, container.callbackNumber, hcsNotificationSystemCreateCompleted, &defaultTimeout) - if err != nil { - if err == ErrTimeout { - // Terminate the container if it still exists. We're okay to ignore a failure here. - container.Terminate() - } - return nil, makeContainerError(container, operation, configuration, err) - } - - logrus.Debugf(title+" succeeded id=%s handle=%d", id, container.handle) - return container, nil -} - -// mergeMaps recursively merges map `fromMap` into map `ToMap`. Any pre-existing values -// in ToMap are overwritten. Values in fromMap are added to ToMap. -// From http://stackoverflow.com/questions/40491438/merging-two-json-strings-in-golang -func mergeMaps(fromMap, ToMap interface{}) interface{} { - switch fromMap := fromMap.(type) { - case map[string]interface{}: - ToMap, ok := ToMap.(map[string]interface{}) - if !ok { - return fromMap - } - for keyToMap, valueToMap := range ToMap { - if valueFromMap, ok := fromMap[keyToMap]; ok { - fromMap[keyToMap] = mergeMaps(valueFromMap, valueToMap) - } else { - fromMap[keyToMap] = valueToMap - } - } - case nil: - // merge(nil, map[string]interface{...}) -> map[string]interface{...} - ToMap, ok := ToMap.(map[string]interface{}) - if ok { - return ToMap - } - } - return fromMap + return &container{system}, err } // OpenContainer opens an existing container by ID. func OpenContainer(id string) (Container, error) { - operation := "OpenContainer" - title := "HCSShim::" + operation - logrus.Debugf(title+" id=%s", id) - - container := &container{ - id: id, - } - - var ( - handle hcsSystem - resultp *uint16 - ) - err := hcsOpenComputeSystem(id, &handle, &resultp) - err = processHcsResult(err, resultp) + system, err := hcs.OpenComputeSystem(id) if err != nil { - return nil, makeContainerError(container, operation, "", err) + return nil, err } - - container.handle = handle - - if err := container.registerCallback(); err != nil { - return nil, makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s handle=%d", id, handle) - return container, nil + return &container{system}, err } // GetContainers gets a list of the containers on the system that match the query func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) { - operation := "GetContainers" - title := "HCSShim::" + operation - - queryb, err := json.Marshal(q) - if err != nil { - return nil, err - } - - query := string(queryb) - logrus.Debugf(title+" query=%s", query) - - var ( - resultp *uint16 - computeSystemsp *uint16 - ) - err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return nil, err - } - - if computeSystemsp == nil { - return nil, ErrUnexpectedValue - } - computeSystemsRaw := convertAndFreeCoTaskMemBytes(computeSystemsp) - computeSystems := []ContainerProperties{} - if err := json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil { - return nil, err - } - - logrus.Debugf(title + " succeeded") - return computeSystems, nil + return hcs.GetComputeSystems(q) } // Start synchronously starts the container. func (container *container) Start() error { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "Start" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - if container.handle == 0 { - return makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - var resultp *uint16 - err := hcsStartComputeSystem(container.handle, "", &resultp) - err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemStartCompleted, &defaultTimeout) - if err != nil { - return makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s", container.id) - return nil + return convertSystemError(container.system.Start(), container) } -// Shutdown requests a container shutdown, if IsPending() on the error returned is true, -// it may not actually be shut down until Wait() succeeds. +// Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds. func (container *container) Shutdown() error { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "Shutdown" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - if container.handle == 0 { - return makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - var resultp *uint16 - err := hcsShutdownComputeSystem(container.handle, "", &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s", container.id) - return nil + return convertSystemError(container.system.Shutdown(), container) } -// Terminate requests a container terminate, if IsPending() on the error returned is true, -// it may not actually be shut down until Wait() succeeds. +// Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds. func (container *container) Terminate() error { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "Terminate" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - if container.handle == 0 { - return makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - var resultp *uint16 - err := hcsTerminateComputeSystem(container.handle, "", &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s", container.id) - return nil + return convertSystemError(container.system.Terminate(), container) } -// Wait synchronously waits for the container to shutdown or terminate. +// Waits synchronously waits for the container to shutdown or terminate. func (container *container) Wait() error { - operation := "Wait" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - err := waitForNotification(container.callbackNumber, hcsNotificationSystemExited, nil) - if err != nil { - return makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s", container.id) - return nil + return convertSystemError(container.system.Wait(), container) } -// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. -// If the timeout expires, IsTimeout(err) == true -func (container *container) WaitTimeout(timeout time.Duration) error { - operation := "WaitTimeout" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - err := waitForNotification(container.callbackNumber, hcsNotificationSystemExited, &timeout) - if err != nil { - return makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s", container.id) - return nil +// WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It +// returns false if timeout occurs. +func (container *container) WaitTimeout(t time.Duration) error { + return convertSystemError(container.system.WaitTimeout(t), container) } -func (container *container) properties(query string) (*ContainerProperties, error) { - var ( - resultp *uint16 - propertiesp *uint16 - ) - err := hcsGetComputeSystemProperties(container.handle, query, &propertiesp, &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return nil, err - } +// Pause pauses the execution of a container. +func (container *container) Pause() error { + return convertSystemError(container.system.Pause(), container) +} - if propertiesp == nil { - return nil, ErrUnexpectedValue - } - propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp) - properties := &ContainerProperties{} - if err := json.Unmarshal(propertiesRaw, properties); err != nil { - return nil, err - } - return properties, nil +// Resume resumes the execution of a container. +func (container *container) Resume() error { + return convertSystemError(container.system.Resume(), container) } // HasPendingUpdates returns true if the container has updates pending to install func (container *container) HasPendingUpdates() (bool, error) { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "HasPendingUpdates" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - if container.handle == 0 { - return false, makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - properties, err := container.properties(pendingUpdatesQuery) - if err != nil { - return false, makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s", container.id) - return properties.AreUpdatesPending, nil + return false, nil } -// Statistics returns statistics for the container +// Statistics returns statistics for the container. This is a legacy v1 call func (container *container) Statistics() (Statistics, error) { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "Statistics" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - if container.handle == 0 { - return Statistics{}, makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - properties, err := container.properties(statisticsQuery) + properties, err := container.system.Properties(schema1.PropertyTypeStatistics) if err != nil { - return Statistics{}, makeContainerError(container, operation, "", err) + return Statistics{}, convertSystemError(err, container) } - logrus.Debugf(title+" succeeded id=%s", container.id) return properties.Statistics, nil } -// ProcessList returns an array of ProcessListItems for the container +// ProcessList returns an array of ProcessListItems for the container. This is a legacy v1 call func (container *container) ProcessList() ([]ProcessListItem, error) { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "ProcessList" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - if container.handle == 0 { - return nil, makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - properties, err := container.properties(processListQuery) + properties, err := container.system.Properties(schema1.PropertyTypeProcessList) if err != nil { - return nil, makeContainerError(container, operation, "", err) + return nil, convertSystemError(err, container) } - logrus.Debugf(title+" succeeded id=%s", container.id) return properties.ProcessList, nil } -// MappedVirtualDisks returns a map of the controllers and the disks mapped -// to a container. -// -// Example of JSON returned by the query. -//{ -// "Id":"1126e8d7d279c707a666972a15976371d365eaf622c02cea2c442b84f6f550a3_svm", -// "SystemType":"Container", -// "RuntimeOsType":"Linux", -// "RuntimeId":"00000000-0000-0000-0000-000000000000", -// "State":"Running", -// "MappedVirtualDiskControllers":{ -// "0":{ -// "MappedVirtualDisks":{ -// "2":{ -// "HostPath":"C:\\lcow\\lcow\\scratch\\1126e8d7d279c707a666972a15976371d365eaf622c02cea2c442b84f6f550a3.vhdx", -// "ContainerPath":"/mnt/gcs/LinuxServiceVM/scratch", -// "Lun":2, -// "CreateInUtilityVM":true -// }, -// "3":{ -// "HostPath":"C:\\lcow\\lcow\\1126e8d7d279c707a666972a15976371d365eaf622c02cea2c442b84f6f550a3\\sandbox.vhdx", -// "Lun":3, -// "CreateInUtilityVM":true, -// "AttachOnly":true -// } -// } -// } -// } -//} +// This is a legacy v1 call func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "MappedVirtualDiskList" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - if container.handle == 0 { - return nil, makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - properties, err := container.properties(mappedVirtualDiskQuery) + properties, err := container.system.Properties(schema1.PropertyTypeMappedVirtualDisk) if err != nil { - return nil, makeContainerError(container, operation, "", err) + return nil, convertSystemError(err, container) } - logrus.Debugf(title+" succeeded id=%s", container.id) return properties.MappedVirtualDiskControllers, nil } -// Pause pauses the execution of the container. This feature is not enabled in TP5. -func (container *container) Pause() error { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "Pause" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - if container.handle == 0 { - return makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - var resultp *uint16 - err := hcsPauseComputeSystem(container.handle, "", &resultp) - err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemPauseCompleted, &defaultTimeout) - if err != nil { - return makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s", container.id) - return nil -} - -// Resume resumes the execution of the container. This feature is not enabled in TP5. -func (container *container) Resume() error { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "Resume" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - if container.handle == 0 { - return makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - var resultp *uint16 - err := hcsResumeComputeSystem(container.handle, "", &resultp) - err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemResumeCompleted, &defaultTimeout) - if err != nil { - return makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s", container.id) - return nil -} - // CreateProcess launches a new process within the container. func (container *container) CreateProcess(c *ProcessConfig) (Process, error) { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "CreateProcess" - title := "HCSShim::Container::" + operation - var ( - processInfo hcsProcessInformation - processHandle hcsProcess - resultp *uint16 - ) - - if container.handle == 0 { - return nil, makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - // If we are not emulating a console, ignore any console size passed to us - if !c.EmulateConsole { - c.ConsoleSize[0] = 0 - c.ConsoleSize[1] = 0 - } - - configurationb, err := json.Marshal(c) + p, err := container.system.CreateProcess(c) if err != nil { - return nil, makeContainerError(container, operation, "", err) + return nil, convertSystemError(err, container) } - - configuration := string(configurationb) - logrus.Debugf(title+" id=%s config=%s", container.id, configuration) - - err = hcsCreateProcess(container.handle, configuration, &processInfo, &processHandle, &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return nil, makeContainerError(container, operation, configuration, err) - } - - process := &process{ - handle: processHandle, - processID: int(processInfo.ProcessId), - container: container, - cachedPipes: &cachedPipes{ - stdIn: processInfo.StdInput, - stdOut: processInfo.StdOutput, - stdErr: processInfo.StdError, - }, - } - - if err := process.registerCallback(); err != nil { - return nil, makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s processid=%d", container.id, process.processID) - return process, nil + return &process{p}, nil } // OpenProcess gets an interface to an existing process within the container. func (container *container) OpenProcess(pid int) (Process, error) { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "OpenProcess" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s, processid=%d", container.id, pid) - var ( - processHandle hcsProcess - resultp *uint16 - ) - - if container.handle == 0 { - return nil, makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - err := hcsOpenProcess(container.handle, uint32(pid), &processHandle, &resultp) - err = processHcsResult(err, resultp) + p, err := container.system.OpenProcess(pid) if err != nil { - return nil, makeContainerError(container, operation, "", err) + return nil, convertSystemError(err, container) } - - process := &process{ - handle: processHandle, - processID: pid, - container: container, - } - - if err := process.registerCallback(); err != nil { - return nil, makeContainerError(container, operation, "", err) - } - - logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID) - return process, nil + return &process{p}, nil } // Close cleans up any state associated with the container but does not terminate or wait for it. func (container *container) Close() error { - container.handleLock.Lock() - defer container.handleLock.Unlock() - operation := "Close" - title := "HCSShim::Container::" + operation - logrus.Debugf(title+" id=%s", container.id) - - // Don't double free this - if container.handle == 0 { - return nil - } - - if err := container.unregisterCallback(); err != nil { - return makeContainerError(container, operation, "", err) - } - - if err := hcsCloseComputeSystem(container.handle); err != nil { - return makeContainerError(container, operation, "", err) - } - - container.handle = 0 - - logrus.Debugf(title+" succeeded id=%s", container.id) - return nil + return convertSystemError(container.system.Close(), container) } -func (container *container) registerCallback() error { - context := ¬ifcationWatcherContext{ - channels: newChannels(), - } - - callbackMapLock.Lock() - callbackNumber := nextCallback - nextCallback++ - callbackMap[callbackNumber] = context - callbackMapLock.Unlock() - - var callbackHandle hcsCallback - err := hcsRegisterComputeSystemCallback(container.handle, notificationWatcherCallback, callbackNumber, &callbackHandle) - if err != nil { - return err - } - context.handle = callbackHandle - container.callbackNumber = callbackNumber - - return nil -} - -func (container *container) unregisterCallback() error { - callbackNumber := container.callbackNumber - - callbackMapLock.RLock() - context := callbackMap[callbackNumber] - callbackMapLock.RUnlock() - - if context == nil { - return nil - } - - handle := context.handle - - if handle == 0 { - return nil - } - - // hcsUnregisterComputeSystemCallback has its own syncronization - // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. - err := hcsUnregisterComputeSystemCallback(handle) - if err != nil { - return err - } - - closeChannels(context.channels) - - callbackMapLock.Lock() - callbackMap[callbackNumber] = nil - callbackMapLock.Unlock() - - handle = 0 - - return nil -} - -// Modifies the System by sending a request to HCS +// Modify the System func (container *container) Modify(config *ResourceModificationRequestResponse) error { - container.handleLock.RLock() - defer container.handleLock.RUnlock() - operation := "Modify" - title := "HCSShim::Container::" + operation - - if container.handle == 0 { - return makeContainerError(container, operation, "", ErrAlreadyClosed) - } - - requestJSON, err := json.Marshal(config) - if err != nil { - return err - } - - requestString := string(requestJSON) - logrus.Debugf(title+" id=%s request=%s", container.id, requestString) - - var resultp *uint16 - err = hcsModifyComputeSystem(container.handle, requestString, &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return makeContainerError(container, operation, "", err) - } - logrus.Debugf(title+" succeeded id=%s", container.id) - return nil + return convertSystemError(container.system.Modify(config), container) } diff --git a/vendor/github.com/Microsoft/hcsshim/createlayer.go b/vendor/github.com/Microsoft/hcsshim/createlayer.go deleted file mode 100644 index 035d9c394..000000000 --- a/vendor/github.com/Microsoft/hcsshim/createlayer.go +++ /dev/null @@ -1,27 +0,0 @@ -package hcsshim - -import "github.com/sirupsen/logrus" - -// CreateLayer creates a new, empty, read-only layer on the filesystem based on -// the parent layer provided. -func CreateLayer(info DriverInfo, id, parent string) error { - title := "hcsshim::CreateLayer " - logrus.Debugf(title+"Flavour %d ID %s parent %s", info.Flavour, id, parent) - - // Convert info to API calling convention - infop, err := convertDriverInfo(info) - if err != nil { - logrus.Error(err) - return err - } - - err = createLayer(&infop, id, parent) - if err != nil { - err = makeErrorf(err, title, "id=%s parent=%s flavour=%d", id, parent, info.Flavour) - logrus.Error(err) - return err - } - - logrus.Debugf(title+" - succeeded id=%s parent=%s flavour=%d", id, parent, info.Flavour) - return nil -} diff --git a/vendor/github.com/Microsoft/hcsshim/createsandboxlayer.go b/vendor/github.com/Microsoft/hcsshim/createsandboxlayer.go deleted file mode 100644 index 7a6a8854c..000000000 --- a/vendor/github.com/Microsoft/hcsshim/createsandboxlayer.go +++ /dev/null @@ -1,35 +0,0 @@ -package hcsshim - -import "github.com/sirupsen/logrus" - -// CreateSandboxLayer creates and populates new read-write layer for use by a container. -// 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 CreateSandboxLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error { - title := "hcsshim::CreateSandboxLayer " - logrus.Debugf(title+"layerId %s parentId %s", layerId, parentId) - - // Generate layer descriptors - layers, err := layerPathsToDescriptors(parentLayerPaths) - if err != nil { - return err - } - - // Convert info to API calling convention - infop, err := convertDriverInfo(info) - if err != nil { - logrus.Error(err) - return err - } - - err = createSandboxLayer(&infop, layerId, parentId, layers) - if err != nil { - err = makeErrorf(err, title, "layerId=%s parentId=%s", layerId, parentId) - logrus.Error(err) - return err - } - - logrus.Debugf(title+"- succeeded layerId=%s parentId=%s", layerId, parentId) - return nil -} diff --git a/vendor/github.com/Microsoft/hcsshim/deactivatelayer.go b/vendor/github.com/Microsoft/hcsshim/deactivatelayer.go deleted file mode 100644 index fd785030f..000000000 --- a/vendor/github.com/Microsoft/hcsshim/deactivatelayer.go +++ /dev/null @@ -1,26 +0,0 @@ -package hcsshim - -import "github.com/sirupsen/logrus" - -// DeactivateLayer will dismount a layer that was mounted via ActivateLayer. -func DeactivateLayer(info DriverInfo, id string) error { - title := "hcsshim::DeactivateLayer " - logrus.Debugf(title+"Flavour %d ID %s", info.Flavour, id) - - // Convert info to API calling convention - infop, err := convertDriverInfo(info) - if err != nil { - logrus.Error(err) - return err - } - - err = deactivateLayer(&infop, id) - if err != nil { - err = makeErrorf(err, title, "id=%s flavour=%d", id, info.Flavour) - logrus.Error(err) - return err - } - - logrus.Debugf(title+"succeeded flavour=%d id=%s", info.Flavour, id) - return nil -} diff --git a/vendor/github.com/Microsoft/hcsshim/destroylayer.go b/vendor/github.com/Microsoft/hcsshim/destroylayer.go deleted file mode 100644 index b1e3b89fc..000000000 --- a/vendor/github.com/Microsoft/hcsshim/destroylayer.go +++ /dev/null @@ -1,27 +0,0 @@ -package hcsshim - -import "github.com/sirupsen/logrus" - -// DestroyLayer will remove the on-disk files representing the layer with the given -// id, including that layer's containing folder, if any. -func DestroyLayer(info DriverInfo, id string) error { - title := "hcsshim::DestroyLayer " - logrus.Debugf(title+"Flavour %d ID %s", info.Flavour, id) - - // Convert info to API calling convention - infop, err := convertDriverInfo(info) - if err != nil { - logrus.Error(err) - return err - } - - err = destroyLayer(&infop, id) - if err != nil { - err = makeErrorf(err, title, "id=%s flavour=%d", id, info.Flavour) - logrus.Error(err) - return err - } - - logrus.Debugf(title+"succeeded flavour=%d id=%s", info.Flavour, id) - return nil -} diff --git a/vendor/github.com/Microsoft/hcsshim/errors.go b/vendor/github.com/Microsoft/hcsshim/errors.go index c0c6cac87..63efa23c7 100644 --- a/vendor/github.com/Microsoft/hcsshim/errors.go +++ b/vendor/github.com/Microsoft/hcsshim/errors.go @@ -1,92 +1,83 @@ package hcsshim import ( - "errors" "fmt" "syscall" + + "github.com/Microsoft/hcsshim/internal/hns" + + "github.com/Microsoft/hcsshim/internal/hcs" + "github.com/Microsoft/hcsshim/internal/hcserror" ) var ( - // ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists - ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e) + // ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists = hcs.exist + ErrComputeSystemDoesNotExist = hcs.ErrComputeSystemDoesNotExist // ErrElementNotFound is an error encountered when the object being referenced does not exist - ErrElementNotFound = syscall.Errno(0x490) + ErrElementNotFound = hcs.ErrElementNotFound // ErrElementNotFound is an error encountered when the object being referenced does not exist - ErrNotSupported = syscall.Errno(0x32) + ErrNotSupported = hcs.ErrNotSupported // ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported // decimal -2147024883 / hex 0x8007000d - ErrInvalidData = syscall.Errno(0xd) + ErrInvalidData = hcs.ErrInvalidData // ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed - ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed") + ErrHandleClose = hcs.ErrHandleClose // ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method - ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed") + ErrAlreadyClosed = hcs.ErrAlreadyClosed // ErrInvalidNotificationType is an error encountered when an invalid notification type is used - ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type") + ErrInvalidNotificationType = hcs.ErrInvalidNotificationType // ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation - ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation") + ErrInvalidProcessState = hcs.ErrInvalidProcessState // ErrTimeout is an error encountered when waiting on a notification times out - ErrTimeout = errors.New("hcsshim: timeout waiting for notification") + ErrTimeout = hcs.ErrTimeout // ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for // a different expected notification - ErrUnexpectedContainerExit = errors.New("unexpected container exit") + ErrUnexpectedContainerExit = hcs.ErrUnexpectedContainerExit // ErrUnexpectedProcessAbort is the error encountered when communication with the compute service // is lost while waiting for a notification - ErrUnexpectedProcessAbort = errors.New("lost communication with compute service") + ErrUnexpectedProcessAbort = hcs.ErrUnexpectedProcessAbort // ErrUnexpectedValue is an error encountered when hcs returns an invalid value - ErrUnexpectedValue = errors.New("unexpected value returned from hcs") + ErrUnexpectedValue = hcs.ErrUnexpectedValue // ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container - ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110) + ErrVmcomputeAlreadyStopped = hcs.ErrVmcomputeAlreadyStopped // ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously - ErrVmcomputeOperationPending = syscall.Errno(0xC0370103) + ErrVmcomputeOperationPending = hcs.ErrVmcomputeOperationPending // ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation - ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105) + ErrVmcomputeOperationInvalidState = hcs.ErrVmcomputeOperationInvalidState // ErrProcNotFound is an error encountered when the the process cannot be found - ErrProcNotFound = syscall.Errno(0x7f) + ErrProcNotFound = hcs.ErrProcNotFound // ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2 // builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3. - ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5) + ErrVmcomputeOperationAccessIsDenied = hcs.ErrVmcomputeOperationAccessIsDenied // ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management - ErrVmcomputeInvalidJSON = syscall.Errno(0xc037010d) + ErrVmcomputeInvalidJSON = hcs.ErrVmcomputeInvalidJSON // ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message - ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b) + ErrVmcomputeUnknownMessage = hcs.ErrVmcomputeUnknownMessage // ErrNotSupported is an error encountered when hcs doesn't support the request - ErrPlatformNotSupported = errors.New("unsupported platform request") + ErrPlatformNotSupported = hcs.ErrPlatformNotSupported ) -type EndpointNotFoundError struct { - EndpointName string -} - -func (e EndpointNotFoundError) Error() string { - return fmt.Sprintf("Endpoint %s not found", e.EndpointName) -} - -type NetworkNotFoundError struct { - NetworkName string -} - -func (e NetworkNotFoundError) Error() string { - return fmt.Sprintf("Network %s not found", e.NetworkName) -} +type EndpointNotFoundError = hns.EndpointNotFoundError +type NetworkNotFoundError = hns.NetworkNotFoundError // ProcessError is an error encountered in HCS during an operation on a Process object type ProcessError struct { @@ -94,6 +85,7 @@ type ProcessError struct { Operation string ExtraInfo string Err error + Events []hcs.ErrorEvent } // ContainerError is an error encountered in HCS during an operation on a Container object @@ -102,6 +94,7 @@ type ContainerError struct { Operation string ExtraInfo string Err error + Events []hcs.ErrorEvent } func (e *ContainerError) Error() string { @@ -113,7 +106,7 @@ func (e *ContainerError) Error() string { return "unexpected nil container for error: " + e.Err.Error() } - s := "container " + e.Container.id + s := "container " + e.Container.system.ID() if e.Operation != "" { s += " encountered an error during " + e.Operation @@ -123,11 +116,15 @@ func (e *ContainerError) Error() string { case nil: break case syscall.Errno: - s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, win32FromError(e.Err)) + s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err)) default: s += fmt.Sprintf(": %s", e.Err.Error()) } + for _, ev := range e.Events { + s += "\n" + ev.String() + } + if e.ExtraInfo != "" { s += " extra info: " + e.ExtraInfo } @@ -153,12 +150,7 @@ func (e *ProcessError) Error() string { return "Unexpected nil process for error: " + e.Err.Error() } - s := fmt.Sprintf("process %d", e.Process.processID) - - if e.Process.container != nil { - s += " in container " + e.Process.container.id - } - + s := fmt.Sprintf("process %d in container %s", e.Process.p.Pid(), e.Process.p.SystemID()) if e.Operation != "" { s += " encountered an error during " + e.Operation } @@ -167,11 +159,15 @@ func (e *ProcessError) Error() string { case nil: break case syscall.Errno: - s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, win32FromError(e.Err)) + s += fmt.Sprintf(": failure in a Windows system call: %s (0x%x)", e.Err, hcserror.Win32FromError(e.Err)) default: s += fmt.Sprintf(": %s", e.Err.Error()) } + for _, ev := range e.Events { + s += "\n" + ev.String() + } + return s } @@ -189,37 +185,31 @@ func makeProcessError(process *process, operation string, extraInfo string, err // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist // will currently return true when the error is ErrElementNotFound or ErrProcNotFound. func IsNotExist(err error) bool { - err = getInnerError(err) if _, ok := err.(EndpointNotFoundError); ok { return true } if _, ok := err.(NetworkNotFoundError); ok { return true } - return err == ErrComputeSystemDoesNotExist || - err == ErrElementNotFound || - err == ErrProcNotFound + return hcs.IsNotExist(getInnerError(err)) } // IsAlreadyClosed checks if an error is caused by the Container or Process having been // already closed by a call to the Close() method. func IsAlreadyClosed(err error) bool { - err = getInnerError(err) - return err == ErrAlreadyClosed + return hcs.IsAlreadyClosed(getInnerError(err)) } // IsPending returns a boolean indicating whether the error is that // the requested operation is being completed in the background. func IsPending(err error) bool { - err = getInnerError(err) - return err == ErrVmcomputeOperationPending + return hcs.IsPending(getInnerError(err)) } // IsTimeout returns a boolean indicating whether the error is caused by // a timeout waiting for the operation to complete. func IsTimeout(err error) bool { - err = getInnerError(err) - return err == ErrTimeout + return hcs.IsTimeout(getInnerError(err)) } // IsAlreadyStopped returns a boolean indicating whether the error is caused by @@ -228,10 +218,7 @@ func IsTimeout(err error) bool { // already exited, or does not exist. Both IsAlreadyStopped and IsNotExist // will currently return true when the error is ErrElementNotFound or ErrProcNotFound. func IsAlreadyStopped(err error) bool { - err = getInnerError(err) - return err == ErrVmcomputeAlreadyStopped || - err == ErrElementNotFound || - err == ErrProcNotFound + return hcs.IsAlreadyStopped(getInnerError(err)) } // IsNotSupported returns a boolean indicating whether the error is caused by @@ -240,12 +227,7 @@ func IsAlreadyStopped(err error) bool { // ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage // is thrown from the Platform func IsNotSupported(err error) bool { - err = getInnerError(err) - // If Platform doesn't recognize or support the request sent, below errors are seen - return err == ErrVmcomputeInvalidJSON || - err == ErrInvalidData || - err == ErrNotSupported || - err == ErrVmcomputeUnknownMessage + return hcs.IsNotSupported(getInnerError(err)) } func getInnerError(err error) error { @@ -259,3 +241,17 @@ func getInnerError(err error) error { } return err } + +func convertSystemError(err error, c *container) error { + if serr, ok := err.(*hcs.SystemError); ok { + return &ContainerError{Container: c, Operation: serr.Op, ExtraInfo: serr.Extra, Err: serr.Err, Events: serr.Events} + } + return err +} + +func convertProcessError(err error, p *process) error { + if perr, ok := err.(*hcs.ProcessError); ok { + return &ProcessError{Process: p, Operation: perr.Op, Err: perr.Err, Events: perr.Events} + } + return err +} diff --git a/vendor/github.com/Microsoft/hcsshim/expandsandboxsize.go b/vendor/github.com/Microsoft/hcsshim/expandsandboxsize.go deleted file mode 100644 index 6946c6a84..000000000 --- a/vendor/github.com/Microsoft/hcsshim/expandsandboxsize.go +++ /dev/null @@ -1,26 +0,0 @@ -package hcsshim - -import "github.com/sirupsen/logrus" - -// ExpandSandboxSize expands the size of a layer to at least size bytes. -func ExpandSandboxSize(info DriverInfo, layerId string, size uint64) error { - title := "hcsshim::ExpandSandboxSize " - logrus.Debugf(title+"layerId=%s size=%d", layerId, size) - - // Convert info to API calling convention - infop, err := convertDriverInfo(info) - if err != nil { - logrus.Error(err) - return err - } - - err = expandSandboxSize(&infop, layerId, size) - if err != nil { - err = makeErrorf(err, title, "layerId=%s size=%d", layerId, size) - logrus.Error(err) - return err - } - - logrus.Debugf(title+"- succeeded layerId=%s size=%d", layerId, size) - return nil -} diff --git a/vendor/github.com/Microsoft/hcsshim/guid.go b/vendor/github.com/Microsoft/hcsshim/guid.go deleted file mode 100644 index 620aba123..000000000 --- a/vendor/github.com/Microsoft/hcsshim/guid.go +++ /dev/null @@ -1,19 +0,0 @@ -package hcsshim - -import ( - "crypto/sha1" - "fmt" -) - -type GUID [16]byte - -func NewGUID(source string) *GUID { - h := sha1.Sum([]byte(source)) - var g GUID - copy(g[0:], h[0:16]) - return &g -} - -func (g *GUID) ToString() string { - return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x-%02x", g[3], g[2], g[1], g[0], g[5], g[4], g[7], g[6], g[8:10], g[10:]) -} diff --git a/vendor/github.com/Microsoft/hcsshim/hcsshim.go b/vendor/github.com/Microsoft/hcsshim/hcsshim.go index b65953191..ceb3ac85e 100644 --- a/vendor/github.com/Microsoft/hcsshim/hcsshim.go +++ b/vendor/github.com/Microsoft/hcsshim/hcsshim.go @@ -4,80 +4,20 @@ package hcsshim import ( - "fmt" "syscall" - "unsafe" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/hcserror" ) -//go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go safeopen.go +//go:generate go run mksyscall_windows.go -output zsyscall_windows.go hcsshim.go -//sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree //sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId -//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? -//sys createLayer(info *driverInfo, id string, parent string) (hr error) = vmcompute.CreateLayer? -//sys createSandboxLayer(info *driverInfo, id string, parent string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.CreateSandboxLayer? -//sys expandSandboxSize(info *driverInfo, id string, size uint64) (hr error) = vmcompute.ExpandSandboxSize? -//sys deactivateLayer(info *driverInfo, id string) (hr error) = vmcompute.DeactivateLayer? -//sys destroyLayer(info *driverInfo, id string) (hr error) = vmcompute.DestroyLayer? -//sys exportLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.ExportLayer? -//sys getLayerMountPath(info *driverInfo, id string, length *uintptr, buffer *uint16) (hr error) = vmcompute.GetLayerMountPath? -//sys getBaseImages(buffer **uint16) (hr error) = vmcompute.GetBaseImages? -//sys importLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.ImportLayer? -//sys layerExists(info *driverInfo, id string, exists *uint32) (hr error) = vmcompute.LayerExists? -//sys nameToGuid(name string, guid *GUID) (hr error) = vmcompute.NameToGuid? -//sys prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.PrepareLayer? -//sys unprepareLayer(info *driverInfo, id string) (hr error) = vmcompute.UnprepareLayer? -//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 hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) = vmcompute.HcsEnumerateComputeSystems? -//sys hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem? -//sys hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsOpenComputeSystem? -//sys hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) = vmcompute.HcsCloseComputeSystem? -//sys hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem? -//sys hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem? -//sys hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem? -//sys hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem? -//sys hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem? -//sys hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetComputeSystemProperties? -//sys hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) = vmcompute.HcsModifyComputeSystem? -//sys hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterComputeSystemCallback? -//sys hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterComputeSystemCallback? - -//sys hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsCreateProcess? -//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 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? -//sys hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetServiceProperties? -//sys hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback? -//sys hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback? - -//sys hcsModifyServiceSettings(settings string, result **uint16) (hr error) = vmcompute.HcsModifyServiceSettings? - -//sys _hnsCall(method string, path string, object string, response **uint16) (hr error) = vmcompute.HNSCall? - const ( // Specific user-visible exit codes WaitErrExecFailed = 32767 - ERROR_GEN_FAILURE = syscall.Errno(31) + ERROR_GEN_FAILURE = hcserror.ERROR_GEN_FAILURE ERROR_SHUTDOWN_IN_PROGRESS = syscall.Errno(1115) WSAEINVAL = syscall.Errno(10022) @@ -85,82 +25,4 @@ const ( TimeoutInfinite = 0xFFFFFFFF ) -type HcsError struct { - title string - rest string - Err error -} - -type hcsSystem syscall.Handle -type hcsProcess syscall.Handle -type hcsCallback syscall.Handle - -type hcsProcessInformation struct { - ProcessId uint32 - Reserved uint32 - StdInput syscall.Handle - StdOutput syscall.Handle - StdError syscall.Handle -} - -func makeError(err error, title, rest string) error { - // Pass through DLL errors directly since they do not originate from HCS. - if _, ok := err.(*syscall.DLLError); ok { - return err - } - return &HcsError{title, rest, err} -} - -func makeErrorf(err error, title, format string, a ...interface{}) error { - return makeError(err, title, fmt.Sprintf(format, a...)) -} - -func win32FromError(err error) uint32 { - if herr, ok := err.(*HcsError); ok { - return win32FromError(herr.Err) - } - if code, ok := err.(syscall.Errno); ok { - return uint32(code) - } - return uint32(ERROR_GEN_FAILURE) -} - -func win32FromHresult(hr uintptr) uintptr { - if hr&0x1fff0000 == 0x00070000 { - return hr & 0xffff - } - return hr -} - -func (e *HcsError) Error() string { - s := e.title - if len(s) > 0 && s[len(s)-1] != ' ' { - s += " " - } - s += fmt.Sprintf("failed in Win32: %s (0x%x)", e.Err, win32FromError(e.Err)) - if e.rest != "" { - if e.rest[0] != ' ' { - s += " " - } - s += e.rest - } - return s -} - -func convertAndFreeCoTaskMemString(buffer *uint16) string { - str := syscall.UTF16ToString((*[1 << 30]uint16)(unsafe.Pointer(buffer))[:]) - coTaskMemFree(unsafe.Pointer(buffer)) - return str -} - -func convertAndFreeCoTaskMemBytes(buffer *uint16) []byte { - return []byte(convertAndFreeCoTaskMemString(buffer)) -} - -func processHcsResult(err error, resultp *uint16) error { - if resultp != nil { - result := convertAndFreeCoTaskMemString(resultp) - logrus.Debugf("Result: %s", result) - } - return err -} +type HcsError = hcserror.HcsError diff --git a/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go b/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go index 90689cb1e..5f0dcfe75 100644 --- a/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go +++ b/vendor/github.com/Microsoft/hcsshim/hnsendpoint.go @@ -1,29 +1,11 @@ package hcsshim import ( - "encoding/json" - "net" - - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/hns" ) // HNSEndpoint represents a network endpoint in HNS -type HNSEndpoint struct { - Id string `json:"ID,omitempty"` - Name string `json:",omitempty"` - VirtualNetwork string `json:",omitempty"` - VirtualNetworkName string `json:",omitempty"` - Policies []json.RawMessage `json:",omitempty"` - MacAddress string `json:",omitempty"` - IPAddress net.IP `json:",omitempty"` - DNSSuffix string `json:",omitempty"` - DNSServerList string `json:",omitempty"` - GatewayAddress string `json:",omitempty"` - EnableInternalDNS bool `json:",omitempty"` - DisableICC bool `json:",omitempty"` - PrefixLength uint8 `json:",omitempty"` - IsRemoteEndpoint bool `json:",omitempty"` -} +type HNSEndpoint = hns.HNSEndpoint //SystemType represents the type of the system on which actions are done type SystemType string @@ -37,39 +19,19 @@ const ( // EndpointAttachDetachRequest is the structure used to send request to the container to modify the system // Supported resource types are Network and Request Types are Add/Remove -type EndpointAttachDetachRequest struct { - ContainerID string `json:"ContainerId,omitempty"` - SystemType SystemType `json:"SystemType"` - CompartmentID uint16 `json:"CompartmentId,omitempty"` - VirtualNICName string `json:"VirtualNicName,omitempty"` -} +type EndpointAttachDetachRequest = hns.EndpointAttachDetachRequest // EndpointResquestResponse is object to get the endpoint request response -type EndpointResquestResponse struct { - Success bool - Error string -} +type EndpointResquestResponse = hns.EndpointResquestResponse // HNSEndpointRequest makes a HNS call to modify/query a network endpoint func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) { - endpoint := &HNSEndpoint{} - err := hnsCall(method, "/endpoints/"+path, request, &endpoint) - if err != nil { - return nil, err - } - - return endpoint, nil + return hns.HNSEndpointRequest(method, path, request) } // HNSListEndpointRequest makes a HNS call to query the list of available endpoints func HNSListEndpointRequest() ([]HNSEndpoint, error) { - var endpoint []HNSEndpoint - err := hnsCall("GET", "/endpoints/", "", &endpoint) - if err != nil { - return nil, err - } - - return endpoint, nil + return hns.HNSListEndpointRequest() } // HotAttachEndpoint makes a HCS Call to attach the endpoint to the container @@ -120,204 +82,10 @@ func modifyNetworkEndpoint(containerID string, endpointID string, request Reques // GetHNSEndpointByID get the Endpoint by ID func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) { - return HNSEndpointRequest("GET", endpointID, "") + return hns.GetHNSEndpointByID(endpointID) } // GetHNSEndpointByName gets the endpoint filtered by Name func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { - hnsResponse, err := HNSListEndpointRequest() - if err != nil { - return nil, err - } - for _, hnsEndpoint := range hnsResponse { - if hnsEndpoint.Name == endpointName { - return &hnsEndpoint, nil - } - } - return nil, EndpointNotFoundError{EndpointName: endpointName} -} - -// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods -func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) { - operation := "Create" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s", endpoint.Id) - - jsonString, err := json.Marshal(endpoint) - if err != nil { - return nil, err - } - return HNSEndpointRequest("POST", "", string(jsonString)) -} - -// Delete Endpoint by sending EndpointRequest to HNS -func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) { - operation := "Delete" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s", endpoint.Id) - - return HNSEndpointRequest("DELETE", endpoint.Id, "") -} - -// Update Endpoint -func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) { - operation := "Update" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s", endpoint.Id) - jsonString, err := json.Marshal(endpoint) - if err != nil { - return nil, err - } - err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint) - - return endpoint, err -} - -// ContainerHotAttach attaches an endpoint to a running container -func (endpoint *HNSEndpoint) ContainerHotAttach(containerID string) error { - operation := "ContainerHotAttach" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID) - - return modifyNetworkEndpoint(containerID, endpoint.Id, Add) -} - -// ContainerHotDetach detaches an endpoint from a running container -func (endpoint *HNSEndpoint) ContainerHotDetach(containerID string) error { - operation := "ContainerHotDetach" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s, containerId=%s", endpoint.Id, containerID) - - return modifyNetworkEndpoint(containerID, endpoint.Id, Remove) -} - -// ApplyACLPolicy applies a set of ACL Policies on the Endpoint -func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error { - operation := "ApplyACLPolicy" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s", endpoint.Id) - - for _, policy := range policies { - if policy == nil { - continue - } - jsonString, err := json.Marshal(policy) - if err != nil { - return err - } - endpoint.Policies = append(endpoint.Policies, jsonString) - } - - _, err := endpoint.Update() - return err -} - -// ContainerAttach attaches an endpoint to container -func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error { - operation := "ContainerAttach" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s", endpoint.Id) - - requestMessage := &EndpointAttachDetachRequest{ - ContainerID: containerID, - CompartmentID: compartmentID, - SystemType: ContainerType, - } - response := &EndpointResquestResponse{} - jsonString, err := json.Marshal(requestMessage) - if err != nil { - return err - } - return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) -} - -// ContainerDetach detaches an endpoint from container -func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error { - operation := "ContainerDetach" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s", endpoint.Id) - - requestMessage := &EndpointAttachDetachRequest{ - ContainerID: containerID, - SystemType: ContainerType, - } - response := &EndpointResquestResponse{} - - jsonString, err := json.Marshal(requestMessage) - if err != nil { - return err - } - return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) -} - -// HostAttach attaches a nic on the host -func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error { - operation := "HostAttach" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s", endpoint.Id) - requestMessage := &EndpointAttachDetachRequest{ - CompartmentID: compartmentID, - SystemType: HostType, - } - response := &EndpointResquestResponse{} - - jsonString, err := json.Marshal(requestMessage) - if err != nil { - return err - } - return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) - -} - -// HostDetach detaches a nic on the host -func (endpoint *HNSEndpoint) HostDetach() error { - operation := "HostDetach" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s", endpoint.Id) - requestMessage := &EndpointAttachDetachRequest{ - SystemType: HostType, - } - response := &EndpointResquestResponse{} - - jsonString, err := json.Marshal(requestMessage) - if err != nil { - return err - } - return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) -} - -// VirtualMachineNICAttach attaches a endpoint to a virtual machine -func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error { - operation := "VirtualMachineNicAttach" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s", endpoint.Id) - requestMessage := &EndpointAttachDetachRequest{ - VirtualNICName: virtualMachineNICName, - SystemType: VirtualMachineType, - } - response := &EndpointResquestResponse{} - - jsonString, err := json.Marshal(requestMessage) - if err != nil { - return err - } - return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) -} - -// VirtualMachineNICDetach detaches a endpoint from a virtual machine -func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error { - operation := "VirtualMachineNicDetach" - title := "HCSShim::HNSEndpoint::" + operation - logrus.Debugf(title+" id=%s", endpoint.Id) - - requestMessage := &EndpointAttachDetachRequest{ - SystemType: VirtualMachineType, - } - response := &EndpointResquestResponse{} - - jsonString, err := json.Marshal(requestMessage) - if err != nil { - return err - } - return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) + return hns.GetHNSEndpointByName(endpointName) } diff --git a/vendor/github.com/Microsoft/hcsshim/hnsglobals.go b/vendor/github.com/Microsoft/hcsshim/hnsglobals.go new file mode 100644 index 000000000..2b5381904 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/hnsglobals.go @@ -0,0 +1,16 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +type HNSGlobals = hns.HNSGlobals +type HNSVersion = hns.HNSVersion + +var ( + HNSVersion1803 = hns.HNSVersion1803 +) + +func GetHNSGlobals() (*HNSGlobals, error) { + return hns.GetHNSGlobals() +} diff --git a/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go b/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go index 398583a4e..f775fa1d0 100644 --- a/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go +++ b/vendor/github.com/Microsoft/hcsshim/hnsnetwork.go @@ -1,141 +1,36 @@ package hcsshim import ( - "encoding/json" - "net" - - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/hns" ) // Subnet is assoicated with a network and represents a list // of subnets available to the network -type Subnet struct { - AddressPrefix string `json:",omitempty"` - GatewayAddress string `json:",omitempty"` - Policies []json.RawMessage `json:",omitempty"` -} +type Subnet = hns.Subnet // MacPool is assoicated with a network and represents a list // of macaddresses available to the network -type MacPool struct { - StartMacAddress string `json:",omitempty"` - EndMacAddress string `json:",omitempty"` -} +type MacPool = hns.MacPool // HNSNetwork represents a network in HNS -type HNSNetwork struct { - Id string `json:"ID,omitempty"` - Name string `json:",omitempty"` - Type string `json:",omitempty"` - NetworkAdapterName string `json:",omitempty"` - SourceMac string `json:",omitempty"` - Policies []json.RawMessage `json:",omitempty"` - MacPools []MacPool `json:",omitempty"` - Subnets []Subnet `json:",omitempty"` - DNSSuffix string `json:",omitempty"` - DNSServerList string `json:",omitempty"` - DNSServerCompartment uint32 `json:",omitempty"` - ManagementIP string `json:",omitempty"` - AutomaticDNS bool `json:",omitempty"` -} - -type hnsNetworkResponse struct { - Success bool - Error string - Output HNSNetwork -} - -type hnsResponse struct { - Success bool - Error string - Output json.RawMessage -} +type HNSNetwork = hns.HNSNetwork // HNSNetworkRequest makes a call into HNS to update/query a single network func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) { - var network HNSNetwork - err := hnsCall(method, "/networks/"+path, request, &network) - if err != nil { - return nil, err - } - - return &network, nil + return hns.HNSNetworkRequest(method, path, request) } // HNSListNetworkRequest makes a HNS call to query the list of available networks func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) { - var network []HNSNetwork - err := hnsCall(method, "/networks/"+path, request, &network) - if err != nil { - return nil, err - } - - return network, nil + return hns.HNSListNetworkRequest(method, path, request) } // GetHNSNetworkByID func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) { - return HNSNetworkRequest("GET", networkID, "") + return hns.GetHNSNetworkByID(networkID) } // GetHNSNetworkName filtered by Name func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) { - hsnnetworks, err := HNSListNetworkRequest("GET", "", "") - if err != nil { - return nil, err - } - for _, hnsnetwork := range hsnnetworks { - if hnsnetwork.Name == networkName { - return &hnsnetwork, nil - } - } - return nil, NetworkNotFoundError{NetworkName: networkName} -} - -// Create Network by sending NetworkRequest to HNS. -func (network *HNSNetwork) Create() (*HNSNetwork, error) { - operation := "Create" - title := "HCSShim::HNSNetwork::" + operation - logrus.Debugf(title+" id=%s", network.Id) - - jsonString, err := json.Marshal(network) - if err != nil { - return nil, err - } - return HNSNetworkRequest("POST", "", string(jsonString)) -} - -// Delete Network by sending NetworkRequest to HNS -func (network *HNSNetwork) Delete() (*HNSNetwork, error) { - operation := "Delete" - title := "HCSShim::HNSNetwork::" + operation - logrus.Debugf(title+" id=%s", network.Id) - - return HNSNetworkRequest("DELETE", network.Id, "") -} - -// Creates an endpoint on the Network. -func (network *HNSNetwork) NewEndpoint(ipAddress net.IP, macAddress net.HardwareAddr) *HNSEndpoint { - return &HNSEndpoint{ - VirtualNetwork: network.Id, - IPAddress: ipAddress, - MacAddress: string(macAddress), - } -} - -func (network *HNSNetwork) CreateEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) { - operation := "CreateEndpoint" - title := "HCSShim::HNSNetwork::" + operation - logrus.Debugf(title+" id=%s, endpointId=%s", network.Id, endpoint.Id) - - endpoint.VirtualNetwork = network.Id - return endpoint.Create() -} - -func (network *HNSNetwork) CreateRemoteEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) { - operation := "CreateRemoteEndpoint" - title := "HCSShim::HNSNetwork::" + operation - logrus.Debugf(title+" id=%s", network.Id) - endpoint.IsRemoteEndpoint = true - return network.CreateEndpoint(endpoint) + return hns.GetHNSNetworkByName(networkName) } diff --git a/vendor/github.com/Microsoft/hcsshim/hnspolicy.go b/vendor/github.com/Microsoft/hcsshim/hnspolicy.go index bf860e938..a3e03ff8f 100644 --- a/vendor/github.com/Microsoft/hcsshim/hnspolicy.go +++ b/vendor/github.com/Microsoft/hcsshim/hnspolicy.go @@ -1,94 +1,57 @@ package hcsshim +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + // Type of Request Support in ModifySystem -type PolicyType string +type PolicyType = hns.PolicyType // RequestType const const ( - Nat PolicyType = "NAT" - ACL PolicyType = "ACL" - PA PolicyType = "PA" - VLAN PolicyType = "VLAN" - VSID PolicyType = "VSID" - VNet PolicyType = "VNET" - L2Driver PolicyType = "L2Driver" - Isolation PolicyType = "Isolation" - QOS PolicyType = "QOS" - OutboundNat PolicyType = "OutBoundNAT" - ExternalLoadBalancer PolicyType = "ELB" - Route PolicyType = "ROUTE" + Nat = hns.Nat + ACL = hns.ACL + PA = hns.PA + VLAN = hns.VLAN + VSID = hns.VSID + VNet = hns.VNet + L2Driver = hns.L2Driver + Isolation = hns.Isolation + QOS = hns.QOS + OutboundNat = hns.OutboundNat + ExternalLoadBalancer = hns.ExternalLoadBalancer + Route = hns.Route ) -type NatPolicy struct { - Type PolicyType `json:"Type"` - Protocol string - InternalPort uint16 - ExternalPort uint16 -} +type NatPolicy = hns.NatPolicy -type QosPolicy struct { - Type PolicyType `json:"Type"` - MaximumOutgoingBandwidthInBytes uint64 -} +type QosPolicy = hns.QosPolicy -type IsolationPolicy struct { - Type PolicyType `json:"Type"` - VLAN uint - VSID uint - InDefaultIsolation bool -} +type IsolationPolicy = hns.IsolationPolicy -type VlanPolicy struct { - Type PolicyType `json:"Type"` - VLAN uint -} +type VlanPolicy = hns.VlanPolicy -type VsidPolicy struct { - Type PolicyType `json:"Type"` - VSID uint -} +type VsidPolicy = hns.VsidPolicy -type PaPolicy struct { - Type PolicyType `json:"Type"` - PA string `json:"PA"` -} +type PaPolicy = hns.PaPolicy -type OutboundNatPolicy struct { - Policy - VIP string `json:"VIP,omitempty"` - Exceptions []string `json:"ExceptionList,omitempty"` -} +type OutboundNatPolicy = hns.OutboundNatPolicy -type ActionType string -type DirectionType string -type RuleType string +type ActionType = hns.ActionType +type DirectionType = hns.DirectionType +type RuleType = hns.RuleType const ( - Allow ActionType = "Allow" - Block ActionType = "Block" + Allow = hns.Allow + Block = hns.Block - In DirectionType = "In" - Out DirectionType = "Out" + In = hns.In + Out = hns.Out - Host RuleType = "Host" - Switch RuleType = "Switch" + Host = hns.Host + Switch = hns.Switch ) -type ACLPolicy struct { - Type PolicyType `json:"Type"` - Protocol uint16 - InternalPort uint16 - Action ActionType - Direction DirectionType - LocalAddresses string - RemoteAddresses string - LocalPort uint16 - RemotePort uint16 - RuleType RuleType `json:"RuleType,omitempty"` - Priority uint16 - ServiceName string -} +type ACLPolicy = hns.ACLPolicy -type Policy struct { - Type PolicyType `json:"Type"` -} +type Policy = hns.Policy diff --git a/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go b/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go index ef1ccab16..55aaa4a50 100644 --- a/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go +++ b/vendor/github.com/Microsoft/hcsshim/hnspolicylist.go @@ -1,200 +1,47 @@ package hcsshim import ( - "encoding/json" - - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/hns" ) // RoutePolicy is a structure defining schema for Route based Policy -type RoutePolicy struct { - Policy - DestinationPrefix string `json:"DestinationPrefix,omitempty"` - NextHop string `json:"NextHop,omitempty"` - EncapEnabled bool `json:"NeedEncap,omitempty"` -} +type RoutePolicy = hns.RoutePolicy // ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy -type ELBPolicy struct { - LBPolicy - SourceVIP string `json:"SourceVIP,omitempty"` - VIPs []string `json:"VIPs,omitempty"` - ILB bool `json:"ILB,omitempty"` -} +type ELBPolicy = hns.ELBPolicy // LBPolicy is a structure defining schema for LoadBalancing based Policy -type LBPolicy struct { - Policy - Protocol uint16 `json:"Protocol,omitempty"` - InternalPort uint16 - ExternalPort uint16 -} +type LBPolicy = hns.LBPolicy // PolicyList is a structure defining schema for Policy list request -type PolicyList struct { - ID string `json:"ID,omitempty"` - EndpointReferences []string `json:"References,omitempty"` - Policies []json.RawMessage `json:"Policies,omitempty"` -} +type PolicyList = hns.PolicyList // HNSPolicyListRequest makes a call into HNS to update/query a single network func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) { - var policy PolicyList - err := hnsCall(method, "/policylists/"+path, request, &policy) - if err != nil { - return nil, err - } - - return &policy, nil + return hns.HNSPolicyListRequest(method, path, request) } // HNSListPolicyListRequest gets all the policy list func HNSListPolicyListRequest() ([]PolicyList, error) { - var plist []PolicyList - err := hnsCall("GET", "/policylists/", "", &plist) - if err != nil { - return nil, err - } - - return plist, nil + return hns.HNSListPolicyListRequest() } // PolicyListRequest makes a HNS call to modify/query a network policy list func PolicyListRequest(method, path, request string) (*PolicyList, error) { - policylist := &PolicyList{} - err := hnsCall(method, "/policylists/"+path, request, &policylist) - if err != nil { - return nil, err - } - - return policylist, nil + return hns.PolicyListRequest(method, path, request) } // GetPolicyListByID get the policy list by ID func GetPolicyListByID(policyListID string) (*PolicyList, error) { - return PolicyListRequest("GET", policyListID, "") -} - -// Create PolicyList by sending PolicyListRequest to HNS. -func (policylist *PolicyList) Create() (*PolicyList, error) { - operation := "Create" - title := "HCSShim::PolicyList::" + operation - logrus.Debugf(title+" id=%s", policylist.ID) - jsonString, err := json.Marshal(policylist) - if err != nil { - return nil, err - } - return PolicyListRequest("POST", "", string(jsonString)) -} - -// Delete deletes PolicyList -func (policylist *PolicyList) Delete() (*PolicyList, error) { - operation := "Delete" - title := "HCSShim::PolicyList::" + operation - logrus.Debugf(title+" id=%s", policylist.ID) - - return PolicyListRequest("DELETE", policylist.ID, "") -} - -// AddEndpoint add an endpoint to a Policy List -func (policylist *PolicyList) AddEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) { - operation := "AddEndpoint" - title := "HCSShim::PolicyList::" + operation - logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id) - - _, err := policylist.Delete() - if err != nil { - return nil, err - } - - // Add Endpoint to the Existing List - policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) - - return policylist.Create() -} - -// RemoveEndpoint removes an endpoint from the Policy List -func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) { - operation := "RemoveEndpoint" - title := "HCSShim::PolicyList::" + operation - logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id) - - _, err := policylist.Delete() - if err != nil { - return nil, err - } - - elementToRemove := "/endpoints/" + endpoint.Id - - var references []string - - for _, endpointReference := range policylist.EndpointReferences { - if endpointReference == elementToRemove { - continue - } - references = append(references, endpointReference) - } - policylist.EndpointReferences = references - return policylist.Create() + return hns.GetPolicyListByID(policyListID) } // AddLoadBalancer policy list for the specified endpoints func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { - operation := "AddLoadBalancer" - title := "HCSShim::PolicyList::" + operation - logrus.Debugf(title+" endpointId=%v, isILB=%v, sourceVIP=%s, vip=%s, protocol=%v, internalPort=%v, externalPort=%v", endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) - - policylist := &PolicyList{} - - elbPolicy := &ELBPolicy{ - SourceVIP: sourceVIP, - ILB: isILB, - } - - if len(vip) > 0 { - elbPolicy.VIPs = []string{vip} - } - elbPolicy.Type = ExternalLoadBalancer - elbPolicy.Protocol = protocol - elbPolicy.InternalPort = internalPort - elbPolicy.ExternalPort = externalPort - - for _, endpoint := range endpoints { - policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) - } - - jsonString, err := json.Marshal(elbPolicy) - if err != nil { - return nil, err - } - policylist.Policies = append(policylist.Policies, jsonString) - return policylist.Create() + return hns.AddLoadBalancer(endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) } // AddRoute adds route policy list for the specified endpoints func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) { - operation := "AddRoute" - title := "HCSShim::PolicyList::" + operation - logrus.Debugf(title+" destinationPrefix:%s", destinationPrefix) - - policylist := &PolicyList{} - - rPolicy := &RoutePolicy{ - DestinationPrefix: destinationPrefix, - NextHop: nextHop, - EncapEnabled: encapEnabled, - } - rPolicy.Type = Route - - for _, endpoint := range endpoints { - policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) - } - - jsonString, err := json.Marshal(rPolicy) - if err != nil { - return nil, err - } - - policylist.Policies = append(policylist.Policies, jsonString) - return policylist.Create() + return hns.AddRoute(endpoints, destinationPrefix, nextHop, encapEnabled) } diff --git a/vendor/github.com/Microsoft/hcsshim/hnssupport.go b/vendor/github.com/Microsoft/hcsshim/hnssupport.go new file mode 100644 index 000000000..69405244b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/hnssupport.go @@ -0,0 +1,13 @@ +package hcsshim + +import ( + "github.com/Microsoft/hcsshim/internal/hns" +) + +type HNSSupportedFeatures = hns.HNSSupportedFeatures + +type HNSAclFeatures = hns.HNSAclFeatures + +func GetHNSSupportedFeatures() HNSSupportedFeatures { + return hns.GetHNSSupportedFeatures() +} diff --git a/vendor/github.com/Microsoft/hcsshim/interface.go b/vendor/github.com/Microsoft/hcsshim/interface.go index e21f30025..2724624fd 100644 --- a/vendor/github.com/Microsoft/hcsshim/interface.go +++ b/vendor/github.com/Microsoft/hcsshim/interface.go @@ -1,106 +1,27 @@ package hcsshim import ( - "encoding/json" "io" "time" + + "github.com/Microsoft/hcsshim/internal/schema1" ) // ProcessConfig is used as both the input of Container.CreateProcess // and to convert the parameters to JSON for passing onto the HCS -type ProcessConfig struct { - ApplicationName string `json:",omitempty"` - CommandLine string `json:",omitempty"` - CommandArgs []string `json:",omitempty"` // Used by Linux Containers on Windows - User string `json:",omitempty"` - WorkingDirectory string `json:",omitempty"` - Environment map[string]string `json:",omitempty"` - EmulateConsole bool `json:",omitempty"` - CreateStdInPipe bool `json:",omitempty"` - CreateStdOutPipe bool `json:",omitempty"` - CreateStdErrPipe bool `json:",omitempty"` - ConsoleSize [2]uint `json:",omitempty"` - CreateInUtilityVm bool `json:",omitempty"` // Used by Linux Containers on Windows - OCISpecification *json.RawMessage `json:",omitempty"` // Used by Linux Containers on Windows -} +type ProcessConfig = schema1.ProcessConfig -type Layer struct { - ID string - Path string -} - -type MappedDir struct { - HostPath string - ContainerPath string - ReadOnly bool - BandwidthMaximum uint64 - IOPSMaximum uint64 - CreateInUtilityVM bool -} - -type MappedPipe struct { - HostPath string - ContainerPipeName string -} - -type HvRuntime struct { - ImagePath string `json:",omitempty"` - SkipTemplate bool `json:",omitempty"` - LinuxInitrdFile string `json:",omitempty"` // File under ImagePath on host containing an initrd image for starting a Linux utility VM - LinuxKernelFile string `json:",omitempty"` // File under ImagePath on host containing a kernel for starting a Linux utility VM - LinuxBootParameters string `json:",omitempty"` // Additional boot parameters for starting a Linux Utility VM in initrd mode - BootSource string `json:",omitempty"` // "Vhd" for Linux Utility VM booting from VHD - WritableBootSource bool `json:",omitempty"` // Linux Utility VM booting from VHD -} - -type MappedVirtualDisk struct { - HostPath string `json:",omitempty"` // Path to VHD on the host - ContainerPath string // Platform-specific mount point path in the container - CreateInUtilityVM bool `json:",omitempty"` - ReadOnly bool `json:",omitempty"` - Cache string `json:",omitempty"` // "" (Unspecified); "Disabled"; "Enabled"; "Private"; "PrivateAllowSharing" - AttachOnly bool `json:",omitempty:` -} +type Layer = schema1.Layer +type MappedDir = schema1.MappedDir +type MappedPipe = schema1.MappedPipe +type HvRuntime = schema1.HvRuntime +type MappedVirtualDisk = schema1.MappedVirtualDisk // ContainerConfig is used as both the input of CreateContainer // and to convert the parameters to JSON for passing onto the HCS -type ContainerConfig struct { - SystemType string // HCS requires this to be hard-coded to "Container" - Name string // Name of the container. We use the docker ID. - Owner string `json:",omitempty"` // The management platform that created this container - VolumePath string `json:",omitempty"` // Windows volume path for scratch space. Used by Windows Server Containers only. Format \\?\\Volume{GUID} - IgnoreFlushesDuringBoot bool `json:",omitempty"` // Optimization hint for container startup in Windows - LayerFolderPath string `json:",omitempty"` // Where the layer folders are located. Used by Windows Server Containers only. Format %root%\windowsfilter\containerID - Layers []Layer // List of storage layers. Required for Windows Server and Hyper-V Containers. Format ID=GUID;Path=%root%\windowsfilter\layerID - Credentials string `json:",omitempty"` // Credentials information - ProcessorCount uint32 `json:",omitempty"` // Number of processors to assign to the container. - ProcessorWeight uint64 `json:",omitempty"` // CPU shares (relative weight to other containers with cpu shares). Range is from 1 to 10000. A value of 0 results in default shares. - ProcessorMaximum int64 `json:",omitempty"` // Specifies the portion of processor cycles that this container can use as a percentage times 100. Range is from 1 to 10000. A value of 0 results in no limit. - StorageIOPSMaximum uint64 `json:",omitempty"` // Maximum Storage IOPS - StorageBandwidthMaximum uint64 `json:",omitempty"` // Maximum Storage Bandwidth in bytes per second - StorageSandboxSize uint64 `json:",omitempty"` // Size in bytes that the container system drive should be expanded to if smaller - MemoryMaximumInMB int64 `json:",omitempty"` // Maximum memory available to the container in Megabytes - HostName string `json:",omitempty"` // Hostname - MappedDirectories []MappedDir `json:",omitempty"` // List of mapped directories (volumes/mounts) - MappedPipes []MappedPipe `json:",omitempty"` // List of mapped Windows named pipes - HvPartition bool // True if it a Hyper-V Container - NetworkSharedContainerName string `json:",omitempty"` // Name (ID) of the container that we will share the network stack with. - EndpointList []string `json:",omitempty"` // List of networking endpoints to be attached to container - HvRuntime *HvRuntime `json:",omitempty"` // Hyper-V container settings. Used by Hyper-V containers only. Format ImagePath=%root%\BaseLayerID\UtilityVM - Servicing bool `json:",omitempty"` // True if this container is for servicing - AllowUnqualifiedDNSQuery bool `json:",omitempty"` // True to allow unqualified DNS name resolution - DNSSearchList string `json:",omitempty"` // Comma seperated list of DNS suffixes to use for name resolution - ContainerType string `json:",omitempty"` // "Linux" for Linux containers on Windows. Omitted otherwise. - TerminateOnLastHandleClosed bool `json:",omitempty"` // Should HCS terminate the container once all handles have been closed - MappedVirtualDisks []MappedVirtualDisk `json:",omitempty"` // Array of virtual disks to mount at start -} +type ContainerConfig = schema1.ContainerConfig -type ComputeSystemQuery struct { - IDs []string `json:"Ids,omitempty"` - Types []string `json:",omitempty"` - Names []string `json:",omitempty"` - Owners []string `json:",omitempty"` -} +type ComputeSystemQuery = schema1.ComputeSystemQuery // Container represents a created (but not necessarily running) container. type Container interface { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go b/vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go new file mode 100644 index 000000000..c37dec8c7 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/guid/guid.go @@ -0,0 +1,22 @@ +package guid + +import ( + "crypto/rand" + "fmt" + "io" +) + +type GUID [16]byte + +func New() GUID { + g := GUID{} + _, err := io.ReadFull(rand.Reader, g[:]) + if err != nil { + panic(err) + } + return g +} + +func (g GUID) String() string { + return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x-%02x", g[3], g[2], g[1], g[0], g[5], g[4], g[7], g[6], g[8:10], g[10:]) +} diff --git a/vendor/github.com/Microsoft/hcsshim/callback.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go similarity index 95% rename from vendor/github.com/Microsoft/hcsshim/callback.go rename to vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go index e8c2b00c8..e41c40ec8 100644 --- a/vendor/github.com/Microsoft/hcsshim/callback.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/callback.go @@ -1,8 +1,10 @@ -package hcsshim +package hcs import ( "sync" "syscall" + + "github.com/Microsoft/hcsshim/internal/interop" ) var ( @@ -62,7 +64,7 @@ func closeChannels(channels notificationChannels) { func notificationWatcher(notificationType hcsNotification, callbackNumber uintptr, notificationStatus uintptr, notificationData *uint16) uintptr { var result error if int32(notificationStatus) < 0 { - result = syscall.Errno(win32FromHresult(notificationStatus)) + result = interop.Win32FromHresult(notificationStatus) } callbackMapLock.RLock() diff --git a/vendor/github.com/Microsoft/hcsshim/cgo.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go similarity index 94% rename from vendor/github.com/Microsoft/hcsshim/cgo.go rename to vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go index 200333233..3669c34aa 100644 --- a/vendor/github.com/Microsoft/hcsshim/cgo.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/cgo.go @@ -1,4 +1,4 @@ -package hcsshim +package hcs import "C" diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go new file mode 100644 index 000000000..7471f5cc1 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/errors.go @@ -0,0 +1,279 @@ +package hcs + +import ( + "encoding/json" + "errors" + "fmt" + "syscall" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/sirupsen/logrus" +) + +var ( + // ErrComputeSystemDoesNotExist is an error encountered when the container being operated on no longer exists + ErrComputeSystemDoesNotExist = syscall.Errno(0xc037010e) + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrElementNotFound = syscall.Errno(0x490) + + // ErrElementNotFound is an error encountered when the object being referenced does not exist + ErrNotSupported = syscall.Errno(0x32) + + // ErrInvalidData is an error encountered when the request being sent to hcs is invalid/unsupported + // decimal -2147024883 / hex 0x8007000d + ErrInvalidData = syscall.Errno(0xd) + + // ErrHandleClose is an error encountered when the handle generating the notification being waited on has been closed + ErrHandleClose = errors.New("hcsshim: the handle generating this notification has been closed") + + // ErrAlreadyClosed is an error encountered when using a handle that has been closed by the Close method + ErrAlreadyClosed = errors.New("hcsshim: the handle has already been closed") + + // ErrInvalidNotificationType is an error encountered when an invalid notification type is used + ErrInvalidNotificationType = errors.New("hcsshim: invalid notification type") + + // ErrInvalidProcessState is an error encountered when the process is not in a valid state for the requested operation + ErrInvalidProcessState = errors.New("the process is in an invalid state for the attempted operation") + + // ErrTimeout is an error encountered when waiting on a notification times out + ErrTimeout = errors.New("hcsshim: timeout waiting for notification") + + // ErrUnexpectedContainerExit is the error encountered when a container exits while waiting for + // a different expected notification + ErrUnexpectedContainerExit = errors.New("unexpected container exit") + + // ErrUnexpectedProcessAbort is the error encountered when communication with the compute service + // is lost while waiting for a notification + ErrUnexpectedProcessAbort = errors.New("lost communication with compute service") + + // ErrUnexpectedValue is an error encountered when hcs returns an invalid value + ErrUnexpectedValue = errors.New("unexpected value returned from hcs") + + // ErrVmcomputeAlreadyStopped is an error encountered when a shutdown or terminate request is made on a stopped container + ErrVmcomputeAlreadyStopped = syscall.Errno(0xc0370110) + + // ErrVmcomputeOperationPending is an error encountered when the operation is being completed asynchronously + ErrVmcomputeOperationPending = syscall.Errno(0xC0370103) + + // ErrVmcomputeOperationInvalidState is an error encountered when the compute system is not in a valid state for the requested operation + ErrVmcomputeOperationInvalidState = syscall.Errno(0xc0370105) + + // ErrProcNotFound is an error encountered when the the process cannot be found + ErrProcNotFound = syscall.Errno(0x7f) + + // ErrVmcomputeOperationAccessIsDenied is an error which can be encountered when enumerating compute systems in RS1/RS2 + // builds when the underlying silo might be in the process of terminating. HCS was fixed in RS3. + ErrVmcomputeOperationAccessIsDenied = syscall.Errno(0x5) + + // ErrVmcomputeInvalidJSON is an error encountered when the compute system does not support/understand the messages sent by management + ErrVmcomputeInvalidJSON = syscall.Errno(0xc037010d) + + // ErrVmcomputeUnknownMessage is an error encountered guest compute system doesn't support the message + ErrVmcomputeUnknownMessage = syscall.Errno(0xc037010b) + + // ErrNotSupported is an error encountered when hcs doesn't support the request + ErrPlatformNotSupported = errors.New("unsupported platform request") +) + +type ErrorEvent struct { + Message string `json:"Message,omitempty"` // Fully formated error message + StackTrace string `json:"StackTrace,omitempty"` // Stack trace in string form + Provider string `json:"Provider,omitempty"` + EventID uint16 `json:"EventId,omitempty"` + Flags uint32 `json:"Flags,omitempty"` + Source string `json:"Source,omitempty"` + //Data []EventData `json:"Data,omitempty"` // Omit this as HCS doesn't encode this well. It's more confusing to include. It is however logged in debug mode (see processHcsResult function) +} + +type hcsResult struct { + Error int32 + ErrorMessage string + ErrorEvents []ErrorEvent `json:"ErrorEvents,omitempty"` +} + +func (ev *ErrorEvent) String() string { + evs := "[Event Detail: " + ev.Message + if ev.StackTrace != "" { + evs += " Stack Trace: " + ev.StackTrace + } + if ev.Provider != "" { + evs += " Provider: " + ev.Provider + } + if ev.EventID != 0 { + evs = fmt.Sprintf("%s EventID: %d", evs, ev.EventID) + } + if ev.Flags != 0 { + evs = fmt.Sprintf("%s flags: %d", evs, ev.Flags) + } + if ev.Source != "" { + evs += " Source: " + ev.Source + } + evs += "]" + return evs +} + +func processHcsResult(resultp *uint16) []ErrorEvent { + if resultp != nil { + resultj := interop.ConvertAndFreeCoTaskMemString(resultp) + logrus.Debugf("Result: %s", resultj) + result := &hcsResult{} + if err := json.Unmarshal([]byte(resultj), result); err != nil { + logrus.Warnf("Could not unmarshal HCS result %s: %s", resultj, err) + return nil + } + return result.ErrorEvents + } + return nil +} + +type HcsError struct { + Op string + Err error + Events []ErrorEvent +} + +func (e *HcsError) Error() string { + s := e.Op + ": " + e.Err.Error() + for _, ev := range e.Events { + s += "\n" + ev.String() + } + return s +} + +// ProcessError is an error encountered in HCS during an operation on a Process object +type ProcessError struct { + SystemID string + Pid int + Op string + Err error + Events []ErrorEvent +} + +// SystemError is an error encountered in HCS during an operation on a Container object +type SystemError struct { + ID string + Op string + Err error + Extra string + Events []ErrorEvent +} + +func (e *SystemError) Error() string { + s := e.Op + " " + e.ID + ": " + e.Err.Error() + for _, ev := range e.Events { + s += "\n" + ev.String() + } + if e.Extra != "" { + s += "\n(extra info: " + e.Extra + ")" + } + return s +} + +func makeSystemError(system *System, op string, extra string, err error, events []ErrorEvent) error { + // Don't double wrap errors + if _, ok := err.(*SystemError); ok { + return err + } + return &SystemError{ + ID: system.ID(), + Op: op, + Extra: extra, + Err: err, + Events: events, + } +} + +func (e *ProcessError) Error() string { + s := fmt.Sprintf("%s %s:%d: %s", e.Op, e.SystemID, e.Pid, e.Err.Error()) + for _, ev := range e.Events { + s += "\n" + ev.String() + } + return s +} + +func makeProcessError(process *Process, op string, err error, events []ErrorEvent) error { + // Don't double wrap errors + if _, ok := err.(*ProcessError); ok { + return err + } + return &ProcessError{ + Pid: process.Pid(), + SystemID: process.SystemID(), + Op: op, + Err: err, + Events: events, + } +} + +// IsNotExist checks if an error is caused by the Container or Process not existing. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +func IsNotExist(err error) bool { + err = getInnerError(err) + return err == ErrComputeSystemDoesNotExist || + err == ErrElementNotFound || + err == ErrProcNotFound +} + +// IsAlreadyClosed checks if an error is caused by the Container or Process having been +// already closed by a call to the Close() method. +func IsAlreadyClosed(err error) bool { + err = getInnerError(err) + return err == ErrAlreadyClosed +} + +// IsPending returns a boolean indicating whether the error is that +// the requested operation is being completed in the background. +func IsPending(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeOperationPending +} + +// IsTimeout returns a boolean indicating whether the error is caused by +// a timeout waiting for the operation to complete. +func IsTimeout(err error) bool { + err = getInnerError(err) + return err == ErrTimeout +} + +// IsAlreadyStopped returns a boolean indicating whether the error is caused by +// a Container or Process being already stopped. +// Note: Currently, ErrElementNotFound can mean that a Process has either +// already exited, or does not exist. Both IsAlreadyStopped and IsNotExist +// will currently return true when the error is ErrElementNotFound or ErrProcNotFound. +func IsAlreadyStopped(err error) bool { + err = getInnerError(err) + return err == ErrVmcomputeAlreadyStopped || + err == ErrElementNotFound || + err == ErrProcNotFound +} + +// IsNotSupported returns a boolean indicating whether the error is caused by +// unsupported platform requests +// Note: Currently Unsupported platform requests can be mean either +// ErrVmcomputeInvalidJSON, ErrInvalidData, ErrNotSupported or ErrVmcomputeUnknownMessage +// is thrown from the Platform +func IsNotSupported(err error) bool { + err = getInnerError(err) + // If Platform doesn't recognize or support the request sent, below errors are seen + return err == ErrVmcomputeInvalidJSON || + err == ErrInvalidData || + err == ErrNotSupported || + err == ErrVmcomputeUnknownMessage +} + +func getInnerError(err error) error { + switch pe := err.(type) { + case nil: + return nil + case *HcsError: + err = pe.Err + case *SystemError: + err = pe.Err + case *ProcessError: + err = pe.Err + } + return err +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go new file mode 100644 index 000000000..b8e30eba1 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/hcs.go @@ -0,0 +1,47 @@ +// Shim for the Host Compute Service (HCS) to manage Windows Server +// containers and Hyper-V containers. + +package hcs + +import ( + "syscall" +) + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go hcs.go + +//sys hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) = vmcompute.HcsEnumerateComputeSystems? +//sys hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsCreateComputeSystem? +//sys hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) = vmcompute.HcsOpenComputeSystem? +//sys hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) = vmcompute.HcsCloseComputeSystem? +//sys hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsStartComputeSystem? +//sys hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsShutdownComputeSystem? +//sys hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsTerminateComputeSystem? +//sys hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsPauseComputeSystem? +//sys hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) = vmcompute.HcsResumeComputeSystem? +//sys hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetComputeSystemProperties? +//sys hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) = vmcompute.HcsModifyComputeSystem? +//sys hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterComputeSystemCallback? +//sys hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterComputeSystemCallback? + +//sys hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) = vmcompute.HcsCreateProcess? +//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 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? +//sys hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) = vmcompute.HcsGetServiceProperties? +//sys hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) = vmcompute.HcsRegisterProcessCallback? +//sys hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) = vmcompute.HcsUnregisterProcessCallback? + +type hcsSystem syscall.Handle +type hcsProcess syscall.Handle +type hcsCallback syscall.Handle + +type hcsProcessInformation struct { + ProcessId uint32 + Reserved uint32 + StdInput syscall.Handle + StdOutput syscall.Handle + StdError syscall.Handle +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go new file mode 100644 index 000000000..0de4a706a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/process.go @@ -0,0 +1,386 @@ +package hcs + +import ( + "encoding/json" + "io" + "sync" + "syscall" + "time" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/sirupsen/logrus" +) + +// ContainerError is an error encountered in HCS +type Process struct { + handleLock sync.RWMutex + handle hcsProcess + processID int + system *System + cachedPipes *cachedPipes + callbackNumber uintptr +} + +type cachedPipes struct { + stdIn syscall.Handle + stdOut syscall.Handle + stdErr syscall.Handle +} + +type processModifyRequest struct { + Operation string + ConsoleSize *consoleSize `json:",omitempty"` + CloseHandle *closeHandle `json:",omitempty"` +} + +type consoleSize struct { + Height uint16 + Width uint16 +} + +type closeHandle struct { + Handle string +} + +type ProcessStatus struct { + ProcessID uint32 + Exited bool + ExitCode uint32 + LastWaitResult int32 +} + +const ( + stdIn string = "StdIn" + stdOut string = "StdOut" + stdErr string = "StdErr" +) + +const ( + modifyConsoleSize string = "ConsoleSize" + modifyCloseHandle string = "CloseHandle" +) + +// Pid returns the process ID of the process within the container. +func (process *Process) Pid() int { + return process.processID +} + +// SystemID returns the ID of the process's compute system. +func (process *Process) SystemID() string { + return process.system.ID() +} + +// Kill signals the process to terminate but does not wait for it to finish terminating. +func (process *Process) Kill() error { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + operation := "Kill" + title := "hcsshim::Process::" + operation + logrus.Debugf(title+" processid=%d", process.processID) + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + var resultp *uint16 + err := hcsTerminateProcess(process.handle, &resultp) + 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) + + 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) + + 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 { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + operation := "ResizeConsole" + title := "hcsshim::Process::" + operation + logrus.Debugf(title+" processid=%d", process.processID) + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + modifyRequest := processModifyRequest{ + Operation: modifyConsoleSize, + ConsoleSize: &consoleSize{ + Height: height, + Width: width, + }, + } + + modifyRequestb, err := json.Marshal(modifyRequest) + if err != nil { + return err + } + + modifyRequestStr := string(modifyRequestb) + + var resultp *uint16 + err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp) + events := processHcsResult(resultp) + if err != nil { + return makeProcessError(process, operation, err, events) + } + + logrus.Debugf(title+" succeeded processid=%d", process.processID) + return nil +} + +func (process *Process) Properties() (*ProcessStatus, error) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + operation := "Properties" + title := "hcsshim::Process::" + operation + logrus.Debugf(title+" processid=%d", process.processID) + + if process.handle == 0 { + return nil, makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + var ( + resultp *uint16 + propertiesp *uint16 + ) + err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp) + events := processHcsResult(resultp) + if err != nil { + return nil, makeProcessError(process, operation, err, events) + } + + if propertiesp == nil { + return nil, ErrUnexpectedValue + } + propertiesRaw := interop.ConvertAndFreeCoTaskMemBytes(propertiesp) + + properties := &ProcessStatus{} + if err := json.Unmarshal(propertiesRaw, properties); err != nil { + 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" + properties, err := process.Properties() + if err != nil { + return 0, makeProcessError(process, operation, err, nil) + } + + if properties.Exited == false { + return 0, makeProcessError(process, operation, ErrInvalidProcessState, nil) + } + + if properties.LastWaitResult != 0 { + return 0, makeProcessError(process, operation, syscall.Errno(properties.LastWaitResult), nil) + } + + return int(properties.ExitCode), nil +} + +// 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) { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + operation := "Stdio" + title := "hcsshim::Process::" + operation + logrus.Debugf(title+" processid=%d", process.processID) + + if process.handle == 0 { + return nil, nil, nil, makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + var stdIn, stdOut, stdErr syscall.Handle + + if process.cachedPipes == nil { + var ( + processInfo hcsProcessInformation + resultp *uint16 + ) + err := hcsGetProcessInfo(process.handle, &processInfo, &resultp) + events := processHcsResult(resultp) + if err != nil { + return nil, nil, nil, makeProcessError(process, operation, err, events) + } + + stdIn, stdOut, stdErr = processInfo.StdInput, processInfo.StdOutput, processInfo.StdError + } else { + // Use cached pipes + stdIn, stdOut, stdErr = process.cachedPipes.stdIn, process.cachedPipes.stdOut, process.cachedPipes.stdErr + + // Invalidate the cache + process.cachedPipes = nil + } + + pipes, err := makeOpenFiles([]syscall.Handle{stdIn, stdOut, stdErr}) + if err != nil { + 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 { + process.handleLock.RLock() + defer process.handleLock.RUnlock() + operation := "CloseStdin" + title := "hcsshim::Process::" + operation + logrus.Debugf(title+" processid=%d", process.processID) + + if process.handle == 0 { + return makeProcessError(process, operation, ErrAlreadyClosed, nil) + } + + modifyRequest := processModifyRequest{ + Operation: modifyCloseHandle, + CloseHandle: &closeHandle{ + Handle: stdIn, + }, + } + + modifyRequestb, err := json.Marshal(modifyRequest) + if err != nil { + return err + } + + modifyRequestStr := string(modifyRequestb) + + var resultp *uint16 + err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp) + events := processHcsResult(resultp) + if err != nil { + 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 { + process.handleLock.Lock() + defer process.handleLock.Unlock() + operation := "Close" + title := "hcsshim::Process::" + operation + logrus.Debugf(title+" processid=%d", process.processID) + + // Don't double free this + if process.handle == 0 { + return nil + } + + if err := process.unregisterCallback(); err != nil { + return makeProcessError(process, operation, 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 +} + +func (process *Process) registerCallback() error { + context := ¬ifcationWatcherContext{ + channels: newChannels(), + } + + callbackMapLock.Lock() + callbackNumber := nextCallback + nextCallback++ + callbackMap[callbackNumber] = context + callbackMapLock.Unlock() + + var callbackHandle hcsCallback + err := hcsRegisterProcessCallback(process.handle, notificationWatcherCallback, callbackNumber, &callbackHandle) + if err != nil { + return err + } + context.handle = callbackHandle + process.callbackNumber = callbackNumber + + return nil +} + +func (process *Process) unregisterCallback() error { + callbackNumber := process.callbackNumber + + callbackMapLock.RLock() + context := callbackMap[callbackNumber] + callbackMapLock.RUnlock() + + if context == nil { + return nil + } + + handle := context.handle + + if handle == 0 { + return nil + } + + // hcsUnregisterProcessCallback has its own syncronization + // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. + err := hcsUnregisterProcessCallback(handle) + if err != nil { + return err + } + + closeChannels(context.channels) + + callbackMapLock.Lock() + callbackMap[callbackNumber] = nil + callbackMapLock.Unlock() + + handle = 0 + + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go new file mode 100644 index 000000000..41ff2877b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/system.go @@ -0,0 +1,547 @@ +package hcs + +import ( + "encoding/json" + "os" + "strconv" + "sync" + "syscall" + "time" + + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/Microsoft/hcsshim/internal/schema1" + "github.com/Microsoft/hcsshim/internal/timeout" + "github.com/sirupsen/logrus" +) + +// currentContainerStarts is used to limit the number of concurrent container +// starts. +var currentContainerStarts containerStarts + +type containerStarts struct { + maxParallel int + inProgress int + sync.Mutex +} + +func init() { + mpsS := os.Getenv("HCSSHIM_MAX_PARALLEL_START") + if len(mpsS) > 0 { + mpsI, err := strconv.Atoi(mpsS) + if err != nil || mpsI < 0 { + return + } + currentContainerStarts.maxParallel = mpsI + } +} + +type System struct { + handleLock sync.RWMutex + 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 + + computeSystem := &System{ + id: id, + } + + hcsDocumentB, err := json.Marshal(hcsDocumentInterface) + if err != nil { + return nil, err + } + + hcsDocument := string(hcsDocumentB) + logrus.Debugf(title+" ID=%s config=%s", id, hcsDocument) + + var ( + resultp *uint16 + identity syscall.Handle + ) + createError := hcsCreateComputeSystem(id, hcsDocument, identity, &computeSystem.handle, &resultp) + + if createError == nil || IsPending(createError) { + if err := computeSystem.registerCallback(); err != nil { + // Terminate the compute system if it still exists. We're okay to + // ignore a failure here. + computeSystem.Terminate() + return nil, makeSystemError(computeSystem, operation, "", err, nil) + } + } + + events, err := processAsyncHcsResult(createError, resultp, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.Duration) + if err != nil { + if err == ErrTimeout { + // Terminate the compute system if it still exists. We're okay to + // ignore a failure here. + computeSystem.Terminate() + } + 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) + + computeSystem := &System{ + id: id, + } + + var ( + handle hcsSystem + resultp *uint16 + ) + err := hcsOpenComputeSystem(id, &handle, &resultp) + events := processHcsResult(resultp) + if err != nil { + return nil, makeSystemError(computeSystem, operation, "", err, events) + } + + computeSystem.handle = handle + + 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 + + queryb, err := json.Marshal(q) + if err != nil { + return nil, err + } + + query := string(queryb) + logrus.Debugf(title+" query=%s", query) + + var ( + resultp *uint16 + computeSystemsp *uint16 + ) + err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp) + events := processHcsResult(resultp) + if err != nil { + return nil, &HcsError{Op: operation, Err: err, Events: events} + } + + if computeSystemsp == nil { + return nil, ErrUnexpectedValue + } + computeSystemsRaw := interop.ConvertAndFreeCoTaskMemBytes(computeSystemsp) + computeSystems := []schema1.ContainerProperties{} + 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 { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + title := "hcsshim::ComputeSystem::Start ID=" + computeSystem.ID() + logrus.Debugf(title) + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Start", "", ErrAlreadyClosed, nil) + } + + // This is a very simple backoff-retry loop to limit the number + // of parallel container starts if environment variable + // HCSSHIM_MAX_PARALLEL_START is set to a positive integer. + // It should generally only be used as a workaround to various + // platform issues that exist between RS1 and RS4 as of Aug 2018 + if currentContainerStarts.maxParallel > 0 { + for { + currentContainerStarts.Lock() + if currentContainerStarts.inProgress < currentContainerStarts.maxParallel { + currentContainerStarts.inProgress++ + currentContainerStarts.Unlock() + break + } + if currentContainerStarts.inProgress == currentContainerStarts.maxParallel { + currentContainerStarts.Unlock() + time.Sleep(100 * time.Millisecond) + } + } + // Make sure we decrement the count when we are done. + defer func() { + currentContainerStarts.Lock() + currentContainerStarts.inProgress-- + currentContainerStarts.Unlock() + }() + } + + var resultp *uint16 + err := hcsStartComputeSystem(computeSystem.handle, "", &resultp) + events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.Duration) + if err != nil { + return makeSystemError(computeSystem, "Start", "", err, events) + } + + logrus.Debugf(title + " succeeded") + return nil +} + +// ID returns the compute system's identifier. +func (computeSystem *System) ID() string { + return computeSystem.id +} + +// 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 { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + title := "hcsshim::ComputeSystem::Shutdown" + logrus.Debugf(title) + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Shutdown", "", ErrAlreadyClosed, nil) + } + + var resultp *uint16 + err := hcsShutdownComputeSystem(computeSystem.handle, "", &resultp) + 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 { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + title := "hcsshim::ComputeSystem::Terminate ID=" + computeSystem.ID() + logrus.Debugf(title) + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Terminate", "", ErrAlreadyClosed, nil) + } + + var resultp *uint16 + err := hcsTerminateComputeSystem(computeSystem.handle, "", &resultp) + events := processHcsResult(resultp) + if err != nil { + 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) + + err := waitForNotification(computeSystem.callbackNumber, hcsNotificationSystemExited, nil) + if err != nil { + return makeSystemError(computeSystem, "Wait", "", err, nil) + } + + logrus.Debugf(title + " succeeded") + 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) + + 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) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + + queryj, err := json.Marshal(schema1.PropertyQuery{types}) + if err != nil { + return nil, makeSystemError(computeSystem, "Properties", "", err, nil) + } + + var resultp, propertiesp *uint16 + err = hcsGetComputeSystemProperties(computeSystem.handle, string(queryj), &propertiesp, &resultp) + events := processHcsResult(resultp) + if err != nil { + return nil, makeSystemError(computeSystem, "Properties", "", err, events) + } + + if propertiesp == nil { + return nil, ErrUnexpectedValue + } + propertiesRaw := interop.ConvertAndFreeCoTaskMemBytes(propertiesp) + properties := &schema1.ContainerProperties{} + 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 { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + title := "hcsshim::ComputeSystem::Pause ID=" + computeSystem.ID() + logrus.Debugf(title) + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Pause", "", ErrAlreadyClosed, nil) + } + + var resultp *uint16 + err := hcsPauseComputeSystem(computeSystem.handle, "", &resultp) + events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.Duration) + 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 { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + title := "hcsshim::ComputeSystem::Resume ID=" + computeSystem.ID() + logrus.Debugf(title) + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Resume", "", ErrAlreadyClosed, nil) + } + + var resultp *uint16 + err := hcsResumeComputeSystem(computeSystem.handle, "", &resultp) + events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.Duration) + 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) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + title := "hcsshim::ComputeSystem::CreateProcess ID=" + computeSystem.ID() + var ( + processInfo hcsProcessInformation + processHandle hcsProcess + resultp *uint16 + ) + + if computeSystem.handle == 0 { + return nil, makeSystemError(computeSystem, "CreateProcess", "", ErrAlreadyClosed, nil) + } + + configurationb, err := json.Marshal(c) + if err != nil { + return nil, makeSystemError(computeSystem, "CreateProcess", "", err, nil) + } + + configuration := string(configurationb) + logrus.Debugf(title+" config=%s", configuration) + + err = hcsCreateProcess(computeSystem.handle, configuration, &processInfo, &processHandle, &resultp) + events := processHcsResult(resultp) + if err != nil { + 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, + }, + } + + 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) { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + title := "hcsshim::ComputeSystem::OpenProcess ID=" + computeSystem.ID() + logrus.Debugf(title+" processid=%d", pid) + var ( + processHandle hcsProcess + resultp *uint16 + ) + + if computeSystem.handle == 0 { + return nil, makeSystemError(computeSystem, "OpenProcess", "", ErrAlreadyClosed, nil) + } + + err := hcsOpenProcess(computeSystem.handle, uint32(pid), &processHandle, &resultp) + 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 { + 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 { + computeSystem.handleLock.Lock() + defer computeSystem.handleLock.Unlock() + title := "hcsshim::ComputeSystem::Close ID=" + computeSystem.ID() + logrus.Debugf(title) + + // Don't double free this + if computeSystem.handle == 0 { + return nil + } + + if err := computeSystem.unregisterCallback(); err != nil { + return makeSystemError(computeSystem, "Close", "", err, nil) + } + + if err := hcsCloseComputeSystem(computeSystem.handle); err != nil { + return makeSystemError(computeSystem, "Close", "", err, nil) + } + + computeSystem.handle = 0 + + logrus.Debugf(title + " succeeded") + return nil +} + +func (computeSystem *System) registerCallback() error { + context := ¬ifcationWatcherContext{ + channels: newChannels(), + } + + callbackMapLock.Lock() + callbackNumber := nextCallback + nextCallback++ + callbackMap[callbackNumber] = context + callbackMapLock.Unlock() + + var callbackHandle hcsCallback + err := hcsRegisterComputeSystemCallback(computeSystem.handle, notificationWatcherCallback, callbackNumber, &callbackHandle) + if err != nil { + return err + } + context.handle = callbackHandle + computeSystem.callbackNumber = callbackNumber + + return nil +} + +func (computeSystem *System) unregisterCallback() error { + callbackNumber := computeSystem.callbackNumber + + callbackMapLock.RLock() + context := callbackMap[callbackNumber] + callbackMapLock.RUnlock() + + if context == nil { + return nil + } + + handle := context.handle + + if handle == 0 { + return nil + } + + // hcsUnregisterComputeSystemCallback has its own syncronization + // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. + err := hcsUnregisterComputeSystemCallback(handle) + if err != nil { + return err + } + + closeChannels(context.channels) + + callbackMapLock.Lock() + callbackMap[callbackNumber] = nil + callbackMapLock.Unlock() + + handle = 0 + + return nil +} + +// Modifies the System by sending a request to HCS +func (computeSystem *System) Modify(config interface{}) error { + computeSystem.handleLock.RLock() + defer computeSystem.handleLock.RUnlock() + title := "hcsshim::Modify ID=" + computeSystem.id + + if computeSystem.handle == 0 { + return makeSystemError(computeSystem, "Modify", "", ErrAlreadyClosed, nil) + } + + requestJSON, err := json.Marshal(config) + if err != nil { + return err + } + + requestString := string(requestJSON) + logrus.Debugf(title + " " + requestString) + + var resultp *uint16 + err = hcsModifyComputeSystem(computeSystem.handle, requestString, &resultp) + 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/utils.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go similarity index 97% rename from vendor/github.com/Microsoft/hcsshim/utils.go rename to vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go index bd6e2d94a..a638677ed 100644 --- a/vendor/github.com/Microsoft/hcsshim/utils.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/utils.go @@ -1,4 +1,4 @@ -package hcsshim +package hcs import ( "io" diff --git a/vendor/github.com/Microsoft/hcsshim/waithelper.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go similarity index 89% rename from vendor/github.com/Microsoft/hcsshim/waithelper.go rename to vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go index b7be20ea0..91e212c57 100644 --- a/vendor/github.com/Microsoft/hcsshim/waithelper.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/waithelper.go @@ -1,4 +1,4 @@ -package hcsshim +package hcs import ( "time" @@ -6,13 +6,13 @@ import ( "github.com/sirupsen/logrus" ) -func processAsyncHcsResult(err error, resultp *uint16, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error { - err = processHcsResult(err, resultp) +func processAsyncHcsResult(err error, resultp *uint16, callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) ([]ErrorEvent, error) { + events := processHcsResult(resultp) if IsPending(err) { - return waitForNotification(callbackNumber, expectedNotification, timeout) + return nil, waitForNotification(callbackNumber, expectedNotification, timeout) } - return err + return events, err } func waitForNotification(callbackNumber uintptr, expectedNotification hcsNotification, timeout *time.Duration) error { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go new file mode 100644 index 000000000..48d5cd32b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcs/zsyscall_windows.go @@ -0,0 +1,441 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package hcs + +import ( + "syscall" + "unsafe" + + "github.com/Microsoft/hcsshim/internal/interop" + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + + procHcsEnumerateComputeSystems = modvmcompute.NewProc("HcsEnumerateComputeSystems") + procHcsCreateComputeSystem = modvmcompute.NewProc("HcsCreateComputeSystem") + procHcsOpenComputeSystem = modvmcompute.NewProc("HcsOpenComputeSystem") + procHcsCloseComputeSystem = modvmcompute.NewProc("HcsCloseComputeSystem") + procHcsStartComputeSystem = modvmcompute.NewProc("HcsStartComputeSystem") + procHcsShutdownComputeSystem = modvmcompute.NewProc("HcsShutdownComputeSystem") + procHcsTerminateComputeSystem = modvmcompute.NewProc("HcsTerminateComputeSystem") + procHcsPauseComputeSystem = modvmcompute.NewProc("HcsPauseComputeSystem") + procHcsResumeComputeSystem = modvmcompute.NewProc("HcsResumeComputeSystem") + procHcsGetComputeSystemProperties = modvmcompute.NewProc("HcsGetComputeSystemProperties") + procHcsModifyComputeSystem = modvmcompute.NewProc("HcsModifyComputeSystem") + procHcsRegisterComputeSystemCallback = modvmcompute.NewProc("HcsRegisterComputeSystemCallback") + procHcsUnregisterComputeSystemCallback = modvmcompute.NewProc("HcsUnregisterComputeSystemCallback") + procHcsCreateProcess = modvmcompute.NewProc("HcsCreateProcess") + 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") +) + +func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(query) + if hr != nil { + return + } + return _hcsEnumerateComputeSystems(_p0, computeSystems, result) +} + +func _hcsEnumerateComputeSystems(query *uint16, computeSystems **uint16, result **uint16) (hr error) { + if hr = procHcsEnumerateComputeSystems.Find(); hr != nil { + return + } + 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) + } + return +} + +func hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(configuration) + if hr != nil { + return + } + return _hcsCreateComputeSystem(_p0, _p1, identity, computeSystem, result) +} + +func _hcsCreateComputeSystem(id *uint16, configuration *uint16, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) { + if hr = procHcsCreateComputeSystem.Find(); hr != nil { + return + } + 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) + } + return +} + +func hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _hcsOpenComputeSystem(_p0, computeSystem, result) +} + +func _hcsOpenComputeSystem(id *uint16, computeSystem *hcsSystem, result **uint16) (hr error) { + if hr = procHcsOpenComputeSystem.Find(); hr != nil { + return + } + 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) + } + return +} + +func hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) { + if hr = procHcsCloseComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsCloseComputeSystem.Addr(), 1, uintptr(computeSystem), 0, 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsStartComputeSystem(computeSystem, _p0, result) +} + +func _hcsStartComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsStartComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsStartComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsShutdownComputeSystem(computeSystem, _p0, result) +} + +func _hcsShutdownComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsShutdownComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsShutdownComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsTerminateComputeSystem(computeSystem, _p0, result) +} + +func _hcsTerminateComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsTerminateComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsPauseComputeSystem(computeSystem, _p0, result) +} + +func _hcsPauseComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsPauseComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsPauseComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(options) + if hr != nil { + return + } + return _hcsResumeComputeSystem(computeSystem, _p0, result) +} + +func _hcsResumeComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { + if hr = procHcsResumeComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsResumeComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(propertyQuery) + if hr != nil { + return + } + return _hcsGetComputeSystemProperties(computeSystem, _p0, properties, result) +} + +func _hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { + if hr = procHcsGetComputeSystemProperties.Find(); hr != nil { + return + } + 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) + } + return +} + +func hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(configuration) + if hr != nil { + return + } + return _hcsModifyComputeSystem(computeSystem, _p0, result) +} + +func _hcsModifyComputeSystem(computeSystem hcsSystem, configuration *uint16, result **uint16) (hr error) { + if hr = procHcsModifyComputeSystem.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsModifyComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) { + if hr = procHcsRegisterComputeSystemCallback.Find(); hr != nil { + return + } + 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) + } + return +} + +func hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) { + if hr = procHcsUnregisterComputeSystemCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsUnregisterComputeSystemCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(processParameters) + if hr != nil { + return + } + return _hcsCreateProcess(computeSystem, _p0, processInformation, process, result) +} + +func _hcsCreateProcess(computeSystem hcsSystem, processParameters *uint16, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) { + if hr = procHcsCreateProcess.Find(); hr != nil { + return + } + 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) + } + return +} + +func hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) { + if hr = procHcsOpenProcess.Find(); hr != nil { + return + } + 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) + } + return +} + +func hcsCloseProcess(process hcsProcess) (hr error) { + if hr = procHcsCloseProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsCloseProcess.Addr(), 1, uintptr(process), 0, 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) { + if hr = procHcsTerminateProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 2, uintptr(process), uintptr(unsafe.Pointer(result)), 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) { + if hr = procHcsGetProcessInfo.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetProcessInfo.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) { + if hr = procHcsGetProcessProperties.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsGetProcessProperties.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processProperties)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(settings) + if hr != nil { + return + } + return _hcsModifyProcess(process, _p0, result) +} + +func _hcsModifyProcess(process hcsProcess, settings *uint16, result **uint16) (hr error) { + if hr = procHcsModifyProcess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsModifyProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result))) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(propertyQuery) + if hr != nil { + return + } + return _hcsGetServiceProperties(_p0, properties, result) +} + +func _hcsGetServiceProperties(propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { + if hr = procHcsGetServiceProperties.Find(); hr != nil { + return + } + 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) + } + return +} + +func hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) { + if hr = procHcsRegisterProcessCallback.Find(); hr != nil { + return + } + 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) + } + return +} + +func hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) { + if hr = procHcsUnregisterProcessCallback.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procHcsUnregisterProcessCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go b/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go new file mode 100644 index 000000000..c8d362c66 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hcserror/hcserror.go @@ -0,0 +1,51 @@ +package hcserror + +import ( + "fmt" + "syscall" +) + +const ERROR_GEN_FAILURE = syscall.Errno(31) + +type HcsError struct { + title string + rest string + Err error +} + +func (e *HcsError) Error() string { + s := e.title + if len(s) > 0 && s[len(s)-1] != ' ' { + s += " " + } + s += fmt.Sprintf("failed in Win32: %s (0x%x)", e.Err, Win32FromError(e.Err)) + if e.rest != "" { + if e.rest[0] != ' ' { + s += " " + } + s += e.rest + } + return s +} + +func New(err error, title, rest string) error { + // Pass through DLL errors directly since they do not originate from HCS. + if _, ok := err.(*syscall.DLLError); ok { + return err + } + 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) + } + if code, ok := err.(syscall.Errno); ok { + return uint32(code) + } + return uint32(ERROR_GEN_FAILURE) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go new file mode 100644 index 000000000..b2e475f53 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hns.go @@ -0,0 +1,23 @@ +package hns + +import "fmt" + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go hns.go + +//sys _hnsCall(method string, path string, object string, response **uint16) (hr error) = vmcompute.HNSCall? + +type EndpointNotFoundError struct { + EndpointName string +} + +func (e EndpointNotFoundError) Error() string { + return fmt.Sprintf("Endpoint %s not found", e.EndpointName) +} + +type NetworkNotFoundError struct { + NetworkName string +} + +func (e NetworkNotFoundError) Error() string { + return fmt.Sprintf("Network %s not found", e.NetworkName) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go new file mode 100644 index 000000000..ce636458c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsendpoint.go @@ -0,0 +1,260 @@ +package hns + +import ( + "encoding/json" + "net" + + "github.com/sirupsen/logrus" +) + +// HNSEndpoint represents a network endpoint in HNS +type HNSEndpoint struct { + Id string `json:"ID,omitempty"` + Name string `json:",omitempty"` + VirtualNetwork string `json:",omitempty"` + VirtualNetworkName string `json:",omitempty"` + Policies []json.RawMessage `json:",omitempty"` + MacAddress string `json:",omitempty"` + IPAddress net.IP `json:",omitempty"` + DNSSuffix string `json:",omitempty"` + DNSServerList string `json:",omitempty"` + GatewayAddress string `json:",omitempty"` + EnableInternalDNS bool `json:",omitempty"` + DisableICC bool `json:",omitempty"` + PrefixLength uint8 `json:",omitempty"` + IsRemoteEndpoint bool `json:",omitempty"` + Namespace *Namespace `json:",omitempty"` +} + +//SystemType represents the type of the system on which actions are done +type SystemType string + +// SystemType const +const ( + ContainerType SystemType = "Container" + VirtualMachineType SystemType = "VirtualMachine" + HostType SystemType = "Host" +) + +// EndpointAttachDetachRequest is the structure used to send request to the container to modify the system +// Supported resource types are Network and Request Types are Add/Remove +type EndpointAttachDetachRequest struct { + ContainerID string `json:"ContainerId,omitempty"` + SystemType SystemType `json:"SystemType"` + CompartmentID uint16 `json:"CompartmentId,omitempty"` + VirtualNICName string `json:"VirtualNicName,omitempty"` +} + +// EndpointResquestResponse is object to get the endpoint request response +type EndpointResquestResponse struct { + Success bool + Error string +} + +// HNSEndpointRequest makes a HNS call to modify/query a network endpoint +func HNSEndpointRequest(method, path, request string) (*HNSEndpoint, error) { + endpoint := &HNSEndpoint{} + err := hnsCall(method, "/endpoints/"+path, request, &endpoint) + if err != nil { + return nil, err + } + + return endpoint, nil +} + +// HNSListEndpointRequest makes a HNS call to query the list of available endpoints +func HNSListEndpointRequest() ([]HNSEndpoint, error) { + var endpoint []HNSEndpoint + err := hnsCall("GET", "/endpoints/", "", &endpoint) + if err != nil { + return nil, err + } + + return endpoint, nil +} + +// GetHNSEndpointByID get the Endpoint by ID +func GetHNSEndpointByID(endpointID string) (*HNSEndpoint, error) { + return HNSEndpointRequest("GET", endpointID, "") +} + +// GetHNSEndpointByName gets the endpoint filtered by Name +func GetHNSEndpointByName(endpointName string) (*HNSEndpoint, error) { + hnsResponse, err := HNSListEndpointRequest() + if err != nil { + return nil, err + } + for _, hnsEndpoint := range hnsResponse { + if hnsEndpoint.Name == endpointName { + return &hnsEndpoint, nil + } + } + return nil, EndpointNotFoundError{EndpointName: endpointName} +} + +// Create Endpoint by sending EndpointRequest to HNS. TODO: Create a separate HNS interface to place all these methods +func (endpoint *HNSEndpoint) Create() (*HNSEndpoint, error) { + operation := "Create" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + jsonString, err := json.Marshal(endpoint) + if err != nil { + return nil, err + } + return HNSEndpointRequest("POST", "", string(jsonString)) +} + +// Delete Endpoint by sending EndpointRequest to HNS +func (endpoint *HNSEndpoint) Delete() (*HNSEndpoint, error) { + operation := "Delete" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + return HNSEndpointRequest("DELETE", endpoint.Id, "") +} + +// Update Endpoint +func (endpoint *HNSEndpoint) Update() (*HNSEndpoint, error) { + operation := "Update" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + jsonString, err := json.Marshal(endpoint) + if err != nil { + return nil, err + } + err = hnsCall("POST", "/endpoints/"+endpoint.Id, string(jsonString), &endpoint) + + return endpoint, err +} + +// ApplyACLPolicy applies a set of ACL Policies on the Endpoint +func (endpoint *HNSEndpoint) ApplyACLPolicy(policies ...*ACLPolicy) error { + operation := "ApplyACLPolicy" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + for _, policy := range policies { + if policy == nil { + continue + } + jsonString, err := json.Marshal(policy) + if err != nil { + return err + } + endpoint.Policies = append(endpoint.Policies, jsonString) + } + + _, err := endpoint.Update() + return err +} + +// ContainerAttach attaches an endpoint to container +func (endpoint *HNSEndpoint) ContainerAttach(containerID string, compartmentID uint16) error { + operation := "ContainerAttach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + requestMessage := &EndpointAttachDetachRequest{ + ContainerID: containerID, + CompartmentID: compartmentID, + SystemType: ContainerType, + } + response := &EndpointResquestResponse{} + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) +} + +// ContainerDetach detaches an endpoint from container +func (endpoint *HNSEndpoint) ContainerDetach(containerID string) error { + operation := "ContainerDetach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + requestMessage := &EndpointAttachDetachRequest{ + ContainerID: containerID, + SystemType: ContainerType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) +} + +// HostAttach attaches a nic on the host +func (endpoint *HNSEndpoint) HostAttach(compartmentID uint16) error { + operation := "HostAttach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + requestMessage := &EndpointAttachDetachRequest{ + CompartmentID: compartmentID, + SystemType: HostType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) + +} + +// HostDetach detaches a nic on the host +func (endpoint *HNSEndpoint) HostDetach() error { + operation := "HostDetach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + requestMessage := &EndpointAttachDetachRequest{ + SystemType: HostType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) +} + +// VirtualMachineNICAttach attaches a endpoint to a virtual machine +func (endpoint *HNSEndpoint) VirtualMachineNICAttach(virtualMachineNICName string) error { + operation := "VirtualMachineNicAttach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + requestMessage := &EndpointAttachDetachRequest{ + VirtualNICName: virtualMachineNICName, + SystemType: VirtualMachineType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/attach", string(jsonString), &response) +} + +// VirtualMachineNICDetach detaches a endpoint from a virtual machine +func (endpoint *HNSEndpoint) VirtualMachineNICDetach() error { + operation := "VirtualMachineNicDetach" + title := "hcsshim::HNSEndpoint::" + operation + logrus.Debugf(title+" id=%s", endpoint.Id) + + requestMessage := &EndpointAttachDetachRequest{ + SystemType: VirtualMachineType, + } + response := &EndpointResquestResponse{} + + jsonString, err := json.Marshal(requestMessage) + if err != nil { + return err + } + return hnsCall("POST", "/endpoints/"+endpoint.Id+"/detach", string(jsonString), &response) +} diff --git a/vendor/github.com/Microsoft/hcsshim/hnsfuncs.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go similarity index 77% rename from vendor/github.com/Microsoft/hcsshim/hnsfuncs.go rename to vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go index 2c1b979ae..969d1b263 100644 --- a/vendor/github.com/Microsoft/hcsshim/hnsfuncs.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsfuncs.go @@ -1,9 +1,11 @@ -package hcsshim +package hns import ( "encoding/json" "fmt" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/interop" "github.com/sirupsen/logrus" ) @@ -13,9 +15,9 @@ func hnsCall(method, path, request string, returnResponse interface{}) error { err := _hnsCall(method, path, request, &responseBuffer) if err != nil { - return makeError(err, "hnsCall ", "") + return hcserror.New(err, "hnsCall ", "") } - response := convertAndFreeCoTaskMemString(responseBuffer) + response := interop.ConvertAndFreeCoTaskMemString(responseBuffer) hnsresponse := &hnsResponse{} if err = json.Unmarshal([]byte(response), &hnsresponse); err != nil { diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go new file mode 100644 index 000000000..a8d8cc56a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsglobals.go @@ -0,0 +1,28 @@ +package hns + +type HNSGlobals struct { + Version HNSVersion `json:"Version"` +} + +type HNSVersion struct { + Major int `json:"Major"` + Minor int `json:"Minor"` +} + +var ( + HNSVersion1803 = HNSVersion{Major: 7, Minor: 2} +) + +func GetHNSGlobals() (*HNSGlobals, error) { + var version HNSVersion + err := hnsCall("GET", "/globals/version", "", &version) + if err != nil { + return nil, err + } + + globals := &HNSGlobals{ + Version: version, + } + + return globals, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go new file mode 100644 index 000000000..7e859de91 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnsnetwork.go @@ -0,0 +1,141 @@ +package hns + +import ( + "encoding/json" + "net" + + "github.com/sirupsen/logrus" +) + +// Subnet is assoicated with a network and represents a list +// of subnets available to the network +type Subnet struct { + AddressPrefix string `json:",omitempty"` + GatewayAddress string `json:",omitempty"` + Policies []json.RawMessage `json:",omitempty"` +} + +// MacPool is assoicated with a network and represents a list +// of macaddresses available to the network +type MacPool struct { + StartMacAddress string `json:",omitempty"` + EndMacAddress string `json:",omitempty"` +} + +// HNSNetwork represents a network in HNS +type HNSNetwork struct { + Id string `json:"ID,omitempty"` + Name string `json:",omitempty"` + Type string `json:",omitempty"` + NetworkAdapterName string `json:",omitempty"` + SourceMac string `json:",omitempty"` + Policies []json.RawMessage `json:",omitempty"` + MacPools []MacPool `json:",omitempty"` + Subnets []Subnet `json:",omitempty"` + DNSSuffix string `json:",omitempty"` + DNSServerList string `json:",omitempty"` + DNSServerCompartment uint32 `json:",omitempty"` + ManagementIP string `json:",omitempty"` + AutomaticDNS bool `json:",omitempty"` +} + +type hnsNetworkResponse struct { + Success bool + Error string + Output HNSNetwork +} + +type hnsResponse struct { + Success bool + Error string + Output json.RawMessage +} + +// HNSNetworkRequest makes a call into HNS to update/query a single network +func HNSNetworkRequest(method, path, request string) (*HNSNetwork, error) { + var network HNSNetwork + err := hnsCall(method, "/networks/"+path, request, &network) + if err != nil { + return nil, err + } + + return &network, nil +} + +// HNSListNetworkRequest makes a HNS call to query the list of available networks +func HNSListNetworkRequest(method, path, request string) ([]HNSNetwork, error) { + var network []HNSNetwork + err := hnsCall(method, "/networks/"+path, request, &network) + if err != nil { + return nil, err + } + + return network, nil +} + +// GetHNSNetworkByID +func GetHNSNetworkByID(networkID string) (*HNSNetwork, error) { + return HNSNetworkRequest("GET", networkID, "") +} + +// GetHNSNetworkName filtered by Name +func GetHNSNetworkByName(networkName string) (*HNSNetwork, error) { + hsnnetworks, err := HNSListNetworkRequest("GET", "", "") + if err != nil { + return nil, err + } + for _, hnsnetwork := range hsnnetworks { + if hnsnetwork.Name == networkName { + return &hnsnetwork, nil + } + } + return nil, NetworkNotFoundError{NetworkName: networkName} +} + +// Create Network by sending NetworkRequest to HNS. +func (network *HNSNetwork) Create() (*HNSNetwork, error) { + operation := "Create" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s", network.Id) + + jsonString, err := json.Marshal(network) + if err != nil { + return nil, err + } + return HNSNetworkRequest("POST", "", string(jsonString)) +} + +// Delete Network by sending NetworkRequest to HNS +func (network *HNSNetwork) Delete() (*HNSNetwork, error) { + operation := "Delete" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s", network.Id) + + return HNSNetworkRequest("DELETE", network.Id, "") +} + +// Creates an endpoint on the Network. +func (network *HNSNetwork) NewEndpoint(ipAddress net.IP, macAddress net.HardwareAddr) *HNSEndpoint { + return &HNSEndpoint{ + VirtualNetwork: network.Id, + IPAddress: ipAddress, + MacAddress: string(macAddress), + } +} + +func (network *HNSNetwork) CreateEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) { + operation := "CreateEndpoint" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s, endpointId=%s", network.Id, endpoint.Id) + + endpoint.VirtualNetwork = network.Id + return endpoint.Create() +} + +func (network *HNSNetwork) CreateRemoteEndpoint(endpoint *HNSEndpoint) (*HNSEndpoint, error) { + operation := "CreateRemoteEndpoint" + title := "hcsshim::HNSNetwork::" + operation + logrus.Debugf(title+" id=%s", network.Id) + endpoint.IsRemoteEndpoint = true + return network.CreateEndpoint(endpoint) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go new file mode 100644 index 000000000..2318a4fce --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicy.go @@ -0,0 +1,98 @@ +package hns + +// Type of Request Support in ModifySystem +type PolicyType string + +// RequestType const +const ( + Nat PolicyType = "NAT" + ACL PolicyType = "ACL" + PA PolicyType = "PA" + VLAN PolicyType = "VLAN" + VSID PolicyType = "VSID" + VNet PolicyType = "VNET" + L2Driver PolicyType = "L2Driver" + Isolation PolicyType = "Isolation" + QOS PolicyType = "QOS" + OutboundNat PolicyType = "OutBoundNAT" + ExternalLoadBalancer PolicyType = "ELB" + Route PolicyType = "ROUTE" +) + +type NatPolicy struct { + Type PolicyType `json:"Type"` + Protocol string + InternalPort uint16 + ExternalPort uint16 +} + +type QosPolicy struct { + Type PolicyType `json:"Type"` + MaximumOutgoingBandwidthInBytes uint64 +} + +type IsolationPolicy struct { + Type PolicyType `json:"Type"` + VLAN uint + VSID uint + InDefaultIsolation bool +} + +type VlanPolicy struct { + Type PolicyType `json:"Type"` + VLAN uint +} + +type VsidPolicy struct { + Type PolicyType `json:"Type"` + VSID uint +} + +type PaPolicy struct { + Type PolicyType `json:"Type"` + PA string `json:"PA"` +} + +type OutboundNatPolicy struct { + Policy + VIP string `json:"VIP,omitempty"` + Exceptions []string `json:"ExceptionList,omitempty"` +} + +type ActionType string +type DirectionType string +type RuleType string + +const ( + Allow ActionType = "Allow" + Block ActionType = "Block" + + In DirectionType = "In" + Out DirectionType = "Out" + + Host RuleType = "Host" + Switch RuleType = "Switch" +) + +type ACLPolicy struct { + Type PolicyType `json:"Type"` + Id string `json:"Id,omitempty"` + Protocol uint16 + Protocols string `json:"Protocols,omitempty"` + InternalPort uint16 + Action ActionType + Direction DirectionType + LocalAddresses string + RemoteAddresses string + LocalPorts string `json:"LocalPorts,omitempty"` + LocalPort uint16 + RemotePorts string `json:"RemotePorts,omitempty"` + RemotePort uint16 + RuleType RuleType `json:"RuleType,omitempty"` + Priority uint16 + ServiceName string +} + +type Policy struct { + Type PolicyType `json:"Type"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go new file mode 100644 index 000000000..ff7369e6f --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnspolicylist.go @@ -0,0 +1,200 @@ +package hns + +import ( + "encoding/json" + + "github.com/sirupsen/logrus" +) + +// RoutePolicy is a structure defining schema for Route based Policy +type RoutePolicy struct { + Policy + DestinationPrefix string `json:"DestinationPrefix,omitempty"` + NextHop string `json:"NextHop,omitempty"` + EncapEnabled bool `json:"NeedEncap,omitempty"` +} + +// ELBPolicy is a structure defining schema for ELB LoadBalancing based Policy +type ELBPolicy struct { + LBPolicy + SourceVIP string `json:"SourceVIP,omitempty"` + VIPs []string `json:"VIPs,omitempty"` + ILB bool `json:"ILB,omitempty"` +} + +// LBPolicy is a structure defining schema for LoadBalancing based Policy +type LBPolicy struct { + Policy + Protocol uint16 `json:"Protocol,omitempty"` + InternalPort uint16 + ExternalPort uint16 +} + +// PolicyList is a structure defining schema for Policy list request +type PolicyList struct { + ID string `json:"ID,omitempty"` + EndpointReferences []string `json:"References,omitempty"` + Policies []json.RawMessage `json:"Policies,omitempty"` +} + +// HNSPolicyListRequest makes a call into HNS to update/query a single network +func HNSPolicyListRequest(method, path, request string) (*PolicyList, error) { + var policy PolicyList + err := hnsCall(method, "/policylists/"+path, request, &policy) + if err != nil { + return nil, err + } + + return &policy, nil +} + +// HNSListPolicyListRequest gets all the policy list +func HNSListPolicyListRequest() ([]PolicyList, error) { + var plist []PolicyList + err := hnsCall("GET", "/policylists/", "", &plist) + if err != nil { + return nil, err + } + + return plist, nil +} + +// PolicyListRequest makes a HNS call to modify/query a network policy list +func PolicyListRequest(method, path, request string) (*PolicyList, error) { + policylist := &PolicyList{} + err := hnsCall(method, "/policylists/"+path, request, &policylist) + if err != nil { + return nil, err + } + + return policylist, nil +} + +// GetPolicyListByID get the policy list by ID +func GetPolicyListByID(policyListID string) (*PolicyList, error) { + return PolicyListRequest("GET", policyListID, "") +} + +// Create PolicyList by sending PolicyListRequest to HNS. +func (policylist *PolicyList) Create() (*PolicyList, error) { + operation := "Create" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s", policylist.ID) + jsonString, err := json.Marshal(policylist) + if err != nil { + return nil, err + } + return PolicyListRequest("POST", "", string(jsonString)) +} + +// Delete deletes PolicyList +func (policylist *PolicyList) Delete() (*PolicyList, error) { + operation := "Delete" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s", policylist.ID) + + return PolicyListRequest("DELETE", policylist.ID, "") +} + +// AddEndpoint add an endpoint to a Policy List +func (policylist *PolicyList) AddEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) { + operation := "AddEndpoint" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id) + + _, err := policylist.Delete() + if err != nil { + return nil, err + } + + // Add Endpoint to the Existing List + policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) + + return policylist.Create() +} + +// RemoveEndpoint removes an endpoint from the Policy List +func (policylist *PolicyList) RemoveEndpoint(endpoint *HNSEndpoint) (*PolicyList, error) { + operation := "RemoveEndpoint" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" id=%s, endpointId:%s", policylist.ID, endpoint.Id) + + _, err := policylist.Delete() + if err != nil { + return nil, err + } + + elementToRemove := "/endpoints/" + endpoint.Id + + var references []string + + for _, endpointReference := range policylist.EndpointReferences { + if endpointReference == elementToRemove { + continue + } + references = append(references, endpointReference) + } + policylist.EndpointReferences = references + return policylist.Create() +} + +// AddLoadBalancer policy list for the specified endpoints +func AddLoadBalancer(endpoints []HNSEndpoint, isILB bool, sourceVIP, vip string, protocol uint16, internalPort uint16, externalPort uint16) (*PolicyList, error) { + operation := "AddLoadBalancer" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" endpointId=%v, isILB=%v, sourceVIP=%s, vip=%s, protocol=%v, internalPort=%v, externalPort=%v", endpoints, isILB, sourceVIP, vip, protocol, internalPort, externalPort) + + policylist := &PolicyList{} + + elbPolicy := &ELBPolicy{ + SourceVIP: sourceVIP, + ILB: isILB, + } + + if len(vip) > 0 { + elbPolicy.VIPs = []string{vip} + } + elbPolicy.Type = ExternalLoadBalancer + elbPolicy.Protocol = protocol + elbPolicy.InternalPort = internalPort + elbPolicy.ExternalPort = externalPort + + for _, endpoint := range endpoints { + policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) + } + + jsonString, err := json.Marshal(elbPolicy) + if err != nil { + return nil, err + } + policylist.Policies = append(policylist.Policies, jsonString) + return policylist.Create() +} + +// AddRoute adds route policy list for the specified endpoints +func AddRoute(endpoints []HNSEndpoint, destinationPrefix string, nextHop string, encapEnabled bool) (*PolicyList, error) { + operation := "AddRoute" + title := "hcsshim::PolicyList::" + operation + logrus.Debugf(title+" destinationPrefix:%s", destinationPrefix) + + policylist := &PolicyList{} + + rPolicy := &RoutePolicy{ + DestinationPrefix: destinationPrefix, + NextHop: nextHop, + EncapEnabled: encapEnabled, + } + rPolicy.Type = Route + + for _, endpoint := range endpoints { + policylist.EndpointReferences = append(policylist.EndpointReferences, "/endpoints/"+endpoint.Id) + } + + jsonString, err := json.Marshal(rPolicy) + if err != nil { + return nil, err + } + + policylist.Policies = append(policylist.Policies, jsonString) + return policylist.Create() +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go new file mode 100644 index 000000000..d5efba7f2 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/hnssupport.go @@ -0,0 +1,49 @@ +package hns + +import ( + "github.com/sirupsen/logrus" +) + +type HNSSupportedFeatures struct { + Acl HNSAclFeatures `json:"ACL"` +} + +type HNSAclFeatures struct { + AclAddressLists bool `json:"AclAddressLists"` + AclNoHostRulePriority bool `json:"AclHostRulePriority"` + AclPortRanges bool `json:"AclPortRanges"` + AclRuleId bool `json:"AclRuleId"` +} + +func GetHNSSupportedFeatures() HNSSupportedFeatures { + var hnsFeatures HNSSupportedFeatures + + globals, err := GetHNSGlobals() + if err != nil { + // Expected on pre-1803 builds, all features will be false/unsupported + logrus.Debugf("Unable to obtain HNS globals: %s", err) + return hnsFeatures + } + + hnsFeatures.Acl = HNSAclFeatures{ + AclAddressLists: isHNSFeatureSupported(globals.Version, HNSVersion1803), + AclNoHostRulePriority: isHNSFeatureSupported(globals.Version, HNSVersion1803), + AclPortRanges: isHNSFeatureSupported(globals.Version, HNSVersion1803), + AclRuleId: isHNSFeatureSupported(globals.Version, HNSVersion1803), + } + + return hnsFeatures +} + +func isHNSFeatureSupported(currentVersion HNSVersion, minVersionSupported HNSVersion) bool { + if currentVersion.Major < minVersionSupported.Major { + return false + } + if currentVersion.Major > minVersionSupported.Major { + return true + } + if currentVersion.Minor < minVersionSupported.Minor { + return false + } + return true +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go new file mode 100644 index 000000000..45e2281b0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/namespace.go @@ -0,0 +1,110 @@ +package hns + +import ( + "encoding/json" + "fmt" + "os" + "path" + "strings" +) + +type namespaceRequest struct { + IsDefault bool `json:",omitempty"` +} + +type namespaceEndpointRequest struct { + ID string `json:"Id"` +} + +type NamespaceResource struct { + Type string + Data json.RawMessage +} + +type namespaceResourceRequest struct { + Type string + Data interface{} +} + +type Namespace struct { + ID string + IsDefault bool `json:",omitempty"` + ResourceList []NamespaceResource `json:",omitempty"` +} + +func issueNamespaceRequest(id *string, method, subpath string, request interface{}) (*Namespace, error) { + var err error + hnspath := "/namespaces/" + if id != nil { + hnspath = path.Join(hnspath, *id) + } + if subpath != "" { + hnspath = path.Join(hnspath, subpath) + } + var reqJSON []byte + if request != nil { + if reqJSON, err = json.Marshal(request); err != nil { + return nil, err + } + } + var ns Namespace + err = hnsCall(method, hnspath, string(reqJSON), &ns) + if err != nil { + if strings.Contains(err.Error(), "Element not found.") { + return nil, os.ErrNotExist + } + return nil, fmt.Errorf("%s %s: %s", method, hnspath, err) + } + return &ns, err +} + +func CreateNamespace() (string, error) { + req := namespaceRequest{} + ns, err := issueNamespaceRequest(nil, "POST", "", &req) + if err != nil { + return "", err + } + return ns.ID, nil +} + +func RemoveNamespace(id string) error { + _, err := issueNamespaceRequest(&id, "DELETE", "", nil) + return err +} + +func GetNamespaceEndpoints(id string) ([]string, error) { + ns, err := issueNamespaceRequest(&id, "GET", "", nil) + if err != nil { + return nil, err + } + var endpoints []string + for _, rsrc := range ns.ResourceList { + if rsrc.Type == "Endpoint" { + var endpoint namespaceEndpointRequest + err = json.Unmarshal(rsrc.Data, &endpoint) + if err != nil { + return nil, fmt.Errorf("unmarshal endpoint: %s", err) + } + endpoints = append(endpoints, endpoint.ID) + } + } + return endpoints, nil +} + +func AddNamespaceEndpoint(id string, endpointID string) error { + resource := namespaceResourceRequest{ + Type: "Endpoint", + Data: namespaceEndpointRequest{endpointID}, + } + _, err := issueNamespaceRequest(&id, "POST", "addresource", &resource) + return err +} + +func RemoveNamespaceEndpoint(id string, endpointID string) error { + resource := namespaceResourceRequest{ + Type: "Endpoint", + Data: namespaceEndpointRequest{endpointID}, + } + _, err := issueNamespaceRequest(&id, "POST", "removeresource", &resource) + return err +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go new file mode 100644 index 000000000..863e3429c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/hns/zsyscall_windows.go @@ -0,0 +1,74 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package hns + +import ( + "syscall" + "unsafe" + + "github.com/Microsoft/hcsshim/internal/interop" + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + + procHNSCall = modvmcompute.NewProc("HNSCall") +) + +func _hnsCall(method string, path string, object string, response **uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(method) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + var _p2 *uint16 + _p2, hr = syscall.UTF16PtrFromString(object) + if hr != nil { + return + } + return __hnsCall(_p0, _p1, _p2, response) +} + +func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16) (hr error) { + if hr = procHNSCall.Find(); hr != nil { + return + } + 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) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go b/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go new file mode 100644 index 000000000..f10c88d08 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/interop/interop.go @@ -0,0 +1,27 @@ +package interop + +import ( + "syscall" + "unsafe" +) + +//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go interop.go + +//sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree + +func ConvertAndFreeCoTaskMemString(buffer *uint16) string { + str := syscall.UTF16ToString((*[1 << 29]uint16)(unsafe.Pointer(buffer))[:]) + coTaskMemFree(unsafe.Pointer(buffer)) + return str +} + +func ConvertAndFreeCoTaskMemBytes(buffer *uint16) []byte { + return []byte(ConvertAndFreeCoTaskMemString(buffer)) +} + +func Win32FromHresult(hr uintptr) syscall.Errno { + if hr&0x1fff0000 == 0x00070000 { + return syscall.Errno(hr & 0xffff) + } + return syscall.Errno(hr) +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go new file mode 100644 index 000000000..32f4e070c --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/interop/zsyscall_windows.go @@ -0,0 +1,48 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package interop + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modole32 = windows.NewLazySystemDLL("ole32.dll") + + procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") +) + +func coTaskMemFree(buffer unsafe.Pointer) { + syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(buffer), 0, 0) + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go b/vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go new file mode 100644 index 000000000..e5b8b85e0 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/longpath/longpath.go @@ -0,0 +1,24 @@ +package longpath + +import ( + "path/filepath" + "strings" +) + +// LongAbs makes a path absolute and returns it in NT long path form. +func LongAbs(path string) (string, error) { + if strings.HasPrefix(path, `\\?\`) || strings.HasPrefix(path, `\\.\`) { + return path, nil + } + if !filepath.IsAbs(path) { + absPath, err := filepath.Abs(path) + if err != nil { + return "", err + } + path = absPath + } + if strings.HasPrefix(path, `\\`) { + return `\\?\UNC\` + path[2:], nil + } + return `\\?\` + path, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go b/vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go new file mode 100644 index 000000000..7e95efb30 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/mergemaps/merge.go @@ -0,0 +1,52 @@ +package mergemaps + +import "encoding/json" + +// Merge recursively merges map `fromMap` into map `ToMap`. Any pre-existing values +// in ToMap are overwritten. Values in fromMap are added to ToMap. +// From http://stackoverflow.com/questions/40491438/merging-two-json-strings-in-golang +func Merge(fromMap, ToMap interface{}) interface{} { + switch fromMap := fromMap.(type) { + case map[string]interface{}: + ToMap, ok := ToMap.(map[string]interface{}) + if !ok { + return fromMap + } + for keyToMap, valueToMap := range ToMap { + if valueFromMap, ok := fromMap[keyToMap]; ok { + fromMap[keyToMap] = Merge(valueFromMap, valueToMap) + } else { + fromMap[keyToMap] = valueToMap + } + } + case nil: + // merge(nil, map[string]interface{...}) -> map[string]interface{...} + ToMap, ok := ToMap.(map[string]interface{}) + if ok { + return ToMap + } + } + return fromMap +} + +// MergeJSON merges the contents of a JSON string into an object representation, +// returning a new object suitable for translating to JSON. +func MergeJSON(object interface{}, additionalJSON []byte) (interface{}, error) { + if len(additionalJSON) == 0 { + return object, nil + } + objectJSON, err := json.Marshal(object) + if err != nil { + return nil, err + } + var objectMap, newMap map[string]interface{} + err = json.Unmarshal(objectJSON, &objectMap) + if err != nil { + return nil, err + } + err = json.Unmarshal(additionalJSON, &newMap) + if err != nil { + return nil, err + } + return Merge(newMap, objectMap), nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/safeopen.go b/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go similarity index 81% rename from vendor/github.com/Microsoft/hcsshim/safeopen.go rename to vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go index 5356456b9..0c0b1159f 100644 --- a/vendor/github.com/Microsoft/hcsshim/safeopen.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/safefile/safeopen.go @@ -1,4 +1,4 @@ -package hcsshim +package safefile import ( "errors" @@ -10,9 +10,13 @@ import ( "unicode/utf16" "unsafe" + "github.com/Microsoft/hcsshim/internal/longpath" + winio "github.com/Microsoft/go-winio" ) +//go:generate go run $GOROOT\src\syscall\mksyscall_windows.go -output zsyscall_windows.go safeopen.go + //sys ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) = ntdll.NtCreateFile //sys ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) = ntdll.NtSetInformationFile //sys rtlNtStatusToDosError(status uint32) (winerr error) = ntdll.RtlNtStatusToDosErrorNoTeb @@ -53,28 +57,28 @@ const ( _FileLinkInformation = 11 _FileDispositionInformationEx = 64 - _FILE_READ_ATTRIBUTES = 0x0080 - _FILE_WRITE_ATTRIBUTES = 0x0100 - _DELETE = 0x10000 + FILE_READ_ATTRIBUTES = 0x0080 + FILE_WRITE_ATTRIBUTES = 0x0100 + DELETE = 0x10000 - _FILE_OPEN = 1 - _FILE_CREATE = 2 + FILE_OPEN = 1 + FILE_CREATE = 2 - _FILE_DIRECTORY_FILE = 0x00000001 - _FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020 - _FILE_DELETE_ON_CLOSE = 0x00001000 - _FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000 - _FILE_OPEN_REPARSE_POINT = 0x00200000 + FILE_DIRECTORY_FILE = 0x00000001 + FILE_SYNCHRONOUS_IO_NONALERT = 0x00000020 + FILE_DELETE_ON_CLOSE = 0x00001000 + FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000 + FILE_OPEN_REPARSE_POINT = 0x00200000 - _FILE_DISPOSITION_DELETE = 0x00000001 + FILE_DISPOSITION_DELETE = 0x00000001 _OBJ_DONT_REPARSE = 0x1000 _STATUS_REPARSE_POINT_ENCOUNTERED = 0xC000050B ) -func openRoot(path string) (*os.File, error) { - longpath, err := makeLongAbsPath(path) +func OpenRoot(path string) (*os.File, error) { + longpath, err := longpath.LongAbs(path) if err != nil { return nil, err } @@ -141,7 +145,7 @@ func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFl 0, shareFlags, createDisposition, - _FILE_OPEN_FOR_BACKUP_INTENT|_FILE_SYNCHRONOUS_IO_NONALERT|flags, + FILE_OPEN_FOR_BACKUP_INTENT|FILE_SYNCHRONOUS_IO_NONALERT|flags, nil, 0, ) @@ -149,7 +153,7 @@ func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFl return nil, rtlNtStatusToDosError(status) } - fullPath, err := makeLongAbsPath(filepath.Join(root.Name(), path)) + fullPath, err := longpath.LongAbs(filepath.Join(root.Name(), path)) if err != nil { syscall.Close(syscall.Handle(h)) return nil, err @@ -158,9 +162,9 @@ func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFl return os.NewFile(h, fullPath), nil } -// openRelative opens a relative path from the given root, failing if +// OpenRelative opens a relative path from the given root, failing if // any of the intermediate path components are reparse points. -func openRelative(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) { +func OpenRelative(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) { f, err := openRelativeInternal(path, root, accessMask, shareFlags, createDisposition, flags) if err != nil { err = &os.PathError{Op: "open", Path: filepath.Join(root.Name(), path), Err: err} @@ -168,17 +172,17 @@ func openRelative(path string, root *os.File, accessMask uint32, shareFlags uint return f, err } -// linkRelative creates a hard link from oldname to newname (relative to oldroot +// LinkRelative creates a hard link from oldname to newname (relative to oldroot // and newroot), failing if any of the intermediate path components are reparse // points. -func linkRelative(oldname string, oldroot *os.File, newname string, newroot *os.File) error { +func LinkRelative(oldname string, oldroot *os.File, newname string, newroot *os.File) error { // Open the old file. oldf, err := openRelativeInternal( oldname, oldroot, syscall.FILE_WRITE_ATTRIBUTES, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - _FILE_OPEN, + FILE_OPEN, 0, ) if err != nil { @@ -195,8 +199,8 @@ func linkRelative(oldname string, oldroot *os.File, newname string, newroot *os. newroot, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - _FILE_OPEN, - _FILE_DIRECTORY_FILE) + FILE_OPEN, + FILE_DIRECTORY_FILE) if err != nil { return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: err} } @@ -248,7 +252,7 @@ func linkRelative(oldname string, oldroot *os.File, newname string, newroot *os. // deleteOnClose marks a file to be deleted when the handle is closed. func deleteOnClose(f *os.File) error { - disposition := fileDispositionInformationEx{Flags: _FILE_DISPOSITION_DELETE} + disposition := fileDispositionInformationEx{Flags: FILE_DISPOSITION_DELETE} var iosb ioStatusBlock status := ntSetInformationFile( f.Fd(), @@ -281,16 +285,16 @@ func clearReadOnly(f *os.File) error { return winio.SetFileBasicInfo(f, &sbi) } -// removeRelative removes a file or directory relative to a root, failing if any +// RemoveRelative removes a file or directory relative to a root, failing if any // intermediate path components are reparse points. -func removeRelative(path string, root *os.File) error { +func RemoveRelative(path string, root *os.File) error { f, err := openRelativeInternal( path, root, - _FILE_READ_ATTRIBUTES|_FILE_WRITE_ATTRIBUTES|_DELETE, + FILE_READ_ATTRIBUTES|FILE_WRITE_ATTRIBUTES|DELETE, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - _FILE_OPEN, - _FILE_OPEN_REPARSE_POINT) + FILE_OPEN, + FILE_OPEN_REPARSE_POINT) if err == nil { defer f.Close() err = deleteOnClose(f) @@ -306,10 +310,10 @@ func removeRelative(path string, root *os.File) error { return nil } -// removeAllRelative removes a directory tree relative to a root, failing if any +// RemoveAllRelative removes a directory tree relative to a root, failing if any // intermediate path components are reparse points. -func removeAllRelative(path string, root *os.File) error { - fi, err := lstatRelative(path, root) +func RemoveAllRelative(path string, root *os.File) error { + fi, err := LstatRelative(path, root) if err != nil { if os.IsNotExist(err) { return nil @@ -319,7 +323,7 @@ func removeAllRelative(path string, root *os.File) error { fileAttributes := fi.Sys().(*syscall.Win32FileAttributeData).FileAttributes if fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY == 0 || fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0 { // If this is a reparse point, it can't have children. Simple remove will do. - err := removeRelative(path, root) + err := RemoveRelative(path, root) if err == nil || os.IsNotExist(err) { return nil } @@ -327,7 +331,7 @@ func removeAllRelative(path string, root *os.File) error { } // It is necessary to use os.Open as Readdirnames does not work with - // openRelative. This is safe because the above lstatrelative fails + // OpenRelative. This is safe because the above lstatrelative fails // if the target is outside the root, and we know this is not a // symlink from the above FILE_ATTRIBUTE_REPARSE_POINT check. fd, err := os.Open(filepath.Join(root.Name(), path)) @@ -344,7 +348,7 @@ func removeAllRelative(path string, root *os.File) error { for { names, err1 := fd.Readdirnames(100) for _, name := range names { - err1 := removeAllRelative(path+string(os.PathSeparator)+name, root) + err1 := RemoveAllRelative(path+string(os.PathSeparator)+name, root) if err == nil { err = err1 } @@ -363,7 +367,7 @@ func removeAllRelative(path string, root *os.File) error { fd.Close() // Remove directory. - err1 := removeRelative(path, root) + err1 := RemoveRelative(path, root) if err1 == nil || os.IsNotExist(err1) { return nil } @@ -373,16 +377,16 @@ func removeAllRelative(path string, root *os.File) error { return err } -// mkdirRelative creates a directory relative to a root, failing if any +// MkdirRelative creates a directory relative to a root, failing if any // intermediate path components are reparse points. -func mkdirRelative(path string, root *os.File) error { +func MkdirRelative(path string, root *os.File) error { f, err := openRelativeInternal( path, root, 0, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - _FILE_CREATE, - _FILE_DIRECTORY_FILE) + FILE_CREATE, + FILE_DIRECTORY_FILE) if err == nil { f.Close() } else { @@ -391,16 +395,16 @@ func mkdirRelative(path string, root *os.File) error { return err } -// lstatRelative performs a stat operation on a file relative to a root, failing +// LstatRelative performs a stat operation on a file relative to a root, failing // if any intermediate path components are reparse points. -func lstatRelative(path string, root *os.File) (os.FileInfo, error) { +func LstatRelative(path string, root *os.File) (os.FileInfo, error) { f, err := openRelativeInternal( path, root, - _FILE_READ_ATTRIBUTES, + FILE_READ_ATTRIBUTES, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - _FILE_OPEN, - _FILE_OPEN_REPARSE_POINT) + FILE_OPEN, + FILE_OPEN_REPARSE_POINT) if err != nil { return nil, &os.PathError{Op: "stat", Path: filepath.Join(root.Name(), path), Err: err} } @@ -408,16 +412,16 @@ func lstatRelative(path string, root *os.File) (os.FileInfo, error) { return f.Stat() } -// ensureNotReparsePointRelative validates that a given file (relative to a +// EnsureNotReparsePointRelative validates that a given file (relative to a // root) and all intermediate path components are not a reparse points. -func ensureNotReparsePointRelative(path string, root *os.File) error { +func EnsureNotReparsePointRelative(path string, root *os.File) error { // Perform an open with OBJ_DONT_REPARSE but without specifying FILE_OPEN_REPARSE_POINT. - f, err := openRelative( + f, err := OpenRelative( path, root, 0, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, - _FILE_OPEN, + FILE_OPEN, 0) if err != nil { return err diff --git a/vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go new file mode 100644 index 000000000..776adbe7a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/safefile/zsyscall_windows.go @@ -0,0 +1,79 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package safefile + +import ( + "syscall" + "unsafe" + + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modntdll = windows.NewLazySystemDLL("ntdll.dll") + modkernel32 = windows.NewLazySystemDLL("kernel32.dll") + + procNtCreateFile = modntdll.NewProc("NtCreateFile") + procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile") + procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") + procLocalAlloc = modkernel32.NewProc("LocalAlloc") + procLocalFree = modkernel32.NewProc("LocalFree") +) + +func ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) { + r0, _, _ := syscall.Syscall12(procNtCreateFile.Addr(), 11, uintptr(unsafe.Pointer(handle)), uintptr(accessMask), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(allocationSize)), uintptr(fileAttributes), uintptr(shareAccess), uintptr(createDisposition), uintptr(createOptions), uintptr(unsafe.Pointer(eaBuffer)), uintptr(eaLength), 0) + status = uint32(r0) + return +} + +func ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) { + r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(information), uintptr(length), uintptr(class), 0) + status = uint32(r0) + return +} + +func rtlNtStatusToDosError(status uint32) (winerr error) { + r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) + if r0 != 0 { + winerr = syscall.Errno(r0) + } + return +} + +func localAlloc(flags uint32, size int) (ptr uintptr) { + r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(size), 0) + ptr = uintptr(r0) + return +} + +func localFree(ptr uintptr) { + syscall.Syscall(procLocalFree.Addr(), 1, uintptr(ptr), 0, 0) + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go b/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go new file mode 100644 index 000000000..6fa3bbc73 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/schema1/schema1.go @@ -0,0 +1,228 @@ +package schema1 + +import ( + "encoding/json" + "time" +) + +// ProcessConfig is used as both the input of Container.CreateProcess +// and to convert the parameters to JSON for passing onto the HCS +type ProcessConfig struct { + ApplicationName string `json:",omitempty"` + CommandLine string `json:",omitempty"` + CommandArgs []string `json:",omitempty"` // Used by Linux Containers on Windows + User string `json:",omitempty"` + WorkingDirectory string `json:",omitempty"` + Environment map[string]string `json:",omitempty"` + EmulateConsole bool `json:",omitempty"` + CreateStdInPipe bool `json:",omitempty"` + CreateStdOutPipe bool `json:",omitempty"` + CreateStdErrPipe bool `json:",omitempty"` + ConsoleSize [2]uint `json:",omitempty"` + CreateInUtilityVm bool `json:",omitempty"` // Used by Linux Containers on Windows + OCISpecification *json.RawMessage `json:",omitempty"` // Used by Linux Containers on Windows +} + +type Layer struct { + ID string + Path string +} + +type MappedDir struct { + HostPath string + ContainerPath string + ReadOnly bool + BandwidthMaximum uint64 + IOPSMaximum uint64 + CreateInUtilityVM bool + // LinuxMetadata - Support added in 1803/RS4+. + LinuxMetadata bool `json:",omitempty"` +} + +type MappedPipe struct { + HostPath string + ContainerPipeName string +} + +type HvRuntime struct { + ImagePath string `json:",omitempty"` + SkipTemplate bool `json:",omitempty"` + LinuxInitrdFile string `json:",omitempty"` // File under ImagePath on host containing an initrd image for starting a Linux utility VM + LinuxKernelFile string `json:",omitempty"` // File under ImagePath on host containing a kernel for starting a Linux utility VM + LinuxBootParameters string `json:",omitempty"` // Additional boot parameters for starting a Linux Utility VM in initrd mode + BootSource string `json:",omitempty"` // "Vhd" for Linux Utility VM booting from VHD + WritableBootSource bool `json:",omitempty"` // Linux Utility VM booting from VHD +} + +type MappedVirtualDisk struct { + HostPath string `json:",omitempty"` // Path to VHD on the host + ContainerPath string // Platform-specific mount point path in the container + CreateInUtilityVM bool `json:",omitempty"` + ReadOnly bool `json:",omitempty"` + Cache string `json:",omitempty"` // "" (Unspecified); "Disabled"; "Enabled"; "Private"; "PrivateAllowSharing" + AttachOnly bool `json:",omitempty:` +} + +// AssignedDevice represents a device that has been directly assigned to a container +// +// NOTE: Support added in RS5 +type AssignedDevice struct { + // InterfaceClassGUID of the device to assign to container. + InterfaceClassGUID string `json:"InterfaceClassGuid,omitempty"` +} + +// ContainerConfig is used as both the input of CreateContainer +// and to convert the parameters to JSON for passing onto the HCS +type ContainerConfig struct { + SystemType string // HCS requires this to be hard-coded to "Container" + Name string // Name of the container. We use the docker ID. + Owner string `json:",omitempty"` // The management platform that created this container + VolumePath string `json:",omitempty"` // Windows volume path for scratch space. Used by Windows Server Containers only. Format \\?\\Volume{GUID} + IgnoreFlushesDuringBoot bool `json:",omitempty"` // Optimization hint for container startup in Windows + LayerFolderPath string `json:",omitempty"` // Where the layer folders are located. Used by Windows Server Containers only. Format %root%\windowsfilter\containerID + Layers []Layer // List of storage layers. Required for Windows Server and Hyper-V Containers. Format ID=GUID;Path=%root%\windowsfilter\layerID + Credentials string `json:",omitempty"` // Credentials information + ProcessorCount uint32 `json:",omitempty"` // Number of processors to assign to the container. + ProcessorWeight uint64 `json:",omitempty"` // CPU shares (relative weight to other containers with cpu shares). Range is from 1 to 10000. A value of 0 results in default shares. + ProcessorMaximum int64 `json:",omitempty"` // Specifies the portion of processor cycles that this container can use as a percentage times 100. Range is from 1 to 10000. A value of 0 results in no limit. + StorageIOPSMaximum uint64 `json:",omitempty"` // Maximum Storage IOPS + StorageBandwidthMaximum uint64 `json:",omitempty"` // Maximum Storage Bandwidth in bytes per second + StorageSandboxSize uint64 `json:",omitempty"` // Size in bytes that the container system drive should be expanded to if smaller + MemoryMaximumInMB int64 `json:",omitempty"` // Maximum memory available to the container in Megabytes + HostName string `json:",omitempty"` // Hostname + MappedDirectories []MappedDir `json:",omitempty"` // List of mapped directories (volumes/mounts) + MappedPipes []MappedPipe `json:",omitempty"` // List of mapped Windows named pipes + HvPartition bool // True if it a Hyper-V Container + NetworkSharedContainerName string `json:",omitempty"` // Name (ID) of the container that we will share the network stack with. + EndpointList []string `json:",omitempty"` // List of networking endpoints to be attached to container + HvRuntime *HvRuntime `json:",omitempty"` // Hyper-V container settings. Used by Hyper-V containers only. Format ImagePath=%root%\BaseLayerID\UtilityVM + Servicing bool `json:",omitempty"` // True if this container is for servicing + AllowUnqualifiedDNSQuery bool `json:",omitempty"` // True to allow unqualified DNS name resolution + DNSSearchList string `json:",omitempty"` // Comma seperated list of DNS suffixes to use for name resolution + ContainerType string `json:",omitempty"` // "Linux" for Linux containers on Windows. Omitted otherwise. + TerminateOnLastHandleClosed bool `json:",omitempty"` // Should HCS terminate the container once all handles have been closed + MappedVirtualDisks []MappedVirtualDisk `json:",omitempty"` // Array of virtual disks to mount at start + AssignedDevices []AssignedDevice `json:",omitempty"` // Array of devices to assign. NOTE: Support added in RS5 +} + +type ComputeSystemQuery struct { + IDs []string `json:"Ids,omitempty"` + Types []string `json:",omitempty"` + Names []string `json:",omitempty"` + Owners []string `json:",omitempty"` +} + +type PropertyType string + +const ( + PropertyTypeStatistics PropertyType = "Statistics" + PropertyTypeProcessList = "ProcessList" + PropertyTypeMappedVirtualDisk = "MappedVirtualDisk" +) + +type PropertyQuery struct { + PropertyTypes []PropertyType `json:",omitempty"` +} + +// ContainerProperties holds the properties for a container and the processes running in that container +type ContainerProperties struct { + ID string `json:"Id"` + State string + Name string + SystemType string + Owner string + SiloGUID string `json:"SiloGuid,omitempty"` + RuntimeID string `json:"RuntimeId,omitempty"` + IsRuntimeTemplate bool `json:",omitempty"` + RuntimeImagePath string `json:",omitempty"` + Stopped bool `json:",omitempty"` + ExitType string `json:",omitempty"` + AreUpdatesPending bool `json:",omitempty"` + ObRoot string `json:",omitempty"` + Statistics Statistics `json:",omitempty"` + ProcessList []ProcessListItem `json:",omitempty"` + MappedVirtualDiskControllers map[int]MappedVirtualDiskController `json:",omitempty"` +} + +// MemoryStats holds the memory statistics for a container +type MemoryStats struct { + UsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"` + UsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"` + UsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"` +} + +// ProcessorStats holds the processor statistics for a container +type ProcessorStats struct { + TotalRuntime100ns uint64 `json:",omitempty"` + RuntimeUser100ns uint64 `json:",omitempty"` + RuntimeKernel100ns uint64 `json:",omitempty"` +} + +// StorageStats holds the storage statistics for a container +type StorageStats struct { + ReadCountNormalized uint64 `json:",omitempty"` + ReadSizeBytes uint64 `json:",omitempty"` + WriteCountNormalized uint64 `json:",omitempty"` + WriteSizeBytes uint64 `json:",omitempty"` +} + +// NetworkStats holds the network statistics for a container +type NetworkStats struct { + BytesReceived uint64 `json:",omitempty"` + BytesSent uint64 `json:",omitempty"` + PacketsReceived uint64 `json:",omitempty"` + PacketsSent uint64 `json:",omitempty"` + DroppedPacketsIncoming uint64 `json:",omitempty"` + DroppedPacketsOutgoing uint64 `json:",omitempty"` + EndpointId string `json:",omitempty"` + InstanceId string `json:",omitempty"` +} + +// Statistics is the structure returned by a statistics call on a container +type Statistics struct { + Timestamp time.Time `json:",omitempty"` + ContainerStartTime time.Time `json:",omitempty"` + Uptime100ns uint64 `json:",omitempty"` + Memory MemoryStats `json:",omitempty"` + Processor ProcessorStats `json:",omitempty"` + Storage StorageStats `json:",omitempty"` + Network []NetworkStats `json:",omitempty"` +} + +// ProcessList is the structure of an item returned by a ProcessList call on a container +type ProcessListItem struct { + CreateTimestamp time.Time `json:",omitempty"` + ImageName string `json:",omitempty"` + KernelTime100ns uint64 `json:",omitempty"` + MemoryCommitBytes uint64 `json:",omitempty"` + MemoryWorkingSetPrivateBytes uint64 `json:",omitempty"` + MemoryWorkingSetSharedBytes uint64 `json:",omitempty"` + ProcessId uint32 `json:",omitempty"` + UserTime100ns uint64 `json:",omitempty"` +} + +// MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container +type MappedVirtualDiskController struct { + MappedVirtualDisks map[int]MappedVirtualDisk `json:",omitempty"` +} + +// Type of Request Support in ModifySystem +type RequestType string + +// Type of Resource Support in ModifySystem +type ResourceType string + +// RequestType const +const ( + Add RequestType = "Add" + Remove RequestType = "Remove" + Network ResourceType = "Network" +) + +// ResourceModificationRequestResponse is the structure used to send request to the container to modify the system +// Supported resource types are Network and Request Types are Add/Remove +type ResourceModificationRequestResponse struct { + Resource ResourceType `json:"ResourceType"` + Data interface{} `json:"Settings"` + Request RequestType `json:"RequestType,omitempty"` +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go b/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go new file mode 100644 index 000000000..e4253f400 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/timeout/timeout.go @@ -0,0 +1,26 @@ +package timeout + +import ( + "os" + "strconv" + "time" +) + +// Duration is the default time to wait for various operations. +// - Waiting for async notifications from HCS +// - Waiting for processes to launch through +// - Waiting to copy data to/from a launched processes stdio pipes. +// +// This can be overridden through environment variable `HCS_TIMEOUT_SECONDS` + +var Duration = 4 * time.Minute + +func init() { + envTimeout := os.Getenv("HCSSHIM_TIMEOUT_SECONDS") + if len(envTimeout) > 0 { + e, err := strconv.Atoi(envTimeout) + if err == nil && e > 0 { + Duration = time.Second * time.Duration(e) + } + } +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go new file mode 100644 index 000000000..3a0d4bc58 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/activatelayer.go @@ -0,0 +1,25 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// ActivateLayer will find the layer with the given id and mount it's filesystem. +// 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) + + err := activateLayer(&stdDriverInfo, path) + if err != nil { + err = hcserror.Errorf(err, title, "path=%s", path) + logrus.Error(err) + return err + } + + logrus.Debugf(title+" - succeeded path=%s", path) + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/baselayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go similarity index 81% rename from vendor/github.com/Microsoft/hcsshim/baselayer.go rename to vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go index 860185c35..5784241df 100644 --- a/vendor/github.com/Microsoft/hcsshim/baselayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/baselayer.go @@ -1,4 +1,4 @@ -package hcsshim +package wclayer import ( "errors" @@ -7,6 +7,8 @@ import ( "syscall" "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/safefile" ) type baseLayerWriter struct { @@ -29,7 +31,7 @@ type dirInfo struct { func reapplyDirectoryTimes(root *os.File, dis []dirInfo) error { for i := range dis { di := &dis[len(dis)-i-1] // reverse order: process child directories first - f, err := openRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_OPEN, _FILE_DIRECTORY_FILE) + f, err := safefile.OpenRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, safefile.FILE_OPEN, safefile.FILE_DIRECTORY_FILE) if err != nil { return err } @@ -84,21 +86,21 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e extraFlags := uint32(0) if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 { - extraFlags |= _FILE_DIRECTORY_FILE + extraFlags |= safefile.FILE_DIRECTORY_FILE if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 { w.dirInfo = append(w.dirInfo, dirInfo{name, *fileInfo}) } } mode := uint32(syscall.GENERIC_READ | syscall.GENERIC_WRITE | winio.WRITE_DAC | winio.WRITE_OWNER | winio.ACCESS_SYSTEM_SECURITY) - f, err = openRelative(name, w.root, mode, syscall.FILE_SHARE_READ, _FILE_CREATE, extraFlags) + f, err = safefile.OpenRelative(name, w.root, mode, syscall.FILE_SHARE_READ, safefile.FILE_CREATE, extraFlags) if err != nil { - return makeError(err, "Failed to openRelative", name) + return hcserror.New(err, "Failed to safefile.OpenRelative", name) } err = winio.SetFileBasicInfo(f, fileInfo) if err != nil { - return makeError(err, "Failed to SetFileBasicInfo", name) + return hcserror.New(err, "Failed to SetFileBasicInfo", name) } w.f = f @@ -119,7 +121,7 @@ func (w *baseLayerWriter) AddLink(name string, target string) (err error) { return err } - return linkRelative(target, w.root, name, w.root) + return safefile.LinkRelative(target, w.root, name, w.root) } func (w *baseLayerWriter) Remove(name string) error { @@ -157,7 +159,7 @@ func (w *baseLayerWriter) Close() error { } if w.hasUtilityVM { - err := ensureNotReparsePointRelative("UtilityVM", w.root) + err := safefile.EnsureNotReparsePointRelative("UtilityVM", w.root) if err != nil { return err } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go new file mode 100644 index 000000000..a3817843a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createlayer.go @@ -0,0 +1,23 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// 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+"Flavour %d ID %s parent %s", path, parent) + + err := createLayer(&stdDriverInfo, path, parent) + if err != nil { + err = hcserror.Errorf(err, title, "path=%s parent=%s flavour=%d", path, parent) + logrus.Error(err) + return err + } + + logrus.Debugf(title+" - succeeded path=%s parent=%s flavour=%d", 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 new file mode 100644 index 000000000..bf2fece19 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/createscratchlayer.go @@ -0,0 +1,31 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// CreateScratchLayer creates and populates new read-write layer for use by a container. +// 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) + + // Generate layer descriptors + layers, err := layerPathsToDescriptors(parentLayerPaths) + if err != nil { + return err + } + + err = createSandboxLayer(&stdDriverInfo, path, 0, layers) + if err != nil { + err = hcserror.Errorf(err, title, "path=%s", path) + logrus.Error(err) + return err + } + + 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 new file mode 100644 index 000000000..b998f8a19 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/deactivatelayer.go @@ -0,0 +1,22 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// DeactivateLayer will dismount a layer that was mounted via ActivateLayer. +func DeactivateLayer(path string) error { + title := "hcsshim::DeactivateLayer " + logrus.Debugf(title+"path %s", path) + + err := deactivateLayer(&stdDriverInfo, path) + if err != nil { + err = hcserror.Errorf(err, title, "path=%s", path) + logrus.Error(err) + return err + } + + 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 new file mode 100644 index 000000000..dc14cecc4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/destroylayer.go @@ -0,0 +1,23 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// 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) + + err := destroyLayer(&stdDriverInfo, path) + if err != nil { + err = hcserror.Errorf(err, title, "path=%s", path) + logrus.Error(err) + return err + } + + 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 new file mode 100644 index 000000000..7832bb452 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/expandscratchsize.go @@ -0,0 +1,22 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// 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) + + err := expandSandboxSize(&stdDriverInfo, path, size) + if err != nil { + err = hcserror.Errorf(err, title, "path=%s size=%d", path, size) + logrus.Error(err) + return err + } + + logrus.Debugf(title+"- succeeded path=%s size=%d", path, size) + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/exportlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go similarity index 70% rename from vendor/github.com/Microsoft/hcsshim/exportlayer.go rename to vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go index d7025f20b..c6b3480ce 100644 --- a/vendor/github.com/Microsoft/hcsshim/exportlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/exportlayer.go @@ -1,4 +1,4 @@ -package hcsshim +package wclayer import ( "io" @@ -7,6 +7,8 @@ import ( "syscall" "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/interop" "github.com/sirupsen/logrus" ) @@ -15,9 +17,9 @@ 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(info DriverInfo, layerId string, exportFolderPath string, parentLayerPaths []string) error { +func ExportLayer(path string, exportFolderPath string, parentLayerPaths []string) error { title := "hcsshim::ExportLayer " - logrus.Debugf(title+"flavour %d layerId %s folder %s", info.Flavour, layerId, exportFolderPath) + logrus.Debugf(title+"path %s folder %s", path, exportFolderPath) // Generate layer descriptors layers, err := layerPathsToDescriptors(parentLayerPaths) @@ -25,21 +27,14 @@ func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, paren return err } - // Convert info to API calling convention - infop, err := convertDriverInfo(info) + 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 } - err = exportLayer(&infop, layerId, exportFolderPath, layers) - if err != nil { - err = makeErrorf(err, title, "layerId=%s flavour=%d folder=%s", layerId, info.Flavour, exportFolderPath) - logrus.Error(err) - return err - } - - logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerId, exportFolderPath) + logrus.Debugf(title+"succeeded path=%s folder=%s", path, exportFolderPath) return nil } @@ -69,11 +64,11 @@ func (r *FilterLayerReader) Next() (string, int64, *winio.FileBasicInfo, error) if err == syscall.ERROR_NO_MORE_FILES { err = io.EOF } else { - err = makeError(err, "ExportLayerNext", "") + err = hcserror.New(err, "ExportLayerNext", "") } return "", 0, nil, err } - fileName := convertAndFreeCoTaskMemString(fileNamep) + fileName := interop.ConvertAndFreeCoTaskMemString(fileNamep) if deleted != 0 { fileInfo = nil } @@ -88,7 +83,7 @@ func (r *FilterLayerReader) Read(b []byte) (int, error) { var bytesRead uint32 err := exportLayerRead(r.context, b, &bytesRead) if err != nil { - return 0, makeError(err, "ExportLayerRead", "") + return 0, hcserror.New(err, "ExportLayerRead", "") } if bytesRead == 0 { return 0, io.EOF @@ -103,7 +98,7 @@ func (r *FilterLayerReader) Close() (err error) { if r.context != 0 { err = exportLayerEnd(r.context) if err != nil { - err = makeError(err, "ExportLayerEnd", "") + err = hcserror.New(err, "ExportLayerEnd", "") } r.context = 0 } @@ -113,34 +108,30 @@ func (r *FilterLayerReader) Close() (err error) { // 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(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error) { +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. - path, err := ioutil.TempDir("", "hcs") + exportPath, err := ioutil.TempDir("", "hcs") if err != nil { return nil, err } - err = ExportLayer(info, layerID, path, parentLayerPaths) + err = ExportLayer(path, exportPath, parentLayerPaths) if err != nil { - os.RemoveAll(path) + os.RemoveAll(exportPath) return nil, err } - return &legacyLayerReaderWrapper{newLegacyLayerReader(path)}, nil + return &legacyLayerReaderWrapper{newLegacyLayerReader(exportPath)}, nil } layers, err := layerPathsToDescriptors(parentLayerPaths) if err != nil { return nil, err } - infop, err := convertDriverInfo(info) - if err != nil { - return nil, err - } r := &FilterLayerReader{} - err = exportLayerBegin(&infop, layerID, layers, &r.context) + err = exportLayerBegin(&stdDriverInfo, path, layers, &r.context) if err != nil { - return nil, makeError(err, "ExportLayerBegin", "") + return nil, hcserror.New(err, "ExportLayerBegin", "") } return r, err } diff --git a/vendor/github.com/Microsoft/hcsshim/getlayermountpath.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go similarity index 52% rename from vendor/github.com/Microsoft/hcsshim/getlayermountpath.go rename to vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go index 89f8079d0..8c37549a0 100644 --- a/vendor/github.com/Microsoft/hcsshim/getlayermountpath.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getlayermountpath.go @@ -1,34 +1,28 @@ -package hcsshim +package wclayer import ( "syscall" + "github.com/Microsoft/hcsshim/internal/hcserror" "github.com/sirupsen/logrus" ) -// GetLayerMountPath will look for a mounted layer with the given id and return +// GetLayerMountPath will look for a mounted layer with the given path and return // 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(info DriverInfo, id string) (string, error) { +func GetLayerMountPath(path string) (string, error) { title := "hcsshim::GetLayerMountPath " - logrus.Debugf(title+"Flavour %d ID %s", info.Flavour, id) - - // Convert info to API calling convention - infop, err := convertDriverInfo(info) - if err != nil { - logrus.Error(err) - return "", err - } + logrus.Debugf(title+"path %s", path) var mountPathLength uintptr mountPathLength = 0 // Call the procedure itself. logrus.Debugf("Calling proc (1)") - err = getLayerMountPath(&infop, id, &mountPathLength, nil) + err := getLayerMountPath(&stdDriverInfo, path, &mountPathLength, nil) if err != nil { - err = makeErrorf(err, title, "(first call) id=%s flavour=%d", id, info.Flavour) + err = hcserror.Errorf(err, title, "(first call) path=%s", path) logrus.Error(err) return "", err } @@ -42,14 +36,14 @@ func GetLayerMountPath(info DriverInfo, id string) (string, error) { // Call the procedure again logrus.Debugf("Calling proc (2)") - err = getLayerMountPath(&infop, id, &mountPathLength, &mountPathp[0]) + err = getLayerMountPath(&stdDriverInfo, path, &mountPathLength, &mountPathp[0]) if err != nil { - err = makeErrorf(err, title, "(second call) id=%s flavour=%d", id, info.Flavour) + err = hcserror.Errorf(err, title, "(second call) path=%s", path) logrus.Error(err) return "", err } - path := syscall.UTF16ToString(mountPathp[0:]) - logrus.Debugf(title+"succeeded flavour=%d id=%s path=%s", info.Flavour, id, path) - return path, nil + mountPath := syscall.UTF16ToString(mountPathp[0:]) + logrus.Debugf(title+"succeeded path=%s mountPath=%s", path, mountPath) + return mountPath, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/getsharedbaseimages.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go similarity index 67% rename from vendor/github.com/Microsoft/hcsshim/getsharedbaseimages.go rename to vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go index 05d3d9532..10899c68a 100644 --- a/vendor/github.com/Microsoft/hcsshim/getsharedbaseimages.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/getsharedbaseimages.go @@ -1,6 +1,10 @@ -package hcsshim +package wclayer -import "github.com/sirupsen/logrus" +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/interop" + "github.com/sirupsen/logrus" +) // GetSharedBaseImages will enumerate the images stored in the common central // image store and return descriptive info about those images for the purpose @@ -12,11 +16,11 @@ func GetSharedBaseImages() (imageData string, err error) { var buffer *uint16 err = getBaseImages(&buffer) if err != nil { - err = makeError(err, title, "") + err = hcserror.New(err, title, "") logrus.Error(err) return } - imageData = convertAndFreeCoTaskMemString(buffer) + imageData = interop.ConvertAndFreeCoTaskMemString(buffer) logrus.Debugf(title+" - succeeded output=%s", imageData) return } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go new file mode 100644 index 000000000..d86e67827 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/grantvmaccess.go @@ -0,0 +1,24 @@ +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) + + err := grantVmAccess(vmid, filepath) + if err != nil { + err = hcserror.Errorf(err, title, "path=%s", filepath) + logrus.Error(err) + return err + } + + logrus.Debugf(title + " - succeeded") + return nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/importlayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go similarity index 71% rename from vendor/github.com/Microsoft/hcsshim/importlayer.go rename to vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go index 2742b9f75..c978450f8 100644 --- a/vendor/github.com/Microsoft/hcsshim/importlayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/importlayer.go @@ -1,4 +1,4 @@ -package hcsshim +package wclayer import ( "errors" @@ -7,6 +7,8 @@ import ( "path/filepath" "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/Microsoft/hcsshim/internal/safefile" "github.com/sirupsen/logrus" ) @@ -14,9 +16,9 @@ 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(info DriverInfo, layerID string, importFolderPath string, parentLayerPaths []string) error { +func ImportLayer(path string, importFolderPath string, parentLayerPaths []string) error { title := "hcsshim::ImportLayer " - logrus.Debugf(title+"flavour %d layerId %s folder %s", info.Flavour, layerID, importFolderPath) + logrus.Debugf(title+"path %s folder %s", path, importFolderPath) // Generate layer descriptors layers, err := layerPathsToDescriptors(parentLayerPaths) @@ -24,21 +26,14 @@ func ImportLayer(info DriverInfo, layerID string, importFolderPath string, paren return err } - // Convert info to API calling convention - infop, err := convertDriverInfo(info) + 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 } - err = importLayer(&infop, layerID, importFolderPath, layers) - if err != nil { - err = makeErrorf(err, title, "layerId=%s flavour=%d folder=%s", layerID, info.Flavour, importFolderPath) - logrus.Error(err) - return err - } - - logrus.Debugf(title+"succeeded flavour=%d layerId=%s folder=%s", info.Flavour, layerID, importFolderPath) + logrus.Debugf(title+"succeeded path=%s folder=%s", path, importFolderPath) return nil } @@ -73,7 +68,7 @@ func (w *FilterLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro } err := importLayerNext(w.context, name, fileInfo) if err != nil { - return makeError(err, "ImportLayerNext", "") + return hcserror.New(err, "ImportLayerNext", "") } return nil } @@ -92,7 +87,7 @@ func (w *FilterLayerWriter) Remove(name string) error { } err := importLayerNext(w.context, name, nil) if err != nil { - return makeError(err, "ImportLayerNext", "") + return hcserror.New(err, "ImportLayerNext", "") } return nil } @@ -101,7 +96,7 @@ func (w *FilterLayerWriter) Remove(name string) error { func (w *FilterLayerWriter) Write(b []byte) (int, error) { err := importLayerWrite(w.context, b) if err != nil { - err = makeError(err, "ImportLayerWrite", "") + err = hcserror.New(err, "ImportLayerWrite", "") return 0, err } return len(b), err @@ -113,7 +108,7 @@ func (w *FilterLayerWriter) Close() (err error) { if w.context != 0 { err = importLayerEnd(w.context) if err != nil { - err = makeError(err, "ImportLayerEnd", "") + err = hcserror.New(err, "ImportLayerEnd", "") } w.context = 0 } @@ -122,8 +117,6 @@ func (w *FilterLayerWriter) Close() (err error) { type legacyLayerWriterWrapper struct { *legacyLayerWriter - info DriverInfo - layerID string path string parentLayerPaths []string } @@ -136,28 +129,26 @@ func (r *legacyLayerWriterWrapper) Close() error { return err } - info := r.info - info.HomeDir = "" - if err = ImportLayer(info, r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil { + if err = ImportLayer(r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil { return err } for _, name := range r.Tombstones { - if err = removeRelative(name, r.destRoot); err != nil && !os.IsNotExist(err) { + if err = safefile.RemoveRelative(name, r.destRoot); err != nil && !os.IsNotExist(err) { return err } } // Add any hard links that were collected. for _, lnk := range r.PendingLinks { - if err = removeRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) { + if err = safefile.RemoveRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) { return err } - if err = linkRelative(lnk.Target, lnk.TargetRoot, lnk.Path, r.destRoot); err != nil { + if err = safefile.LinkRelative(lnk.Target, lnk.TargetRoot, lnk.Path, r.destRoot); err != nil { return err } } // Prepare the utility VM for use if one is present in the layer. if r.HasUtilityVM { - err := ensureNotReparsePointRelative("UtilityVM", r.destRoot) + err := safefile.EnsureNotReparsePointRelative("UtilityVM", r.destRoot) if err != nil { return err } @@ -172,10 +163,10 @@ func (r *legacyLayerWriterWrapper) Close() error { // NewLayerWriter returns a new layer writer for creating a layer on disk. // The caller must have taken the SeBackupPrivilege and SeRestorePrivilege privileges // to call this and any methods on the resulting LayerWriter. -func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) { +func NewLayerWriter(path string, parentLayerPaths []string) (LayerWriter, error) { if len(parentLayerPaths) == 0 { // This is a base layer. It gets imported differently. - f, err := openRoot(filepath.Join(info.HomeDir, layerID)) + f, err := safefile.OpenRoot(path) if err != nil { return nil, err } @@ -187,19 +178,17 @@ func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) if procImportLayerBegin.Find() != nil { // The new layer reader is not available on this Windows build. Fall back to the // legacy export code path. - path, err := ioutil.TempDir("", "hcs") + importPath, err := ioutil.TempDir("", "hcs") if err != nil { return nil, err } - w, err := newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID)) + w, err := newLegacyLayerWriter(importPath, parentLayerPaths, path) if err != nil { return nil, err } return &legacyLayerWriterWrapper{ legacyLayerWriter: w, - info: info, - layerID: layerID, - path: path, + path: importPath, parentLayerPaths: parentLayerPaths, }, nil } @@ -208,15 +197,10 @@ func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) return nil, err } - infop, err := convertDriverInfo(info) - if err != nil { - return nil, err - } - w := &FilterLayerWriter{} - err = importLayerBegin(&infop, layerID, layers, &w.context) + err = importLayerBegin(&stdDriverInfo, path, layers, &w.context) if err != nil { - return nil, makeError(err, "ImportLayerStart", "") + return nil, hcserror.New(err, "ImportLayerStart", "") } return w, nil } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go new file mode 100644 index 000000000..71287ff8a --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerexists.go @@ -0,0 +1,25 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// 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) + + // Call the procedure itself. + var exists uint32 + err := layerExists(&stdDriverInfo, path, &exists) + if err != nil { + err = hcserror.Errorf(err, title, "path=%s", path) + logrus.Error(err) + return false, err + } + + logrus.Debugf(title+"succeeded path=%s exists=%d", path, exists) + return exists != 0, nil +} diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go new file mode 100644 index 000000000..90df3bedc --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerid.go @@ -0,0 +1,13 @@ +package wclayer + +import ( + "path/filepath" + + "github.com/Microsoft/hcsshim/internal/guid" +) + +// LayerID returns the layer ID of a layer on disk. +func LayerID(path string) (guid.GUID, error) { + _, file := filepath.Split(path) + return NameToGuid(file) +} diff --git a/vendor/github.com/Microsoft/hcsshim/layerutils.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go similarity index 72% rename from vendor/github.com/Microsoft/hcsshim/layerutils.go rename to vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go index c0e550377..a1b8b9882 100644 --- a/vendor/github.com/Microsoft/hcsshim/layerutils.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/layerutils.go @@ -1,12 +1,12 @@ -package hcsshim +package wclayer // This file contains utility functions to support storage (graph) related // functionality. import ( - "path/filepath" "syscall" + "github.com/Microsoft/hcsshim/internal/guid" "github.com/sirupsen/logrus" ) @@ -22,28 +22,16 @@ struct DriverInfo { LPCWSTR HomeDir; }; */ -type DriverInfo struct { - Flavour int - HomeDir string -} type driverInfo struct { Flavour int HomeDirp *uint16 } -func convertDriverInfo(info DriverInfo) (driverInfo, error) { - homedirp, err := syscall.UTF16PtrFromString(info.HomeDir) - if err != nil { - logrus.Debugf("Failed conversion of home to pointer for driver info: %s", err.Error()) - return driverInfo{}, err - } - - return driverInfo{ - Flavour: info.Flavour, - HomeDirp: homedirp, - }, nil -} +var ( + utf16EmptyString uint16 + stdDriverInfo = driverInfo{1, &utf16EmptyString} +) /* To pass into syscall, we need a struct matching the following: typedef struct _WC_LAYER_DESCRIPTOR { @@ -75,7 +63,7 @@ typedef struct _WC_LAYER_DESCRIPTOR { } WC_LAYER_DESCRIPTOR, *PWC_LAYER_DESCRIPTOR; */ type WC_LAYER_DESCRIPTOR struct { - LayerId GUID + LayerId guid.GUID Flags uint32 Pathp *uint16 } @@ -85,10 +73,7 @@ func layerPathsToDescriptors(parentLayerPaths []string) ([]WC_LAYER_DESCRIPTOR, var layers []WC_LAYER_DESCRIPTOR for i := 0; i < len(parentLayerPaths); i++ { - // Create a layer descriptor, using the folder name - // as the source for a GUID LayerId - _, folderName := filepath.Split(parentLayerPaths[i]) - g, err := NameToGuid(folderName) + g, err := LayerID(parentLayerPaths[i]) if err != nil { logrus.Debugf("Failed to convert name to guid %s", err) return nil, err diff --git a/vendor/github.com/Microsoft/hcsshim/legacy.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go similarity index 88% rename from vendor/github.com/Microsoft/hcsshim/legacy.go rename to vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go index 0b23b6c4d..b8ea5d263 100644 --- a/vendor/github.com/Microsoft/hcsshim/legacy.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/legacy.go @@ -1,4 +1,4 @@ -package hcsshim +package wclayer import ( "bufio" @@ -6,12 +6,15 @@ import ( "errors" "fmt" "io" + "io/ioutil" "os" "path/filepath" "strings" "syscall" "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/longpath" + "github.com/Microsoft/hcsshim/internal/safefile" ) var errorIterationCanceled = errors.New("") @@ -34,23 +37,6 @@ func openFileOrDir(path string, mode uint32, createDisposition uint32) (file *os return winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createDisposition) } -func makeLongAbsPath(path string) (string, error) { - if strings.HasPrefix(path, `\\?\`) || strings.HasPrefix(path, `\\.\`) { - return path, nil - } - if !filepath.IsAbs(path) { - absPath, err := filepath.Abs(path) - if err != nil { - return "", err - } - path = absPath - } - if strings.HasPrefix(path, `\\`) { - return `\\?\UNC\` + path[2:], nil - } - return `\\?\` + path, nil -} - func hasPathPrefix(p, prefix string) bool { return strings.HasPrefix(p, prefix) && len(p) > len(prefix) && p[len(prefix)] == '\\' } @@ -106,7 +92,7 @@ func readTombstones(path string) (map[string]([]string), error) { } func (r *legacyLayerReader) walkUntilCancelled() error { - root, err := makeLongAbsPath(r.root) + root, err := longpath.LongAbs(r.root) if err != nil { return err } @@ -283,7 +269,7 @@ func (r *legacyLayerReader) Next() (path string, size int64, fileInfo *winio.Fil if err != nil { return } - fileInfo.FileAttributes = uintptr(attr) + fileInfo.FileAttributes = attr beginning := int64(4) // Find the accurate file size. @@ -349,6 +335,7 @@ type legacyLayerWriter struct { destRoot *os.File parentRoots []*os.File currentFile *os.File + bufWriter *bufio.Writer currentFileName string currentFileRoot *os.File backupWriter *winio.BackupFileWriter @@ -373,21 +360,22 @@ func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) (w w = nil } }() - w.root, err = openRoot(root) + w.root, err = safefile.OpenRoot(root) if err != nil { return } - w.destRoot, err = openRoot(destRoot) + w.destRoot, err = safefile.OpenRoot(destRoot) if err != nil { return } for _, r := range parentRoots { - f, err := openRoot(r) + f, err := safefile.OpenRoot(r) if err != nil { return w, err } w.parentRoots = append(w.parentRoots, f) } + w.bufWriter = bufio.NewWriterSize(ioutil.Discard, 65536) return } @@ -408,7 +396,7 @@ func (w *legacyLayerWriter) CloseRoots() { func (w *legacyLayerWriter) initUtilityVM() error { if !w.HasUtilityVM { - err := mkdirRelative(utilityVMPath, w.destRoot) + err := safefile.MkdirRelative(utilityVMPath, w.destRoot) if err != nil { return err } @@ -426,6 +414,11 @@ func (w *legacyLayerWriter) initUtilityVM() error { } func (w *legacyLayerWriter) reset() error { + err := w.bufWriter.Flush() + if err != nil { + return err + } + w.bufWriter.Reset(ioutil.Discard) if w.currentIsDir { r := w.currentFile br := winio.NewBackupStreamReader(r) @@ -449,7 +442,7 @@ func (w *legacyLayerWriter) reset() error { // describes a directory reparse point. Delete the placeholder // directory to prevent future files being added into the // destination of the reparse point during the ImportLayer call - if err := removeRelative(w.currentFileName, w.currentFileRoot); err != nil { + if err := safefile.RemoveRelative(w.currentFileName, w.currentFileRoot); err != nil { return err } w.pendingDirs = append(w.pendingDirs, pendingDir{Path: w.currentFileName, Root: w.currentFileRoot}) @@ -474,13 +467,13 @@ func (w *legacyLayerWriter) reset() error { // copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) { - src, err := openRelative( + src, err := safefile.OpenRelative( subPath, srcRoot, syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY, syscall.FILE_SHARE_READ, - _FILE_OPEN, - _FILE_OPEN_REPARSE_POINT) + safefile.FILE_OPEN, + safefile.FILE_OPEN_REPARSE_POINT) if err != nil { return nil, err } @@ -495,14 +488,14 @@ func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool extraFlags := uint32(0) if isDir { - extraFlags |= _FILE_DIRECTORY_FILE + extraFlags |= safefile.FILE_DIRECTORY_FILE } - dest, err := openRelative( + dest, err := safefile.OpenRelative( subPath, destRoot, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, syscall.FILE_SHARE_READ, - _FILE_CREATE, + safefile.FILE_CREATE, extraFlags) if err != nil { return nil, err @@ -534,7 +527,7 @@ func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool // the file names in the provided map and just copies those files. func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles map[string]bool) error { var di []dirInfo - err := ensureNotReparsePointRelative(subPath, srcRoot) + err := safefile.EnsureNotReparsePointRelative(subPath, srcRoot) if err != nil { return err } @@ -566,18 +559,12 @@ func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles di = append(di, dirInfo{path: relPath, fileInfo: *fi}) } } else { - err = linkRelative(relPath, srcRoot, relPath, destRoot) + err = safefile.LinkRelative(relPath, srcRoot, relPath, destRoot) if err != nil { return err } } - // Don't recurse on reparse points in go1.8 and older. Filepath.Walk - // handles this in go1.9 and newer. - if isDir && isReparsePoint && shouldSkipDirectoryReparse { - return filepath.SkipDir - } - return nil }) if err != nil { @@ -604,9 +591,9 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath { return errors.New("invalid UtilityVM layer") } - createDisposition := uint32(_FILE_OPEN) + createDisposition := uint32(safefile.FILE_OPEN) if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { - st, err := lstatRelative(name, w.destRoot) + st, err := safefile.LstatRelative(name, w.destRoot) if err != nil && !os.IsNotExist(err) { return err } @@ -614,14 +601,14 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro // Delete the existing file/directory if it is not the same type as this directory. existingAttr := st.Sys().(*syscall.Win32FileAttributeData).FileAttributes if (uint32(fileInfo.FileAttributes)^existingAttr)&(syscall.FILE_ATTRIBUTE_DIRECTORY|syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 { - if err = removeAllRelative(name, w.destRoot); err != nil { + if err = safefile.RemoveAllRelative(name, w.destRoot); err != nil { return err } st = nil } } if st == nil { - if err = mkdirRelative(name, w.destRoot); err != nil { + if err = safefile.MkdirRelative(name, w.destRoot); err != nil { return err } } @@ -630,20 +617,20 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro } } else { // Overwrite any existing hard link. - err := removeRelative(name, w.destRoot) + err := safefile.RemoveRelative(name, w.destRoot) if err != nil && !os.IsNotExist(err) { return err } - createDisposition = _FILE_CREATE + createDisposition = safefile.FILE_CREATE } - f, err := openRelative( + f, err := safefile.OpenRelative( name, w.destRoot, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, syscall.FILE_SHARE_READ, createDisposition, - _FILE_OPEN_REPARSE_POINT, + safefile.FILE_OPEN_REPARSE_POINT, ) if err != nil { return err @@ -651,7 +638,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro defer func() { if f != nil { f.Close() - removeRelative(name, w.destRoot) + safefile.RemoveRelative(name, w.destRoot) } }() @@ -661,6 +648,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro } w.backupWriter = winio.NewBackupFileWriter(f, true) + w.bufWriter.Reset(w.backupWriter) w.currentFile = f w.currentFileName = name w.currentFileRoot = w.destRoot @@ -671,7 +659,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro fname := name if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 { - err := mkdirRelative(name, w.root) + err := safefile.MkdirRelative(name, w.root) if err != nil { return err } @@ -679,14 +667,14 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro w.currentIsDir = true } - f, err := openRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_CREATE, 0) + f, err := safefile.OpenRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, safefile.FILE_CREATE, 0) if err != nil { return err } defer func() { if f != nil { f.Close() - removeRelative(fname, w.root) + safefile.RemoveRelative(fname, w.root) } }() @@ -699,10 +687,13 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro if hasPathPrefix(name, hivesPath) { w.backupWriter = winio.NewBackupFileWriter(f, false) + w.bufWriter.Reset(w.backupWriter) } else { + w.bufWriter.Reset(f) // The file attributes are written before the stream. - err = binary.Write(f, binary.LittleEndian, uint32(fileInfo.FileAttributes)) + err = binary.Write(w.bufWriter, binary.LittleEndian, uint32(fileInfo.FileAttributes)) if err != nil { + w.bufWriter.Reset(ioutil.Discard) return err } } @@ -744,7 +735,7 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error { selectedRoot = w.destRoot } else { for _, r := range roots { - if _, err := lstatRelative(target, r); err != nil { + if _, err := safefile.LstatRelative(target, r); err != nil { if !os.IsNotExist(err) { return err } @@ -780,10 +771,10 @@ func (w *legacyLayerWriter) Remove(name string) error { // Make sure the path exists; os.RemoveAll will not fail if the file is // already gone, and this needs to be a fatal error for diagnostics // purposes. - if _, err := lstatRelative(name, w.destRoot); err != nil { + if _, err := safefile.LstatRelative(name, w.destRoot); err != nil { return err } - err = removeAllRelative(name, w.destRoot) + err = safefile.RemoveAllRelative(name, w.destRoot) if err != nil { return err } @@ -795,24 +786,21 @@ func (w *legacyLayerWriter) Remove(name string) error { } func (w *legacyLayerWriter) Write(b []byte) (int, error) { - if w.backupWriter == nil { - if w.currentFile == nil { - return 0, errors.New("closed") - } - return w.currentFile.Write(b) + if w.backupWriter == nil && w.currentFile == nil { + return 0, errors.New("closed") } - return w.backupWriter.Write(b) + return w.bufWriter.Write(b) } func (w *legacyLayerWriter) Close() error { if err := w.reset(); err != nil { return err } - if err := removeRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) { + if err := safefile.RemoveRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) { return err } for _, pd := range w.pendingDirs { - err := mkdirRelative(pd.Path, pd.Root) + err := safefile.MkdirRelative(pd.Path, pd.Root) if err != nil { return err } diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go new file mode 100644 index 000000000..741994ba4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/nametoguid.go @@ -0,0 +1,24 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/guid" + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// NameToGuid converts the given string into a GUID using the algorithm in the +// 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 " + + err = nameToGuid(name, &id) + if err != nil { + err = hcserror.Errorf(err, title, "name=%s", name) + logrus.Error(err) + return + } + + logrus.Debugf(title+"name:%s guid:%s", name, id.String()) + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/preparelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go similarity index 58% rename from vendor/github.com/Microsoft/hcsshim/preparelayer.go rename to vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go index 5c5b61841..bd4005dc4 100644 --- a/vendor/github.com/Microsoft/hcsshim/preparelayer.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/preparelayer.go @@ -1,21 +1,22 @@ -package hcsshim +package wclayer import ( "sync" + "github.com/Microsoft/hcsshim/internal/hcserror" "github.com/sirupsen/logrus" ) var prepareLayerLock sync.Mutex -// PrepareLayer finds a mounted read-write layer matching layerId and enables the +// PrepareLayer finds a mounted read-write layer matching path and enables the // the filesystem filter for use on that layer. This requires the paths to all // 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(info DriverInfo, layerId string, parentLayerPaths []string) error { +func PrepareLayer(path string, parentLayerPaths []string) error { title := "hcsshim::PrepareLayer " - logrus.Debugf(title+"flavour %d layerId %s", info.Flavour, layerId) + logrus.Debugf(title+"path %s", path) // Generate layer descriptors layers, err := layerPathsToDescriptors(parentLayerPaths) @@ -23,24 +24,17 @@ func PrepareLayer(info DriverInfo, layerId string, parentLayerPaths []string) er return err } - // Convert info to API calling convention - infop, err := convertDriverInfo(info) - if err != nil { - logrus.Error(err) - return err - } - // This lock is a temporary workaround for a Windows bug. Only allowing one // call to prepareLayer at a time vastly reduces the chance of a timeout. prepareLayerLock.Lock() defer prepareLayerLock.Unlock() - err = prepareLayer(&infop, layerId, layers) + err = prepareLayer(&stdDriverInfo, path, layers) if err != nil { - err = makeErrorf(err, title, "layerId=%s flavour=%d", layerId, info.Flavour) + err = hcserror.Errorf(err, title, "path=%s", path) logrus.Error(err) return err } - logrus.Debugf(title+"succeeded flavour=%d layerId=%s", info.Flavour, layerId) + logrus.Debugf(title+"succeeded path=%s", path) return nil } diff --git a/vendor/github.com/Microsoft/hcsshim/processimage.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go similarity index 97% rename from vendor/github.com/Microsoft/hcsshim/processimage.go rename to vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go index fadb1b92c..884207c3e 100644 --- a/vendor/github.com/Microsoft/hcsshim/processimage.go +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/processimage.go @@ -1,4 +1,4 @@ -package hcsshim +package wclayer import "os" diff --git a/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go new file mode 100644 index 000000000..5f1b4f4f4 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/unpreparelayer.go @@ -0,0 +1,23 @@ +package wclayer + +import ( + "github.com/Microsoft/hcsshim/internal/hcserror" + "github.com/sirupsen/logrus" +) + +// 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) + + err := unprepareLayer(&stdDriverInfo, path) + if err != nil { + err = hcserror.Errorf(err, title, "path=%s", path) + logrus.Error(err) + return err + } + + 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 new file mode 100644 index 000000000..768a6f2f1 --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/wclayer.go @@ -0,0 +1,37 @@ +package wclayer + +import "github.com/Microsoft/hcsshim/internal/guid" + +//go:generate go run ../../mksyscall_windows.go -output zsyscall_windows.go -winio 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? +//sys createLayer(info *driverInfo, id string, parent string) (hr error) = vmcompute.CreateLayer? +//sys createSandboxLayer(info *driverInfo, id string, parent uintptr, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.CreateSandboxLayer? +//sys expandSandboxSize(info *driverInfo, id string, size uint64) (hr error) = vmcompute.ExpandSandboxSize? +//sys deactivateLayer(info *driverInfo, id string) (hr error) = vmcompute.DeactivateLayer? +//sys destroyLayer(info *driverInfo, id string) (hr error) = vmcompute.DestroyLayer? +//sys exportLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.ExportLayer? +//sys getLayerMountPath(info *driverInfo, id string, length *uintptr, buffer *uint16) (hr error) = vmcompute.GetLayerMountPath? +//sys getBaseImages(buffer **uint16) (hr error) = vmcompute.GetBaseImages? +//sys importLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.ImportLayer? +//sys layerExists(info *driverInfo, id string, exists *uint32) (hr error) = vmcompute.LayerExists? +//sys nameToGuid(name string, guid *_guid) (hr error) = vmcompute.NameToGuid? +//sys prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) = vmcompute.PrepareLayer? +//sys unprepareLayer(info *driverInfo, id string) (hr error) = vmcompute.UnprepareLayer? +//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 new file mode 100644 index 000000000..cb813aa3d --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/internal/wclayer/zsyscall_windows.go @@ -0,0 +1,597 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package wclayer + +import ( + "syscall" + "unsafe" + + "github.com/Microsoft/go-winio" + "github.com/Microsoft/hcsshim/internal/interop" + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") + + procActivateLayer = modvmcompute.NewProc("ActivateLayer") + procCopyLayer = modvmcompute.NewProc("CopyLayer") + procCreateLayer = modvmcompute.NewProc("CreateLayer") + procCreateSandboxLayer = modvmcompute.NewProc("CreateSandboxLayer") + procExpandSandboxSize = modvmcompute.NewProc("ExpandSandboxSize") + procDeactivateLayer = modvmcompute.NewProc("DeactivateLayer") + procDestroyLayer = modvmcompute.NewProc("DestroyLayer") + procExportLayer = modvmcompute.NewProc("ExportLayer") + procGetLayerMountPath = modvmcompute.NewProc("GetLayerMountPath") + procGetBaseImages = modvmcompute.NewProc("GetBaseImages") + procImportLayer = modvmcompute.NewProc("ImportLayer") + procLayerExists = modvmcompute.NewProc("LayerExists") + procNameToGuid = modvmcompute.NewProc("NameToGuid") + procPrepareLayer = modvmcompute.NewProc("PrepareLayer") + 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") +) + +func activateLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _activateLayer(info, _p0) +} + +func _activateLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procActivateLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procActivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func copyLayer(info *driverInfo, srcId string, dstId string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(srcId) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(dstId) + if hr != nil { + return + } + return _copyLayer(info, _p0, _p1, descriptors) +} + +func _copyLayer(info *driverInfo, srcId *uint16, dstId *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p2 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p2 = &descriptors[0] + } + if hr = procCopyLayer.Find(); hr != nil { + return + } + 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) + } + return +} + +func createLayer(info *driverInfo, id string, parent string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(parent) + if hr != nil { + return + } + return _createLayer(info, _p0, _p1) +} + +func _createLayer(info *driverInfo, id *uint16, parent *uint16) (hr error) { + if hr = procCreateLayer.Find(); hr != nil { + return + } + 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) + } + return +} + +func createSandboxLayer(info *driverInfo, id string, parent uintptr, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _createSandboxLayer(info, _p0, parent, descriptors) +} + +func _createSandboxLayer(info *driverInfo, id *uint16, parent uintptr, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p1 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p1 = &descriptors[0] + } + if hr = procCreateSandboxLayer.Find(); hr != nil { + return + } + 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) + } + return +} + +func expandSandboxSize(info *driverInfo, id string, size uint64) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _expandSandboxSize(info, _p0, size) +} + +func _expandSandboxSize(info *driverInfo, id *uint16, size uint64) (hr error) { + if hr = procExpandSandboxSize.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procExpandSandboxSize.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(size)) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func deactivateLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _deactivateLayer(info, _p0) +} + +func _deactivateLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procDeactivateLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procDeactivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func destroyLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _destroyLayer(info, _p0) +} + +func _destroyLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procDestroyLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procDestroyLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func exportLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _exportLayer(info, _p0, _p1, descriptors) +} + +func _exportLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p2 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p2 = &descriptors[0] + } + if hr = procExportLayer.Find(); hr != nil { + return + } + 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) + } + return +} + +func getLayerMountPath(info *driverInfo, id string, length *uintptr, buffer *uint16) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _getLayerMountPath(info, _p0, length, buffer) +} + +func _getLayerMountPath(info *driverInfo, id *uint16, length *uintptr, buffer *uint16) (hr error) { + if hr = procGetLayerMountPath.Find(); hr != nil { + return + } + 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) + } + return +} + +func getBaseImages(buffer **uint16) (hr error) { + if hr = procGetBaseImages.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procGetBaseImages.Addr(), 1, uintptr(unsafe.Pointer(buffer)), 0, 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func importLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _importLayer(info, _p0, _p1, descriptors) +} + +func _importLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p2 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p2 = &descriptors[0] + } + if hr = procImportLayer.Find(); hr != nil { + return + } + 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) + } + return +} + +func layerExists(info *driverInfo, id string, exists *uint32) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _layerExists(info, _p0, exists) +} + +func _layerExists(info *driverInfo, id *uint16, exists *uint32) (hr error) { + if hr = procLayerExists.Find(); hr != nil { + return + } + 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) + } + return +} + +func nameToGuid(name string, guid *_guid) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(name) + if hr != nil { + return + } + return _nameToGuid(_p0, guid) +} + +func _nameToGuid(name *uint16, guid *_guid) (hr error) { + if hr = procNameToGuid.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procNameToGuid.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(guid)), 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _prepareLayer(info, _p0, descriptors) +} + +func _prepareLayer(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { + var _p1 *WC_LAYER_DESCRIPTOR + if len(descriptors) > 0 { + _p1 = &descriptors[0] + } + if hr = procPrepareLayer.Find(); hr != nil { + return + } + 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) + } + return +} + +func unprepareLayer(info *driverInfo, id string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(id) + if hr != nil { + return + } + return _unprepareLayer(info, _p0) +} + +func _unprepareLayer(info *driverInfo, id *uint16) (hr error) { + if hr = procUnprepareLayer.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procUnprepareLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func processBaseImage(path string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _processBaseImage(_p0) +} + +func _processBaseImage(path *uint16) (hr error) { + if hr = procProcessBaseImage.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procProcessBaseImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} + +func processUtilityImage(path string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(path) + if hr != nil { + return + } + return _processUtilityImage(_p0) +} + +func _processUtilityImage(path *uint16) (hr error) { + if hr = procProcessUtilityImage.Find(); hr != nil { + return + } + 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) + } + return +} + +func grantVmAccess(vmid string, filepath string) (hr error) { + var _p0 *uint16 + _p0, hr = syscall.UTF16PtrFromString(vmid) + if hr != nil { + return + } + var _p1 *uint16 + _p1, hr = syscall.UTF16PtrFromString(filepath) + if hr != nil { + return + } + return _grantVmAccess(_p0, _p1) +} + +func _grantVmAccess(vmid *uint16, filepath *uint16) (hr error) { + if hr = procGrantVmAccess.Find(); hr != nil { + return + } + r0, _, _ := syscall.Syscall(procGrantVmAccess.Addr(), 2, uintptr(unsafe.Pointer(vmid)), uintptr(unsafe.Pointer(filepath)), 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} diff --git a/vendor/github.com/Microsoft/hcsshim/layer.go b/vendor/github.com/Microsoft/hcsshim/layer.go new file mode 100644 index 000000000..8cdc247dc --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/layer.go @@ -0,0 +1,108 @@ +package hcsshim + +import ( + "crypto/sha1" + "path/filepath" + + "github.com/Microsoft/hcsshim/internal/guid" + + "github.com/Microsoft/hcsshim/internal/wclayer" +) + +func layerPath(info *DriverInfo, id string) string { + return filepath.Join(info.HomeDir, id) +} + +func ActivateLayer(info DriverInfo, id string) error { + return wclayer.ActivateLayer(layerPath(&info, id)) +} +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) +} +func CreateScratchLayer(info DriverInfo, layerId, parentId string, parentLayerPaths []string) error { + return wclayer.CreateScratchLayer(layerPath(&info, layerId), parentLayerPaths) +} +func DeactivateLayer(info DriverInfo, id string) error { + return wclayer.DeactivateLayer(layerPath(&info, id)) +} +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) +} +func ExpandScratchSize(info DriverInfo, layerId string, size uint64) error { + return wclayer.ExpandScratchSize(layerPath(&info, layerId), size) +} +func ExportLayer(info DriverInfo, layerId string, exportFolderPath string, parentLayerPaths []string) error { + return wclayer.ExportLayer(layerPath(&info, layerId), exportFolderPath, parentLayerPaths) +} +func GetLayerMountPath(info DriverInfo, id string) (string, error) { + return wclayer.GetLayerMountPath(layerPath(&info, id)) +} +func GetSharedBaseImages() (imageData string, err error) { + return wclayer.GetSharedBaseImages() +} +func ImportLayer(info DriverInfo, layerID string, importFolderPath string, parentLayerPaths []string) error { + return wclayer.ImportLayer(layerPath(&info, layerID), importFolderPath, parentLayerPaths) +} +func LayerExists(info DriverInfo, id string) (bool, error) { + return wclayer.LayerExists(layerPath(&info, id)) +} +func PrepareLayer(info DriverInfo, layerId string, parentLayerPaths []string) error { + return wclayer.PrepareLayer(layerPath(&info, layerId), parentLayerPaths) +} +func ProcessBaseLayer(path string) error { + return wclayer.ProcessBaseLayer(path) +} +func ProcessUtilityVMImage(path string) error { + return wclayer.ProcessUtilityVMImage(path) +} +func UnprepareLayer(info DriverInfo, layerId string) error { + return wclayer.UnprepareLayer(layerPath(&info, layerId)) +} + +type DriverInfo struct { + Flavour int + HomeDir string +} + +type FilterLayerReader = wclayer.FilterLayerReader +type FilterLayerWriter = wclayer.FilterLayerWriter + +type GUID [16]byte + +func NameToGuid(name string) (id GUID, err error) { + g, err := wclayer.NameToGuid(name) + return GUID(g), err +} + +func NewGUID(source string) *GUID { + h := sha1.Sum([]byte(source)) + var g GUID + copy(g[0:], h[0:16]) + return &g +} + +func (g *GUID) ToString() string { + return (guid.GUID)(*g).String() +} + +type LayerReader = wclayer.LayerReader + +func NewLayerReader(info DriverInfo, layerID string, parentLayerPaths []string) (LayerReader, error) { + return wclayer.NewLayerReader(layerPath(&info, layerID), parentLayerPaths) +} + +type LayerWriter = wclayer.LayerWriter + +func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string) (LayerWriter, error) { + return wclayer.NewLayerWriter(layerPath(&info, layerID), parentLayerPaths) +} + +type WC_LAYER_DESCRIPTOR = wclayer.WC_LAYER_DESCRIPTOR diff --git a/vendor/github.com/Microsoft/hcsshim/layerexists.go b/vendor/github.com/Microsoft/hcsshim/layerexists.go deleted file mode 100644 index fe46f404c..000000000 --- a/vendor/github.com/Microsoft/hcsshim/layerexists.go +++ /dev/null @@ -1,30 +0,0 @@ -package hcsshim - -import "github.com/sirupsen/logrus" - -// LayerExists will return true if a layer with the given id exists and is known -// to the system. -func LayerExists(info DriverInfo, id string) (bool, error) { - title := "hcsshim::LayerExists " - logrus.Debugf(title+"Flavour %d ID %s", info.Flavour, id) - - // Convert info to API calling convention - infop, err := convertDriverInfo(info) - if err != nil { - logrus.Error(err) - return false, err - } - - // Call the procedure itself. - var exists uint32 - - err = layerExists(&infop, id, &exists) - if err != nil { - err = makeErrorf(err, title, "id=%s flavour=%d", id, info.Flavour) - logrus.Error(err) - return false, err - } - - logrus.Debugf(title+"succeeded flavour=%d id=%s exists=%d", info.Flavour, id, exists) - return exists != 0, nil -} diff --git a/vendor/github.com/Microsoft/hcsshim/legacy18.go b/vendor/github.com/Microsoft/hcsshim/legacy18.go deleted file mode 100644 index 0f593e8ab..000000000 --- a/vendor/github.com/Microsoft/hcsshim/legacy18.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build !go1.9 - -package hcsshim - -// Due to a bug in go1.8 and before, directory reparse points need to be skipped -// during filepath.Walk. This is fixed in go1.9 -var shouldSkipDirectoryReparse = true diff --git a/vendor/github.com/Microsoft/hcsshim/legacy19.go b/vendor/github.com/Microsoft/hcsshim/legacy19.go deleted file mode 100644 index fb0b7644f..000000000 --- a/vendor/github.com/Microsoft/hcsshim/legacy19.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build go1.9 - -package hcsshim - -// Due to a bug in go1.8 and before, directory reparse points need to be skipped -// during filepath.Walk. This is fixed in go1.9 -var shouldSkipDirectoryReparse = false diff --git a/vendor/github.com/Microsoft/hcsshim/nametoguid.go b/vendor/github.com/Microsoft/hcsshim/nametoguid.go deleted file mode 100644 index b7c6d020c..000000000 --- a/vendor/github.com/Microsoft/hcsshim/nametoguid.go +++ /dev/null @@ -1,20 +0,0 @@ -package hcsshim - -import "github.com/sirupsen/logrus" - -// NameToGuid converts the given string into a GUID using the algorithm in the -// Host Compute Service, ensuring GUIDs generated with the same string are common -// across all clients. -func NameToGuid(name string) (id GUID, err error) { - title := "hcsshim::NameToGuid " - logrus.Debugf(title+"Name %s", name) - - err = nameToGuid(name, &id) - if err != nil { - err = makeErrorf(err, title, "name=%s", name) - logrus.Error(err) - return - } - - return -} diff --git a/vendor/github.com/Microsoft/hcsshim/process.go b/vendor/github.com/Microsoft/hcsshim/process.go index faee2cfee..ca8acbb7c 100644 --- a/vendor/github.com/Microsoft/hcsshim/process.go +++ b/vendor/github.com/Microsoft/hcsshim/process.go @@ -1,384 +1,72 @@ package hcsshim import ( - "encoding/json" "io" - "sync" - "syscall" "time" - "github.com/sirupsen/logrus" + "github.com/Microsoft/hcsshim/internal/hcs" ) // ContainerError is an error encountered in HCS type process struct { - handleLock sync.RWMutex - handle hcsProcess - processID int - container *container - cachedPipes *cachedPipes - callbackNumber uintptr + p *hcs.Process } -type cachedPipes struct { - stdIn syscall.Handle - stdOut syscall.Handle - stdErr syscall.Handle -} - -type processModifyRequest struct { - Operation string - ConsoleSize *consoleSize `json:",omitempty"` - CloseHandle *closeHandle `json:",omitempty"` -} - -type consoleSize struct { - Height uint16 - Width uint16 -} - -type closeHandle struct { - Handle string -} - -type processStatus struct { - ProcessID uint32 - Exited bool - ExitCode uint32 - LastWaitResult int32 -} - -const ( - stdIn string = "StdIn" - stdOut string = "StdOut" - stdErr string = "StdErr" -) - -const ( - modifyConsoleSize string = "ConsoleSize" - modifyCloseHandle string = "CloseHandle" -) - // Pid returns the process ID of the process within the container. func (process *process) Pid() int { - return process.processID + return process.p.Pid() } // Kill signals the process to terminate but does not wait for it to finish terminating. func (process *process) Kill() error { - process.handleLock.RLock() - defer process.handleLock.RUnlock() - operation := "Kill" - title := "HCSShim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) - - if process.handle == 0 { - return makeProcessError(process, operation, "", ErrAlreadyClosed) - } - - var resultp *uint16 - err := hcsTerminateProcess(process.handle, &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return makeProcessError(process, operation, "", err) - } - - logrus.Debugf(title+" succeeded processid=%d", process.processID) - return nil + return convertProcessError(process.p.Kill(), process) } // 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) - - err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, nil) - if err != nil { - return makeProcessError(process, operation, "", err) - } - - logrus.Debugf(title+" succeeded processid=%d", process.processID) - return nil + return convertProcessError(process.p.Wait(), process) } // 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) - - err := waitForNotification(process.callbackNumber, hcsNotificationProcessExited, &timeout) - if err != nil { - return makeProcessError(process, operation, "", err) - } - - logrus.Debugf(title+" succeeded processid=%d", process.processID) - return nil + return convertProcessError(process.p.WaitTimeout(timeout), process) } // ExitCode returns the exit code of the process. The process must have // already terminated. func (process *process) ExitCode() (int, error) { - process.handleLock.RLock() - defer process.handleLock.RUnlock() - operation := "ExitCode" - title := "HCSShim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) - - if process.handle == 0 { - return 0, makeProcessError(process, operation, "", ErrAlreadyClosed) - } - - properties, err := process.properties() + code, err := process.p.ExitCode() if err != nil { - return 0, makeProcessError(process, operation, "", err) + err = convertProcessError(err, process) } - - if properties.Exited == false { - return 0, makeProcessError(process, operation, "", ErrInvalidProcessState) - } - - if properties.LastWaitResult != 0 { - return 0, makeProcessError(process, operation, "", syscall.Errno(properties.LastWaitResult)) - } - - logrus.Debugf(title+" succeeded processid=%d exitCode=%d", process.processID, properties.ExitCode) - return int(properties.ExitCode), nil + return code, err } // ResizeConsole resizes the console of the process. func (process *process) ResizeConsole(width, height uint16) error { - process.handleLock.RLock() - defer process.handleLock.RUnlock() - operation := "ResizeConsole" - title := "HCSShim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) - - if process.handle == 0 { - return makeProcessError(process, operation, "", ErrAlreadyClosed) - } - - modifyRequest := processModifyRequest{ - Operation: modifyConsoleSize, - ConsoleSize: &consoleSize{ - Height: height, - Width: width, - }, - } - - modifyRequestb, err := json.Marshal(modifyRequest) - if err != nil { - return err - } - - modifyRequestStr := string(modifyRequestb) - - var resultp *uint16 - err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return makeProcessError(process, operation, "", err) - } - - logrus.Debugf(title+" succeeded processid=%d", process.processID) - return nil -} - -func (process *process) properties() (*processStatus, error) { - operation := "properties" - title := "HCSShim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) - - var ( - resultp *uint16 - propertiesp *uint16 - ) - err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return nil, err - } - - if propertiesp == nil { - return nil, ErrUnexpectedValue - } - propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp) - - properties := &processStatus{} - if err := json.Unmarshal(propertiesRaw, properties); err != nil { - return nil, err - } - - logrus.Debugf(title+" succeeded processid=%d, properties=%s", process.processID, propertiesRaw) - return properties, nil + return convertProcessError(process.p.ResizeConsole(width, height), process) } // 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) { - process.handleLock.RLock() - defer process.handleLock.RUnlock() - operation := "Stdio" - title := "HCSShim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) - - if process.handle == 0 { - return nil, nil, nil, makeProcessError(process, operation, "", ErrAlreadyClosed) - } - - var stdIn, stdOut, stdErr syscall.Handle - - if process.cachedPipes == nil { - var ( - processInfo hcsProcessInformation - resultp *uint16 - ) - err := hcsGetProcessInfo(process.handle, &processInfo, &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return nil, nil, nil, makeProcessError(process, operation, "", err) - } - - stdIn, stdOut, stdErr = processInfo.StdInput, processInfo.StdOutput, processInfo.StdError - } else { - // Use cached pipes - stdIn, stdOut, stdErr = process.cachedPipes.stdIn, process.cachedPipes.stdOut, process.cachedPipes.stdErr - - // Invalidate the cache - process.cachedPipes = nil - } - - pipes, err := makeOpenFiles([]syscall.Handle{stdIn, stdOut, stdErr}) + stdin, stdout, stderr, err := process.p.Stdio() if err != nil { - return nil, nil, nil, makeProcessError(process, operation, "", err) + err = convertProcessError(err, process) } - - logrus.Debugf(title+" succeeded processid=%d", process.processID) - return pipes[0], pipes[1], pipes[2], nil + return stdin, stdout, stderr, err } // 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 { - process.handleLock.RLock() - defer process.handleLock.RUnlock() - operation := "CloseStdin" - title := "HCSShim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) - - if process.handle == 0 { - return makeProcessError(process, operation, "", ErrAlreadyClosed) - } - - modifyRequest := processModifyRequest{ - Operation: modifyCloseHandle, - CloseHandle: &closeHandle{ - Handle: stdIn, - }, - } - - modifyRequestb, err := json.Marshal(modifyRequest) - if err != nil { - return err - } - - modifyRequestStr := string(modifyRequestb) - - var resultp *uint16 - err = hcsModifyProcess(process.handle, modifyRequestStr, &resultp) - err = processHcsResult(err, resultp) - if err != nil { - return makeProcessError(process, operation, "", err) - } - - logrus.Debugf(title+" succeeded processid=%d", process.processID) - return nil + return convertProcessError(process.p.CloseStdin(), process) } // Close cleans up any state associated with the process but does not kill // or wait on it. func (process *process) Close() error { - process.handleLock.Lock() - defer process.handleLock.Unlock() - operation := "Close" - title := "HCSShim::Process::" + operation - logrus.Debugf(title+" processid=%d", process.processID) - - // Don't double free this - if process.handle == 0 { - return nil - } - - if err := process.unregisterCallback(); err != nil { - return makeProcessError(process, operation, "", err) - } - - if err := hcsCloseProcess(process.handle); err != nil { - return makeProcessError(process, operation, "", err) - } - - process.handle = 0 - - logrus.Debugf(title+" succeeded processid=%d", process.processID) - return nil -} - -func (process *process) registerCallback() error { - context := ¬ifcationWatcherContext{ - channels: newChannels(), - } - - callbackMapLock.Lock() - callbackNumber := nextCallback - nextCallback++ - callbackMap[callbackNumber] = context - callbackMapLock.Unlock() - - var callbackHandle hcsCallback - err := hcsRegisterProcessCallback(process.handle, notificationWatcherCallback, callbackNumber, &callbackHandle) - if err != nil { - return err - } - context.handle = callbackHandle - process.callbackNumber = callbackNumber - - return nil -} - -func (process *process) unregisterCallback() error { - callbackNumber := process.callbackNumber - - callbackMapLock.RLock() - context := callbackMap[callbackNumber] - callbackMapLock.RUnlock() - - if context == nil { - return nil - } - - handle := context.handle - - if handle == 0 { - return nil - } - - // hcsUnregisterProcessCallback has its own syncronization - // to wait for all callbacks to complete. We must NOT hold the callbackMapLock. - err := hcsUnregisterProcessCallback(handle) - if err != nil { - return err - } - - closeChannels(context.channels) - - callbackMapLock.Lock() - callbackMap[callbackNumber] = nil - callbackMapLock.Unlock() - - handle = 0 - - return nil + return convertProcessError(process.p.Close(), process) } diff --git a/vendor/github.com/Microsoft/hcsshim/unpreparelayer.go b/vendor/github.com/Microsoft/hcsshim/unpreparelayer.go deleted file mode 100644 index e8a3b507b..000000000 --- a/vendor/github.com/Microsoft/hcsshim/unpreparelayer.go +++ /dev/null @@ -1,27 +0,0 @@ -package hcsshim - -import "github.com/sirupsen/logrus" - -// UnprepareLayer disables the filesystem filter for the read-write layer with -// the given id. -func UnprepareLayer(info DriverInfo, layerId string) error { - title := "hcsshim::UnprepareLayer " - logrus.Debugf(title+"flavour %d layerId %s", info.Flavour, layerId) - - // Convert info to API calling convention - infop, err := convertDriverInfo(info) - if err != nil { - logrus.Error(err) - return err - } - - err = unprepareLayer(&infop, layerId) - if err != nil { - err = makeErrorf(err, title, "layerId=%s flavour=%d", layerId, info.Flavour) - logrus.Error(err) - return err - } - - logrus.Debugf(title+"succeeded flavour %d layerId=%s", info.Flavour, layerId) - return nil -} diff --git a/vendor/github.com/Microsoft/hcsshim/version.go b/vendor/github.com/Microsoft/hcsshim/version.go index ae10c23d4..9ebb257b3 100644 --- a/vendor/github.com/Microsoft/hcsshim/version.go +++ b/vendor/github.com/Microsoft/hcsshim/version.go @@ -2,6 +2,5 @@ package hcsshim // IsTP4 returns whether the currently running Windows build is at least TP4. func IsTP4() bool { - // HNSCall was not present in TP4 - return procHNSCall.Find() != nil + return false } diff --git a/vendor/github.com/Microsoft/hcsshim/zhcsshim.go b/vendor/github.com/Microsoft/hcsshim/zhcsshim.go deleted file mode 100644 index 5123e8d8e..000000000 --- a/vendor/github.com/Microsoft/hcsshim/zhcsshim.go +++ /dev/null @@ -1,1080 +0,0 @@ -// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT - -package hcsshim - -import ( - "syscall" - "unsafe" - - "github.com/Microsoft/go-winio" - "golang.org/x/sys/windows" -) - -var _ unsafe.Pointer - -// Do the interface allocations only once for common -// Errno values. -const ( - errnoERROR_IO_PENDING = 997 -) - -var ( - errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) -) - -// errnoErr returns common boxed Errno values, to prevent -// allocations at runtime. -func errnoErr(e syscall.Errno) error { - switch e { - case 0: - return nil - case errnoERROR_IO_PENDING: - return errERROR_IO_PENDING - } - // TODO: add more here, after collecting data on the common - // error values see on Windows. (perhaps when running - // all.bat?) - return e -} - -var ( - modole32 = windows.NewLazySystemDLL("ole32.dll") - modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") - modvmcompute = windows.NewLazySystemDLL("vmcompute.dll") - modntdll = windows.NewLazySystemDLL("ntdll.dll") - modkernel32 = windows.NewLazySystemDLL("kernel32.dll") - - procCoTaskMemFree = modole32.NewProc("CoTaskMemFree") - procSetCurrentThreadCompartmentId = modiphlpapi.NewProc("SetCurrentThreadCompartmentId") - procActivateLayer = modvmcompute.NewProc("ActivateLayer") - procCopyLayer = modvmcompute.NewProc("CopyLayer") - procCreateLayer = modvmcompute.NewProc("CreateLayer") - procCreateSandboxLayer = modvmcompute.NewProc("CreateSandboxLayer") - procExpandSandboxSize = modvmcompute.NewProc("ExpandSandboxSize") - procDeactivateLayer = modvmcompute.NewProc("DeactivateLayer") - procDestroyLayer = modvmcompute.NewProc("DestroyLayer") - procExportLayer = modvmcompute.NewProc("ExportLayer") - procGetLayerMountPath = modvmcompute.NewProc("GetLayerMountPath") - procGetBaseImages = modvmcompute.NewProc("GetBaseImages") - procImportLayer = modvmcompute.NewProc("ImportLayer") - procLayerExists = modvmcompute.NewProc("LayerExists") - procNameToGuid = modvmcompute.NewProc("NameToGuid") - procPrepareLayer = modvmcompute.NewProc("PrepareLayer") - 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") - procHcsEnumerateComputeSystems = modvmcompute.NewProc("HcsEnumerateComputeSystems") - procHcsCreateComputeSystem = modvmcompute.NewProc("HcsCreateComputeSystem") - procHcsOpenComputeSystem = modvmcompute.NewProc("HcsOpenComputeSystem") - procHcsCloseComputeSystem = modvmcompute.NewProc("HcsCloseComputeSystem") - procHcsStartComputeSystem = modvmcompute.NewProc("HcsStartComputeSystem") - procHcsShutdownComputeSystem = modvmcompute.NewProc("HcsShutdownComputeSystem") - procHcsTerminateComputeSystem = modvmcompute.NewProc("HcsTerminateComputeSystem") - procHcsPauseComputeSystem = modvmcompute.NewProc("HcsPauseComputeSystem") - procHcsResumeComputeSystem = modvmcompute.NewProc("HcsResumeComputeSystem") - procHcsGetComputeSystemProperties = modvmcompute.NewProc("HcsGetComputeSystemProperties") - procHcsModifyComputeSystem = modvmcompute.NewProc("HcsModifyComputeSystem") - procHcsRegisterComputeSystemCallback = modvmcompute.NewProc("HcsRegisterComputeSystemCallback") - procHcsUnregisterComputeSystemCallback = modvmcompute.NewProc("HcsUnregisterComputeSystemCallback") - procHcsCreateProcess = modvmcompute.NewProc("HcsCreateProcess") - 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") - procHcsModifyServiceSettings = modvmcompute.NewProc("HcsModifyServiceSettings") - procHNSCall = modvmcompute.NewProc("HNSCall") - procNtCreateFile = modntdll.NewProc("NtCreateFile") - procNtSetInformationFile = modntdll.NewProc("NtSetInformationFile") - procRtlNtStatusToDosErrorNoTeb = modntdll.NewProc("RtlNtStatusToDosErrorNoTeb") - procLocalAlloc = modkernel32.NewProc("LocalAlloc") - procLocalFree = modkernel32.NewProc("LocalFree") -) - -func coTaskMemFree(buffer unsafe.Pointer) { - syscall.Syscall(procCoTaskMemFree.Addr(), 1, uintptr(buffer), 0, 0) - return -} - -func SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) { - r0, _, _ := syscall.Syscall(procSetCurrentThreadCompartmentId.Addr(), 1, uintptr(compartmentId), 0, 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func activateLayer(info *driverInfo, id string) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _activateLayer(info, _p0) -} - -func _activateLayer(info *driverInfo, id *uint16) (hr error) { - if hr = procActivateLayer.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procActivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func copyLayer(info *driverInfo, srcId string, dstId string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(srcId) - if hr != nil { - return - } - var _p1 *uint16 - _p1, hr = syscall.UTF16PtrFromString(dstId) - if hr != nil { - return - } - return _copyLayer(info, _p0, _p1, descriptors) -} - -func _copyLayer(info *driverInfo, srcId *uint16, dstId *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { - var _p2 *WC_LAYER_DESCRIPTOR - if len(descriptors) > 0 { - _p2 = &descriptors[0] - } - if hr = procCopyLayer.Find(); hr != nil { - return - } - 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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func createLayer(info *driverInfo, id string, parent string) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - var _p1 *uint16 - _p1, hr = syscall.UTF16PtrFromString(parent) - if hr != nil { - return - } - return _createLayer(info, _p0, _p1) -} - -func _createLayer(info *driverInfo, id *uint16, parent *uint16) (hr error) { - if hr = procCreateLayer.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procCreateLayer.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(parent))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func createSandboxLayer(info *driverInfo, id string, parent string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - var _p1 *uint16 - _p1, hr = syscall.UTF16PtrFromString(parent) - if hr != nil { - return - } - return _createSandboxLayer(info, _p0, _p1, descriptors) -} - -func _createSandboxLayer(info *driverInfo, id *uint16, parent *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { - var _p2 *WC_LAYER_DESCRIPTOR - if len(descriptors) > 0 { - _p2 = &descriptors[0] - } - if hr = procCreateSandboxLayer.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procCreateSandboxLayer.Addr(), 5, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(parent)), uintptr(unsafe.Pointer(_p2)), uintptr(len(descriptors)), 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func expandSandboxSize(info *driverInfo, id string, size uint64) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _expandSandboxSize(info, _p0, size) -} - -func _expandSandboxSize(info *driverInfo, id *uint16, size uint64) (hr error) { - if hr = procExpandSandboxSize.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procExpandSandboxSize.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(size)) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func deactivateLayer(info *driverInfo, id string) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _deactivateLayer(info, _p0) -} - -func _deactivateLayer(info *driverInfo, id *uint16) (hr error) { - if hr = procDeactivateLayer.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procDeactivateLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func destroyLayer(info *driverInfo, id string) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _destroyLayer(info, _p0) -} - -func _destroyLayer(info *driverInfo, id *uint16) (hr error) { - if hr = procDestroyLayer.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procDestroyLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func exportLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - var _p1 *uint16 - _p1, hr = syscall.UTF16PtrFromString(path) - if hr != nil { - return - } - return _exportLayer(info, _p0, _p1, descriptors) -} - -func _exportLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { - var _p2 *WC_LAYER_DESCRIPTOR - if len(descriptors) > 0 { - _p2 = &descriptors[0] - } - if hr = procExportLayer.Find(); hr != nil { - return - } - 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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func getLayerMountPath(info *driverInfo, id string, length *uintptr, buffer *uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _getLayerMountPath(info, _p0, length, buffer) -} - -func _getLayerMountPath(info *driverInfo, id *uint16, length *uintptr, buffer *uint16) (hr error) { - if hr = procGetLayerMountPath.Find(); hr != nil { - return - } - 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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func getBaseImages(buffer **uint16) (hr error) { - if hr = procGetBaseImages.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procGetBaseImages.Addr(), 1, uintptr(unsafe.Pointer(buffer)), 0, 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func importLayer(info *driverInfo, id string, path string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - var _p1 *uint16 - _p1, hr = syscall.UTF16PtrFromString(path) - if hr != nil { - return - } - return _importLayer(info, _p0, _p1, descriptors) -} - -func _importLayer(info *driverInfo, id *uint16, path *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { - var _p2 *WC_LAYER_DESCRIPTOR - if len(descriptors) > 0 { - _p2 = &descriptors[0] - } - if hr = procImportLayer.Find(); hr != nil { - return - } - 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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func layerExists(info *driverInfo, id string, exists *uint32) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _layerExists(info, _p0, exists) -} - -func _layerExists(info *driverInfo, id *uint16, exists *uint32) (hr error) { - if hr = procLayerExists.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procLayerExists.Addr(), 3, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(exists))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func nameToGuid(name string, guid *GUID) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(name) - if hr != nil { - return - } - return _nameToGuid(_p0, guid) -} - -func _nameToGuid(name *uint16, guid *GUID) (hr error) { - if hr = procNameToGuid.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procNameToGuid.Addr(), 2, uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(guid)), 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func prepareLayer(info *driverInfo, id string, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _prepareLayer(info, _p0, descriptors) -} - -func _prepareLayer(info *driverInfo, id *uint16, descriptors []WC_LAYER_DESCRIPTOR) (hr error) { - var _p1 *WC_LAYER_DESCRIPTOR - if len(descriptors) > 0 { - _p1 = &descriptors[0] - } - if hr = procPrepareLayer.Find(); hr != nil { - return - } - 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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func unprepareLayer(info *driverInfo, id string) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _unprepareLayer(info, _p0) -} - -func _unprepareLayer(info *driverInfo, id *uint16) (hr error) { - if hr = procUnprepareLayer.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procUnprepareLayer.Addr(), 2, uintptr(unsafe.Pointer(info)), uintptr(unsafe.Pointer(id)), 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func processBaseImage(path string) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(path) - if hr != nil { - return - } - return _processBaseImage(_p0) -} - -func _processBaseImage(path *uint16) (hr error) { - if hr = procProcessBaseImage.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procProcessBaseImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func processUtilityImage(path string) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(path) - if hr != nil { - return - } - return _processUtilityImage(_p0) -} - -func _processUtilityImage(path *uint16) (hr error) { - if hr = procProcessUtilityImage.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procProcessUtilityImage.Addr(), 1, uintptr(unsafe.Pointer(path)), 0, 0) - if int32(r0) < 0 { - hr = syscall.Errno(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 = syscall.Errno(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 = syscall.Errno(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 = syscall.Errno(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 = syscall.Errno(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 = syscall.Errno(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 = syscall.Errno(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 = syscall.Errno(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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsEnumerateComputeSystems(query string, computeSystems **uint16, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(query) - if hr != nil { - return - } - return _hcsEnumerateComputeSystems(_p0, computeSystems, result) -} - -func _hcsEnumerateComputeSystems(query *uint16, computeSystems **uint16, result **uint16) (hr error) { - if hr = procHcsEnumerateComputeSystems.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsEnumerateComputeSystems.Addr(), 3, uintptr(unsafe.Pointer(query)), uintptr(unsafe.Pointer(computeSystems)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsCreateComputeSystem(id string, configuration string, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - var _p1 *uint16 - _p1, hr = syscall.UTF16PtrFromString(configuration) - if hr != nil { - return - } - return _hcsCreateComputeSystem(_p0, _p1, identity, computeSystem, result) -} - -func _hcsCreateComputeSystem(id *uint16, configuration *uint16, identity syscall.Handle, computeSystem *hcsSystem, result **uint16) (hr error) { - if hr = procHcsCreateComputeSystem.Find(); hr != nil { - return - } - 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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsOpenComputeSystem(id string, computeSystem *hcsSystem, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(id) - if hr != nil { - return - } - return _hcsOpenComputeSystem(_p0, computeSystem, result) -} - -func _hcsOpenComputeSystem(id *uint16, computeSystem *hcsSystem, result **uint16) (hr error) { - if hr = procHcsOpenComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsOpenComputeSystem.Addr(), 3, uintptr(unsafe.Pointer(id)), uintptr(unsafe.Pointer(computeSystem)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsCloseComputeSystem(computeSystem hcsSystem) (hr error) { - if hr = procHcsCloseComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsCloseComputeSystem.Addr(), 1, uintptr(computeSystem), 0, 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsStartComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsStartComputeSystem(computeSystem, _p0, result) -} - -func _hcsStartComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { - if hr = procHcsStartComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsStartComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsShutdownComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsShutdownComputeSystem(computeSystem, _p0, result) -} - -func _hcsShutdownComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { - if hr = procHcsShutdownComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsShutdownComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsTerminateComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsTerminateComputeSystem(computeSystem, _p0, result) -} - -func _hcsTerminateComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { - if hr = procHcsTerminateComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsTerminateComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsPauseComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsPauseComputeSystem(computeSystem, _p0, result) -} - -func _hcsPauseComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { - if hr = procHcsPauseComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsPauseComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsResumeComputeSystem(computeSystem hcsSystem, options string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(options) - if hr != nil { - return - } - return _hcsResumeComputeSystem(computeSystem, _p0, result) -} - -func _hcsResumeComputeSystem(computeSystem hcsSystem, options *uint16, result **uint16) (hr error) { - if hr = procHcsResumeComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsResumeComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(options)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery string, properties **uint16, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(propertyQuery) - if hr != nil { - return - } - return _hcsGetComputeSystemProperties(computeSystem, _p0, properties, result) -} - -func _hcsGetComputeSystemProperties(computeSystem hcsSystem, propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { - if hr = procHcsGetComputeSystemProperties.Find(); hr != nil { - return - } - 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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsModifyComputeSystem(computeSystem hcsSystem, configuration string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(configuration) - if hr != nil { - return - } - return _hcsModifyComputeSystem(computeSystem, _p0, result) -} - -func _hcsModifyComputeSystem(computeSystem hcsSystem, configuration *uint16, result **uint16) (hr error) { - if hr = procHcsModifyComputeSystem.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsModifyComputeSystem.Addr(), 3, uintptr(computeSystem), uintptr(unsafe.Pointer(configuration)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsRegisterComputeSystemCallback(computeSystem hcsSystem, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) { - if hr = procHcsRegisterComputeSystemCallback.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procHcsRegisterComputeSystemCallback.Addr(), 4, uintptr(computeSystem), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsUnregisterComputeSystemCallback(callbackHandle hcsCallback) (hr error) { - if hr = procHcsUnregisterComputeSystemCallback.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsUnregisterComputeSystemCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsCreateProcess(computeSystem hcsSystem, processParameters string, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(processParameters) - if hr != nil { - return - } - return _hcsCreateProcess(computeSystem, _p0, processInformation, process, result) -} - -func _hcsCreateProcess(computeSystem hcsSystem, processParameters *uint16, processInformation *hcsProcessInformation, process *hcsProcess, result **uint16) (hr error) { - if hr = procHcsCreateProcess.Find(); hr != nil { - return - } - 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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsOpenProcess(computeSystem hcsSystem, pid uint32, process *hcsProcess, result **uint16) (hr error) { - if hr = procHcsOpenProcess.Find(); hr != nil { - return - } - 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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsCloseProcess(process hcsProcess) (hr error) { - if hr = procHcsCloseProcess.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsCloseProcess.Addr(), 1, uintptr(process), 0, 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsTerminateProcess(process hcsProcess, result **uint16) (hr error) { - if hr = procHcsTerminateProcess.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsTerminateProcess.Addr(), 2, uintptr(process), uintptr(unsafe.Pointer(result)), 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsGetProcessInfo(process hcsProcess, processInformation *hcsProcessInformation, result **uint16) (hr error) { - if hr = procHcsGetProcessInfo.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsGetProcessInfo.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processInformation)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsGetProcessProperties(process hcsProcess, processProperties **uint16, result **uint16) (hr error) { - if hr = procHcsGetProcessProperties.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsGetProcessProperties.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(processProperties)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsModifyProcess(process hcsProcess, settings string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(settings) - if hr != nil { - return - } - return _hcsModifyProcess(process, _p0, result) -} - -func _hcsModifyProcess(process hcsProcess, settings *uint16, result **uint16) (hr error) { - if hr = procHcsModifyProcess.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsModifyProcess.Addr(), 3, uintptr(process), uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsGetServiceProperties(propertyQuery string, properties **uint16, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(propertyQuery) - if hr != nil { - return - } - return _hcsGetServiceProperties(_p0, properties, result) -} - -func _hcsGetServiceProperties(propertyQuery *uint16, properties **uint16, result **uint16) (hr error) { - if hr = procHcsGetServiceProperties.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsGetServiceProperties.Addr(), 3, uintptr(unsafe.Pointer(propertyQuery)), uintptr(unsafe.Pointer(properties)), uintptr(unsafe.Pointer(result))) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsRegisterProcessCallback(process hcsProcess, callback uintptr, context uintptr, callbackHandle *hcsCallback) (hr error) { - if hr = procHcsRegisterProcessCallback.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall6(procHcsRegisterProcessCallback.Addr(), 4, uintptr(process), uintptr(callback), uintptr(context), uintptr(unsafe.Pointer(callbackHandle)), 0, 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsUnregisterProcessCallback(callbackHandle hcsCallback) (hr error) { - if hr = procHcsUnregisterProcessCallback.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsUnregisterProcessCallback.Addr(), 1, uintptr(callbackHandle), 0, 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func hcsModifyServiceSettings(settings string, result **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(settings) - if hr != nil { - return - } - return _hcsModifyServiceSettings(_p0, result) -} - -func _hcsModifyServiceSettings(settings *uint16, result **uint16) (hr error) { - if hr = procHcsModifyServiceSettings.Find(); hr != nil { - return - } - r0, _, _ := syscall.Syscall(procHcsModifyServiceSettings.Addr(), 2, uintptr(unsafe.Pointer(settings)), uintptr(unsafe.Pointer(result)), 0) - if int32(r0) < 0 { - hr = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func _hnsCall(method string, path string, object string, response **uint16) (hr error) { - var _p0 *uint16 - _p0, hr = syscall.UTF16PtrFromString(method) - if hr != nil { - return - } - var _p1 *uint16 - _p1, hr = syscall.UTF16PtrFromString(path) - if hr != nil { - return - } - var _p2 *uint16 - _p2, hr = syscall.UTF16PtrFromString(object) - if hr != nil { - return - } - return __hnsCall(_p0, _p1, _p2, response) -} - -func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16) (hr error) { - if hr = procHNSCall.Find(); hr != nil { - return - } - 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 = syscall.Errno(win32FromHresult(r0)) - } - return -} - -func ntCreateFile(handle *uintptr, accessMask uint32, oa *objectAttributes, iosb *ioStatusBlock, allocationSize *uint64, fileAttributes uint32, shareAccess uint32, createDisposition uint32, createOptions uint32, eaBuffer *byte, eaLength uint32) (status uint32) { - r0, _, _ := syscall.Syscall12(procNtCreateFile.Addr(), 11, uintptr(unsafe.Pointer(handle)), uintptr(accessMask), uintptr(unsafe.Pointer(oa)), uintptr(unsafe.Pointer(iosb)), uintptr(unsafe.Pointer(allocationSize)), uintptr(fileAttributes), uintptr(shareAccess), uintptr(createDisposition), uintptr(createOptions), uintptr(unsafe.Pointer(eaBuffer)), uintptr(eaLength), 0) - status = uint32(r0) - return -} - -func ntSetInformationFile(handle uintptr, iosb *ioStatusBlock, information uintptr, length uint32, class uint32) (status uint32) { - r0, _, _ := syscall.Syscall6(procNtSetInformationFile.Addr(), 5, uintptr(handle), uintptr(unsafe.Pointer(iosb)), uintptr(information), uintptr(length), uintptr(class), 0) - status = uint32(r0) - return -} - -func rtlNtStatusToDosError(status uint32) (winerr error) { - r0, _, _ := syscall.Syscall(procRtlNtStatusToDosErrorNoTeb.Addr(), 1, uintptr(status), 0, 0) - if r0 != 0 { - winerr = syscall.Errno(r0) - } - return -} - -func localAlloc(flags uint32, size int) (ptr uintptr) { - r0, _, _ := syscall.Syscall(procLocalAlloc.Addr(), 2, uintptr(flags), uintptr(size), 0) - ptr = uintptr(r0) - return -} - -func localFree(ptr uintptr) { - syscall.Syscall(procLocalFree.Addr(), 1, uintptr(ptr), 0, 0) - return -} diff --git a/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go b/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go new file mode 100644 index 000000000..cd471295b --- /dev/null +++ b/vendor/github.com/Microsoft/hcsshim/zsyscall_windows.go @@ -0,0 +1,52 @@ +// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT + +package hcsshim + +import ( + "syscall" + "unsafe" + + "github.com/Microsoft/hcsshim/internal/interop" + "golang.org/x/sys/windows" +) + +var _ unsafe.Pointer + +// Do the interface allocations only once for common +// Errno values. +const ( + errnoERROR_IO_PENDING = 997 +) + +var ( + errERROR_IO_PENDING error = syscall.Errno(errnoERROR_IO_PENDING) +) + +// errnoErr returns common boxed Errno values, to prevent +// allocations at runtime. +func errnoErr(e syscall.Errno) error { + switch e { + case 0: + return nil + case errnoERROR_IO_PENDING: + return errERROR_IO_PENDING + } + // TODO: add more here, after collecting data on the common + // error values see on Windows. (perhaps when running + // all.bat?) + return e +} + +var ( + modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll") + + procSetCurrentThreadCompartmentId = modiphlpapi.NewProc("SetCurrentThreadCompartmentId") +) + +func SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) { + r0, _, _ := syscall.Syscall(procSetCurrentThreadCompartmentId.Addr(), 1, uintptr(compartmentId), 0, 0) + if int32(r0) < 0 { + hr = interop.Win32FromHresult(r0) + } + return +} diff --git a/vendor/github.com/containerd/console/console_windows.go b/vendor/github.com/containerd/console/console_windows.go index ff0174df4..62dbe1c03 100644 --- a/vendor/github.com/containerd/console/console_windows.go +++ b/vendor/github.com/containerd/console/console_windows.go @@ -17,6 +17,7 @@ package console import ( + "fmt" "os" "github.com/pkg/errors" @@ -28,55 +29,90 @@ var ( ErrNotImplemented = errors.New("not implemented") ) -func (m *master) init() { - m.h = windows.Handle(m.f.Fd()) - if err := windows.GetConsoleMode(m.h, &m.mode); err == nil { - if m.f == os.Stdin { - // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. - if err = windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil { - vtInputSupported = true - } - // Unconditionally set the console mode back even on failure because SetConsoleMode - // remembers invalid bits on input handles. - windows.SetConsoleMode(m.h, m.mode) - } else if err := windows.SetConsoleMode(m.h, m.mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { - m.mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING - } else { - windows.SetConsoleMode(m.h, m.mode) +func (m *master) initStdios() { + m.in = windows.Handle(os.Stdin.Fd()) + if err := windows.GetConsoleMode(m.in, &m.inMode); err == nil { + // Validate that windows.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it. + if err = windows.SetConsoleMode(m.in, m.inMode|windows.ENABLE_VIRTUAL_TERMINAL_INPUT); err == nil { + vtInputSupported = true } + // Unconditionally set the console mode back even on failure because SetConsoleMode + // remembers invalid bits on input handles. + windows.SetConsoleMode(m.in, m.inMode) + } else { + fmt.Printf("failed to get console mode for stdin: %v\n", err) + } + + m.out = windows.Handle(os.Stdout.Fd()) + if err := windows.GetConsoleMode(m.out, &m.outMode); err == nil { + if err := windows.SetConsoleMode(m.out, m.outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { + m.outMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + } else { + windows.SetConsoleMode(m.out, m.outMode) + } + } else { + fmt.Printf("failed to get console mode for stdout: %v\n", err) + } + + m.err = windows.Handle(os.Stderr.Fd()) + if err := windows.GetConsoleMode(m.err, &m.errMode); err == nil { + if err := windows.SetConsoleMode(m.err, m.errMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil { + m.errMode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING + } else { + windows.SetConsoleMode(m.err, m.errMode) + } + } else { + fmt.Printf("failed to get console mode for stderr: %v\n", err) } } type master struct { - h windows.Handle - mode uint32 - f *os.File + in windows.Handle + inMode uint32 + + out windows.Handle + outMode uint32 + + err windows.Handle + errMode uint32 } func (m *master) SetRaw() error { - if m.f == os.Stdin { - if err := makeInputRaw(m.h, m.mode); err != nil { - return err - } - } else { - // Set StdOut and StdErr to raw mode, we ignore failures since - // windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of - // Windows. - windows.SetConsoleMode(m.h, m.mode|windows.DISABLE_NEWLINE_AUTO_RETURN) + if err := makeInputRaw(m.in, m.inMode); err != nil { + return err } + + // Set StdOut and StdErr to raw mode, we ignore failures since + // windows.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this version of + // Windows. + + windows.SetConsoleMode(m.out, m.outMode|windows.DISABLE_NEWLINE_AUTO_RETURN) + + windows.SetConsoleMode(m.err, m.errMode|windows.DISABLE_NEWLINE_AUTO_RETURN) + return nil } func (m *master) Reset() error { - if err := windows.SetConsoleMode(m.h, m.mode); err != nil { - return errors.Wrap(err, "unable to restore console mode") + for _, s := range []struct { + fd windows.Handle + mode uint32 + }{ + {m.in, m.inMode}, + {m.out, m.outMode}, + {m.err, m.errMode}, + } { + if err := windows.SetConsoleMode(s.fd, s.mode); err != nil { + return errors.Wrap(err, "unable to restore console mode") + } } + return nil } func (m *master) Size() (WinSize, error) { var info windows.ConsoleScreenBufferInfo - err := windows.GetConsoleScreenBufferInfo(m.h, &info) + err := windows.GetConsoleScreenBufferInfo(m.out, &info) if err != nil { return WinSize{}, errors.Wrap(err, "unable to get console info") } @@ -98,11 +134,11 @@ func (m *master) ResizeFrom(c Console) error { } func (m *master) DisableEcho() error { - mode := m.mode &^ windows.ENABLE_ECHO_INPUT + mode := m.inMode &^ windows.ENABLE_ECHO_INPUT mode |= windows.ENABLE_PROCESSED_INPUT mode |= windows.ENABLE_LINE_INPUT - if err := windows.SetConsoleMode(m.h, mode); err != nil { + if err := windows.SetConsoleMode(m.in, mode); err != nil { return errors.Wrap(err, "unable to set console to disable echo") } @@ -114,15 +150,15 @@ func (m *master) Close() error { } func (m *master) Read(b []byte) (int, error) { - return m.f.Read(b) + return os.Stdin.Read(b) } func (m *master) Write(b []byte) (int, error) { - return m.f.Write(b) + return os.Stdout.Write(b) } func (m *master) Fd() uintptr { - return uintptr(m.h) + return uintptr(m.in) } // on windows, console can only be made from os.Std{in,out,err}, hence there @@ -174,7 +210,7 @@ func newMaster(f *os.File) (Console, error) { if f != os.Stdin && f != os.Stdout && f != os.Stderr { return nil, errors.New("creating a console from a file is not supported on windows") } - m := &master{f: f} - m.init() + m := &master{} + m.initStdios() return m, nil } diff --git a/vendor/github.com/containerd/containerd/README.md b/vendor/github.com/containerd/containerd/README.md index a8acc9181..d3e4aa435 100644 --- a/vendor/github.com/containerd/containerd/README.md +++ b/vendor/github.com/containerd/containerd/README.md @@ -1,7 +1,8 @@ -![banner](/docs/static/img/containerd-dark.png?raw=true) +![banner](https://github.com/containerd/containerd.io/blob/master/static/img/containerd-dark.png?raw=true) [![GoDoc](https://godoc.org/github.com/containerd/containerd?status.svg)](https://godoc.org/github.com/containerd/containerd) [![Build Status](https://travis-ci.org/containerd/containerd.svg?branch=master)](https://travis-ci.org/containerd/containerd) +[![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/containerd/containerd?branch=master&svg=true)](https://ci.appveyor.com/project/mlaventure/containerd-3g73f?branch=master) [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd.svg?type=shield)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fcontainerd%2Fcontainerd?ref=badge_shield) [![Go Report Card](https://goreportcard.com/badge/github.com/containerd/containerd)](https://goreportcard.com/report/github.com/containerd/containerd) [![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/1271/badge)](https://bestpractices.coreinfrastructure.org/projects/1271) diff --git a/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go b/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go index 0173f394e..d6a7b38a8 100644 --- a/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go +++ b/vendor/github.com/containerd/containerd/api/services/events/v1/events.pb.go @@ -141,7 +141,7 @@ type EventsClient interface { // Forward sends an event that has already been packaged into an envelope // with a timestamp and namespace. // - // This is useful if earlier timestamping is required or when fowarding on + // This is useful if earlier timestamping is required or when forwarding on // behalf of another component, namespace or publisher. Forward(ctx context.Context, in *ForwardRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error) // Subscribe to a stream of events, possibly returning only that match any @@ -223,7 +223,7 @@ type EventsServer interface { // Forward sends an event that has already been packaged into an envelope // with a timestamp and namespace. // - // This is useful if earlier timestamping is required or when fowarding on + // This is useful if earlier timestamping is required or when forwarding on // behalf of another component, namespace or publisher. Forward(context.Context, *ForwardRequest) (*google_protobuf2.Empty, error) // Subscribe to a stream of events, possibly returning only that match any diff --git a/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto b/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto index 58f2dadeb..1959c8e39 100644 --- a/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto +++ b/vendor/github.com/containerd/containerd/api/services/events/v1/events.proto @@ -20,7 +20,7 @@ service Events { // Forward sends an event that has already been packaged into an envelope // with a timestamp and namespace. // - // This is useful if earlier timestamping is required or when fowarding on + // This is useful if earlier timestamping is required or when forwarding on // behalf of another component, namespace or publisher. rpc Forward(ForwardRequest) returns (google.protobuf.Empty); diff --git a/vendor/github.com/containerd/containerd/archive/tar_windows.go b/vendor/github.com/containerd/containerd/archive/tar_windows.go index 025796a7b..a3f585ac8 100644 --- a/vendor/github.com/containerd/containerd/archive/tar_windows.go +++ b/vendor/github.com/containerd/containerd/archive/tar_windows.go @@ -259,7 +259,7 @@ func fileInfoFromHeader(hdr *tar.Header) (name string, size int64, fileInfo *win if err != nil { return "", 0, nil, err } - fileInfo.FileAttributes = uintptr(attr) + fileInfo.FileAttributes = uint32(attr) } else { if hdr.Typeflag == tar.TypeDir { fileInfo.FileAttributes |= syscall.FILE_ATTRIBUTE_DIRECTORY diff --git a/vendor/github.com/containerd/containerd/cio/io.go b/vendor/github.com/containerd/containerd/cio/io.go index 10ad36ba8..a9c6d2b15 100644 --- a/vendor/github.com/containerd/containerd/cio/io.go +++ b/vendor/github.com/containerd/containerd/cio/io.go @@ -141,6 +141,15 @@ func NewCreator(opts ...Opt) Creator { if err != nil { return nil, err } + if streams.Stdin == nil { + fifos.Stdin = "" + } + if streams.Stdout == nil { + fifos.Stdout = "" + } + if streams.Stderr == nil { + fifos.Stderr = "" + } return copyIO(fifos, streams) } } diff --git a/vendor/github.com/containerd/containerd/cio/io_windows.go b/vendor/github.com/containerd/containerd/cio/io_windows.go index a751f7d7a..5208f3eaa 100644 --- a/vendor/github.com/containerd/containerd/cio/io_windows.go +++ b/vendor/github.com/containerd/containerd/cio/io_windows.go @@ -74,7 +74,7 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { if fifos.Stdout != "" { l, err := winio.ListenPipe(fifos.Stdout, nil) if err != nil { - return nil, errors.Wrapf(err, "failed to create stdin pipe %s", fifos.Stdout) + return nil, errors.Wrapf(err, "failed to create stdout pipe %s", fifos.Stdout) } defer func(l net.Listener) { if err != nil { @@ -99,7 +99,7 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { }() } - if !fifos.Terminal && fifos.Stderr != "" { + if fifos.Stderr != "" { l, err := winio.ListenPipe(fifos.Stderr, nil) if err != nil { return nil, errors.Wrapf(err, "failed to create stderr pipe %s", fifos.Stderr) diff --git a/vendor/github.com/containerd/containerd/client.go b/vendor/github.com/containerd/containerd/client.go index 788a381d6..fd20c3dd0 100644 --- a/vendor/github.com/containerd/containerd/client.go +++ b/vendor/github.com/containerd/containerd/client.go @@ -82,6 +82,9 @@ func New(address string, opts ...ClientOpt) (*Client, error) { return nil, err } } + if copts.timeout == 0 { + copts.timeout = 10 * time.Second + } rt := fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS) if copts.defaultRuntime != "" { rt = copts.defaultRuntime @@ -115,7 +118,7 @@ func New(address string, opts ...ClientOpt) (*Client, error) { ) } connector := func() (*grpc.ClientConn, error) { - ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), copts.timeout) defer cancel() conn, err := grpc.DialContext(ctx, dialer.DialAddress(address), gopts...) if err != nil { @@ -256,9 +259,10 @@ type RemoteContext struct { // If no resolver is provided, defaults to Docker registry resolver. Resolver remotes.Resolver - // Platforms defines which platforms to handle when doing the image operation. - // If this field is empty, content for all platforms will be pulled. - Platforms []string + // PlatformMatcher is used to match the platforms for an image + // operation and define the preference when a single match is required + // from multiple platforms. + PlatformMatcher platforms.MatchComparer // Unpack is done after an image is pulled to extract into a snapshotter. // If an image is not unpacked on pull, it can be unpacked any time @@ -280,6 +284,12 @@ type RemoteContext struct { // manifests. If this option is false then any image which resolves // to schema 1 will return an error since schema 1 is not supported. ConvertSchema1 bool + + // Platforms defines which platforms to handle when doing the image operation. + // Platforms is ignored when a PlatformMatcher is set, otherwise the + // platforms will be used to create a PlatformMatcher with no ordering + // preference. + Platforms []string } func defaultRemoteContext() *RemoteContext { @@ -305,13 +315,30 @@ func (c *Client) Fetch(ctx context.Context, ref string, opts ...RemoteOpt) (imag return images.Image{}, errors.New("unpack on fetch not supported, try pull") } + if fetchCtx.PlatformMatcher == nil { + if len(fetchCtx.Platforms) == 0 { + fetchCtx.PlatformMatcher = platforms.All + } else { + var ps []ocispec.Platform + for _, s := range fetchCtx.Platforms { + p, err := platforms.Parse(s) + if err != nil { + return images.Image{}, errors.Wrapf(err, "invalid platform %s", s) + } + ps = append(ps, p) + } + + fetchCtx.PlatformMatcher = platforms.Any(ps...) + } + } + ctx, done, err := c.WithLease(ctx) if err != nil { return images.Image{}, err } defer done(ctx) - return c.fetch(ctx, fetchCtx, ref) + return c.fetch(ctx, fetchCtx, ref, 0) } // Pull downloads the provided content into containerd's content store @@ -324,10 +351,19 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image } } - if len(pullCtx.Platforms) > 1 { - return nil, errors.New("cannot pull multiplatform image locally, try Fetch") - } else if len(pullCtx.Platforms) == 0 { - pullCtx.Platforms = []string{platforms.Default()} + if pullCtx.PlatformMatcher == nil { + if len(pullCtx.Platforms) > 1 { + return nil, errors.New("cannot pull multiplatform image locally, try Fetch") + } else if len(pullCtx.Platforms) == 0 { + pullCtx.PlatformMatcher = platforms.Default() + } else { + p, err := platforms.Parse(pullCtx.Platforms[0]) + if err != nil { + return nil, errors.Wrapf(err, "invalid platform %s", pullCtx.Platforms[0]) + } + + pullCtx.PlatformMatcher = platforms.Only(p) + } } ctx, done, err := c.WithLease(ctx) @@ -336,12 +372,12 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image } defer done(ctx) - img, err := c.fetch(ctx, pullCtx, ref) + img, err := c.fetch(ctx, pullCtx, ref, 1) if err != nil { return nil, err } - i := NewImageWithPlatform(c, img, pullCtx.Platforms[0]) + i := NewImageWithPlatform(c, img, pullCtx.PlatformMatcher) if pullCtx.Unpack { if err := i.Unpack(ctx, pullCtx.Snapshotter); err != nil { @@ -352,7 +388,7 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image return i, nil } -func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string) (images.Image, error) { +func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string, limit int) (images.Image, error) { store := c.ContentStore() name, desc, err := rCtx.Resolver.Resolve(ctx, ref) if err != nil { @@ -377,7 +413,11 @@ func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string) (im // Set any children labels for that content childrenHandler = images.SetChildrenLabels(store, childrenHandler) // Filter children by platforms - childrenHandler = images.FilterPlatforms(childrenHandler, rCtx.Platforms...) + childrenHandler = images.FilterPlatforms(childrenHandler, rCtx.PlatformMatcher) + // Sort and limit manifests if a finite number is needed + if limit > 0 { + childrenHandler = images.LimitManifests(childrenHandler, rCtx.PlatformMatcher, limit) + } handler = images.Handlers(append(rCtx.BaseHandlers, remotes.FetchHandler(store, fetcher), @@ -434,13 +474,28 @@ func (c *Client) Push(ctx context.Context, ref string, desc ocispec.Descriptor, return err } } + if pushCtx.PlatformMatcher == nil { + if len(pushCtx.Platforms) > 0 { + var ps []ocispec.Platform + for _, platform := range pushCtx.Platforms { + p, err := platforms.Parse(platform) + if err != nil { + return errors.Wrapf(err, "invalid platform %s", platform) + } + ps = append(ps, p) + } + pushCtx.PlatformMatcher = platforms.Any(ps...) + } else { + pushCtx.PlatformMatcher = platforms.All + } + } pusher, err := pushCtx.Resolver.Pusher(ctx, ref) if err != nil { return err } - return remotes.PushContent(ctx, pusher, desc, c.ContentStore(), pushCtx.Platforms, pushCtx.BaseHandlers...) + return remotes.PushContent(ctx, pusher, desc, c.ContentStore(), pushCtx.PlatformMatcher, pushCtx.BaseHandlers...) } // GetImage returns an existing image diff --git a/vendor/github.com/containerd/containerd/client_opts.go b/vendor/github.com/containerd/containerd/client_opts.go index 6e6198739..b7431ad29 100644 --- a/vendor/github.com/containerd/containerd/client_opts.go +++ b/vendor/github.com/containerd/containerd/client_opts.go @@ -17,6 +17,8 @@ package containerd import ( + "time" + "github.com/containerd/containerd/images" "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/remotes" @@ -28,6 +30,7 @@ type clientOpts struct { defaultRuntime string services *services dialOptions []grpc.DialOption + timeout time.Duration } // ClientOpt allows callers to set options on the containerd client @@ -71,6 +74,14 @@ func WithServices(opts ...ServicesOpt) ClientOpt { } } +// WithTimeout sets the connection timeout for the client +func WithTimeout(d time.Duration) ClientOpt { + return func(c *clientOpts) error { + c.timeout = d + return nil + } +} + // RemoteOpt allows the caller to set distribution options for a remote type RemoteOpt func(*Client, *RemoteContext) error @@ -78,7 +89,7 @@ type RemoteOpt func(*Client, *RemoteContext) error // content for func WithPlatform(platform string) RemoteOpt { if platform == "" { - platform = platforms.Default() + platform = platforms.DefaultString() } return func(_ *Client, c *RemoteContext) error { for _, p := range c.Platforms { @@ -92,6 +103,16 @@ func WithPlatform(platform string) RemoteOpt { } } +// WithPlatformMatcher specifies the matcher to use for +// determining which platforms to pull content for. +// This value supersedes anything set with `WithPlatform`. +func WithPlatformMatcher(m platforms.MatchComparer) RemoteOpt { + return func(_ *Client, c *RemoteContext) error { + c.PlatformMatcher = m + return nil + } +} + // WithPullUnpack is used to unpack an image after pull. This // uses the snapshotter, content store, and diff service // configured for the client. diff --git a/vendor/github.com/containerd/containerd/cmd/ctr/commands/containers/containers.go b/vendor/github.com/containerd/containerd/cmd/ctr/commands/containers/containers.go index ec49ff996..b89015216 100644 --- a/vendor/github.com/containerd/containerd/cmd/ctr/commands/containers/containers.go +++ b/vendor/github.com/containerd/containerd/cmd/ctr/commands/containers/containers.go @@ -53,11 +53,22 @@ var createCommand = cli.Command{ Flags: append(commands.SnapshotterFlags, commands.ContainerFlags...), Action: func(context *cli.Context) error { var ( - id = context.Args().Get(1) - ref = context.Args().First() + id string + ref string + config = context.IsSet("config") ) - if ref == "" { - return errors.New("image ref must be provided") + + if config { + id = context.Args().First() + if context.NArg() > 1 { + return errors.New("with spec config file, only container id should be provided") + } + } else { + id = context.Args().Get(1) + ref = context.Args().First() + if ref == "" { + return errors.New("image ref must be provided") + } } if id == "" { return errors.New("container id must be provided") diff --git a/vendor/github.com/containerd/containerd/cmd/ctr/commands/content/fetch.go b/vendor/github.com/containerd/containerd/cmd/ctr/commands/content/fetch.go index 4169bd620..a9b70026c 100644 --- a/vendor/github.com/containerd/containerd/cmd/ctr/commands/content/fetch.go +++ b/vendor/github.com/containerd/containerd/cmd/ctr/commands/content/fetch.go @@ -76,28 +76,61 @@ Most of this is experimental and there are few leaps to make this work.`, return err } defer cancel() - - _, err = Fetch(ctx, client, ref, clicontext) + config, err := NewFetchConfig(ctx, clicontext) + if err != nil { + return err + } + _, err = Fetch(ctx, client, ref, config) return err }, } -// Fetch loads all resources into the content store and returns the image -func Fetch(ctx context.Context, client *containerd.Client, ref string, cliContext *cli.Context) (images.Image, error) { - resolver, err := commands.GetResolver(ctx, cliContext) - if err != nil { - return images.Image{}, err - } +// FetchConfig for content fetch +type FetchConfig struct { + // Resolver + Resolver remotes.Resolver + // ProgressOutput to display progress + ProgressOutput io.Writer + // Labels to set on the content + Labels []string + // Platforms to fetch + Platforms []string +} +// NewFetchConfig returns the default FetchConfig from cli flags +func NewFetchConfig(ctx context.Context, clicontext *cli.Context) (*FetchConfig, error) { + resolver, err := commands.GetResolver(ctx, clicontext) + if err != nil { + return nil, err + } + config := &FetchConfig{ + Resolver: resolver, + Labels: clicontext.StringSlice("label"), + } + if !clicontext.GlobalBool("debug") { + config.ProgressOutput = os.Stdout + } + if !clicontext.Bool("all-platforms") { + p := clicontext.StringSlice("platform") + if len(p) == 0 { + p = append(p, platforms.DefaultString()) + } + config.Platforms = p + } + return config, nil +} + +// Fetch loads all resources into the content store and returns the image +func Fetch(ctx context.Context, client *containerd.Client, ref string, config *FetchConfig) (images.Image, error) { ongoing := newJobs(ref) pctx, stopProgress := context.WithCancel(ctx) progress := make(chan struct{}) go func() { - if !cliContext.GlobalBool("debug") { + if config.ProgressOutput != nil { // no progress bar, because it hides some debug logs - showProgress(pctx, ongoing, client.ContentStore(), os.Stdout) + showProgress(pctx, ongoing, client.ContentStore(), config.ProgressOutput) } close(progress) }() @@ -110,24 +143,16 @@ func Fetch(ctx context.Context, client *containerd.Client, ref string, cliContex }) log.G(pctx).WithField("image", ref).Debug("fetching") - labels := commands.LabelArgs(cliContext.StringSlice("label")) + labels := commands.LabelArgs(config.Labels) opts := []containerd.RemoteOpt{ containerd.WithPullLabels(labels), - containerd.WithResolver(resolver), + containerd.WithResolver(config.Resolver), containerd.WithImageHandler(h), containerd.WithSchema1Conversion, } - - if !cliContext.Bool("all-platforms") { - p := cliContext.StringSlice("platform") - if len(p) == 0 { - p = append(p, platforms.Default()) - } - for _, platform := range p { - opts = append(opts, containerd.WithPlatform(platform)) - } + for _, platform := range config.Platforms { + opts = append(opts, containerd.WithPlatform(platform)) } - img, err := client.Fetch(pctx, ref, opts...) stopProgress() if err != nil { diff --git a/vendor/github.com/containerd/containerd/cmd/ctr/commands/images/pull.go b/vendor/github.com/containerd/containerd/cmd/ctr/commands/images/pull.go index de99ae4a6..3216976be 100644 --- a/vendor/github.com/containerd/containerd/cmd/ctr/commands/images/pull.go +++ b/vendor/github.com/containerd/containerd/cmd/ctr/commands/images/pull.go @@ -25,6 +25,7 @@ import ( "github.com/containerd/containerd/images" "github.com/containerd/containerd/log" "github.com/containerd/containerd/platforms" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/urfave/cli" ) @@ -73,7 +74,11 @@ command. As part of this process, we do the following: } defer done(ctx) - img, err := content.Fetch(ctx, client, ref, context) + config, err := content.NewFetchConfig(ctx, context) + if err != nil { + return err + } + img, err := content.Fetch(ctx, client, ref, config) if err != nil { return err } @@ -82,26 +87,28 @@ command. As part of this process, we do the following: // TODO: Show unpack status - var p []string + var p []ocispec.Platform if context.Bool("all-platforms") { - all, err := images.Platforms(ctx, client.ContentStore(), img.Target) + p, err = images.Platforms(ctx, client.ContentStore(), img.Target) if err != nil { return errors.Wrap(err, "unable to resolve image platforms") } - p = make([]string, len(all)) - for i := range all { - p[i] = platforms.Format(all[i]) - } } else { - p = context.StringSlice("platform") + for _, s := range context.StringSlice("platform") { + ps, err := platforms.Parse(s) + if err != nil { + return errors.Wrapf(err, "unable to parse platform %s", s) + } + p = append(p, ps) + } } if len(p) == 0 { - p = append(p, platforms.Default()) + p = append(p, platforms.DefaultSpec()) } for _, platform := range p { - fmt.Printf("unpacking %s %s...\n", platform, img.Target.Digest) - i := containerd.NewImageWithPlatform(client, img, platform) + fmt.Printf("unpacking %s %s...\n", platforms.Format(platform), img.Target.Digest) + i := containerd.NewImageWithPlatform(client, img, platforms.Only(platform)) err = i.Unpack(ctx, context.String("snapshotter")) if err != nil { return err diff --git a/vendor/github.com/containerd/containerd/cmd/ctr/commands/install/install.go b/vendor/github.com/containerd/containerd/cmd/ctr/commands/install/install.go index 4113418fb..e04fa926a 100644 --- a/vendor/github.com/containerd/containerd/cmd/ctr/commands/install/install.go +++ b/vendor/github.com/containerd/containerd/cmd/ctr/commands/install/install.go @@ -37,6 +37,10 @@ var Command = cli.Command{ Name: "replace,r", Usage: "replace any binaries or libs in the opt directory", }, + cli.StringFlag{ + Name: "path", + Usage: "set an optional install path other than the managed opt directory", + }, }, Action: func(context *cli.Context) error { client, ctx, cancel, err := commands.NewClient(context) @@ -56,6 +60,9 @@ var Command = cli.Command{ if context.Bool("replace") { opts = append(opts, containerd.WithInstallReplace) } + if path := context.String("path"); path != "" { + opts = append(opts, containerd.WithInstallPath(path)) + } return client.Install(ctx, image, opts...) }, } diff --git a/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run.go b/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run.go index da91429cc..c314b1e8c 100644 --- a/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run.go +++ b/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run.go @@ -110,15 +110,26 @@ var Command = cli.Command{ Action: func(context *cli.Context) error { var ( err error + id string + ref string - id = context.Args().Get(1) - ref = context.Args().First() tty = context.Bool("tty") detach = context.Bool("detach") + config = context.IsSet("config") ) - if ref == "" { - return errors.New("image ref must be provided") + if config { + id = context.Args().First() + if context.NArg() > 1 { + return errors.New("with spec config file, only container id should be provided") + } + } else { + id = context.Args().Get(1) + ref = context.Args().First() + + if ref == "" { + return errors.New("image ref must be provided") + } } if id == "" { return errors.New("container id must be provided") @@ -135,9 +146,17 @@ var Command = cli.Command{ if context.Bool("rm") && !detach { defer container.Delete(ctx, containerd.WithSnapshotCleanup) } + var con console.Console + if tty { + con = console.Current() + defer con.Reset() + if err := con.SetRaw(); err != nil { + return err + } + } opts := getNewTaskOpts(context) ioOpts := []cio.Opt{cio.WithFIFODir(context.String("fifo-dir"))} - task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), tty, context.Bool("null-io"), ioOpts, opts...) + task, err := tasks.NewTask(ctx, client, container, context.String("checkpoint"), con, context.Bool("null-io"), ioOpts, opts...) if err != nil { return err } @@ -153,14 +172,6 @@ var Command = cli.Command{ return err } } - var con console.Console - if tty { - con = console.Current() - defer con.Reset() - if err := con.SetRaw(); err != nil { - return err - } - } if err := task.Start(ctx); err != nil { return err } diff --git a/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run_unix.go b/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run_unix.go index 6b1f242d2..d82b0e648 100644 --- a/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run_unix.go +++ b/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run_unix.go @@ -20,6 +20,7 @@ package run import ( gocontext "context" + "path/filepath" "strings" "github.com/containerd/containerd" @@ -34,10 +35,14 @@ import ( // NewContainer creates a new container func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) { var ( - ref = context.Args().First() - id = context.Args().Get(1) - args = context.Args()[2:] + id string + config = context.IsSet("config") ) + if config { + id = context.Args().First() + } else { + id = context.Args().Get(1) + } if raw := context.String("checkpoint"); raw != "" { im, err := client.GetImage(ctx, raw) @@ -53,78 +58,89 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli spec containerd.NewContainerOpts ) - if context.IsSet("config") { + if config { opts = append(opts, oci.WithSpecFromFile(context.String("config"))) } else { - opts = append(opts, oci.WithDefaultSpec()) - } + var ( + ref = context.Args().First() + //for container's id is Args[1] + args = context.Args()[2:] + ) + opts = append(opts, oci.WithDefaultSpec(), oci.WithDefaultUnixDevices) + opts = append(opts, oci.WithEnv(context.StringSlice("env"))) + opts = append(opts, withMounts(context)) - opts = append(opts, oci.WithEnv(context.StringSlice("env"))) - opts = append(opts, withMounts(context)) - cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) - cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil)) - if context.Bool("rootfs") { - opts = append(opts, oci.WithRootFSPath(ref)) - } else { - snapshotter := context.String("snapshotter") - image, err := client.GetImage(ctx, ref) - if err != nil { - return nil, err - } - unpacked, err := image.IsUnpacked(ctx, snapshotter) - if err != nil { - return nil, err - } - if !unpacked { - if err := image.Unpack(ctx, snapshotter); err != nil { + if context.Bool("rootfs") { + rootfs, err := filepath.Abs(ref) + if err != nil { return nil, err } + opts = append(opts, oci.WithRootFSPath(rootfs)) + } else { + snapshotter := context.String("snapshotter") + image, err := client.GetImage(ctx, ref) + if err != nil { + return nil, err + } + unpacked, err := image.IsUnpacked(ctx, snapshotter) + if err != nil { + return nil, err + } + if !unpacked { + if err := image.Unpack(ctx, snapshotter); err != nil { + return nil, err + } + } + opts = append(opts, oci.WithImageConfig(image)) + cOpts = append(cOpts, + containerd.WithImage(image), + containerd.WithSnapshotter(snapshotter), + // Even when "readonly" is set, we don't use KindView snapshot here. (#1495) + // We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only, + // after creating some mount points on demand. + containerd.WithNewSnapshot(id, image)) } - opts = append(opts, oci.WithImageConfig(image)) - cOpts = append(cOpts, - containerd.WithImage(image), - containerd.WithSnapshotter(snapshotter), - // Even when "readonly" is set, we don't use KindView snapshot here. (#1495) - // We pass writable snapshot to the OCI runtime, and the runtime remounts it as read-only, - // after creating some mount points on demand. - containerd.WithNewSnapshot(id, image)) - } - if context.Bool("readonly") { - opts = append(opts, oci.WithRootFSReadonly()) - } - if len(args) > 0 { - opts = append(opts, oci.WithProcessArgs(args...)) - } - if cwd := context.String("cwd"); cwd != "" { - opts = append(opts, oci.WithProcessCwd(cwd)) - } - if context.Bool("tty") { - opts = append(opts, oci.WithTTY) - } - if context.Bool("privileged") { - opts = append(opts, oci.WithPrivileged) - } - if context.Bool("net-host") { - opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostHostsFile, oci.WithHostResolvconf) - } - joinNs := context.StringSlice("with-ns") - for _, ns := range joinNs { - parts := strings.Split(ns, ":") - if len(parts) != 2 { - return nil, errors.New("joining a Linux namespace using --with-ns requires the format 'nstype:path'") + if context.Bool("readonly") { + opts = append(opts, oci.WithRootFSReadonly()) } - if !validNamespace(parts[0]) { - return nil, errors.New("the Linux namespace type specified in --with-ns is not valid: " + parts[0]) + if len(args) > 0 { + opts = append(opts, oci.WithProcessArgs(args...)) + } + if cwd := context.String("cwd"); cwd != "" { + opts = append(opts, oci.WithProcessCwd(cwd)) + } + if context.Bool("tty") { + opts = append(opts, oci.WithTTY) + } + if context.Bool("privileged") { + opts = append(opts, oci.WithPrivileged) + } + if context.Bool("net-host") { + opts = append(opts, oci.WithHostNamespace(specs.NetworkNamespace), oci.WithHostHostsFile, oci.WithHostResolvconf) + } + + joinNs := context.StringSlice("with-ns") + for _, ns := range joinNs { + parts := strings.Split(ns, ":") + if len(parts) != 2 { + return nil, errors.New("joining a Linux namespace using --with-ns requires the format 'nstype:path'") + } + if !validNamespace(parts[0]) { + return nil, errors.New("the Linux namespace type specified in --with-ns is not valid: " + parts[0]) + } + opts = append(opts, oci.WithLinuxNamespace(specs.LinuxNamespace{ + Type: specs.LinuxNamespaceType(parts[0]), + Path: parts[1], + })) + } + if context.IsSet("gpus") { + opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.Int("gpus")), nvidia.WithAllCapabilities)) } - opts = append(opts, oci.WithLinuxNamespace(specs.LinuxNamespace{ - Type: specs.LinuxNamespaceType(parts[0]), - Path: parts[1], - })) - } - if context.IsSet("gpus") { - opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.Int("gpus")), nvidia.WithAllCapabilities)) } + cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) + cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil)) + var s specs.Spec spec = containerd.WithSpec(&s, opts...) diff --git a/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run_windows.go b/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run_windows.go index d80d3b065..00d3d7578 100644 --- a/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run_windows.go +++ b/vendor/github.com/containerd/containerd/cmd/ctr/commands/run/run_windows.go @@ -22,29 +22,12 @@ import ( "github.com/containerd/console" "github.com/containerd/containerd" "github.com/containerd/containerd/cmd/ctr/commands" - "github.com/containerd/containerd/containers" "github.com/containerd/containerd/oci" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) -func withTTY(terminal bool) oci.SpecOpts { - if !terminal { - return func(ctx gocontext.Context, client oci.Client, c *containers.Container, s *specs.Spec) error { - s.Process.Terminal = false - return nil - } - } - - con := console.Current() - size, err := con.Size() - if err != nil { - logrus.WithError(err).Error("console size") - } - return oci.WithTTY(int(size.Width), int(size.Height)) -} - // NewContainer creates a new container func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) { var ( @@ -73,7 +56,17 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli opts = append(opts, oci.WithImageConfig(image)) opts = append(opts, oci.WithEnv(context.StringSlice("env"))) opts = append(opts, withMounts(context)) - opts = append(opts, withTTY(context.Bool("tty"))) + if context.Bool("tty") { + opts = append(opts, oci.WithTTY) + + con := console.Current() + size, err := con.Size() + if err != nil { + logrus.WithError(err).Error("console size") + } + opts = append(opts, oci.WithTTYSize(int(size.Width), int(size.Height))) + } + if len(args) > 0 { opts = append(opts, oci.WithProcessArgs(args...)) } diff --git a/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/start.go b/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/start.go index 63694a2e1..0774d5784 100644 --- a/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/start.go +++ b/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/start.go @@ -18,6 +18,7 @@ package tasks import ( "github.com/containerd/console" + "github.com/containerd/containerd" "github.com/containerd/containerd/cio" "github.com/containerd/containerd/cmd/ctr/commands" "github.com/pkg/errors" @@ -42,11 +43,16 @@ var startCommand = cli.Command{ Name: "pid-file", Usage: "file path to write the task's pid", }, + cli.BoolFlag{ + Name: "detach,d", + Usage: "detach from the task after it has started execution", + }, }, Action: func(context *cli.Context) error { var ( - err error - id = context.Args().Get(0) + err error + id = context.Args().Get(0) + detach = context.Bool("detach") ) if id == "" { return errors.New("container id must be provided") @@ -65,27 +71,11 @@ var startCommand = cli.Command{ if err != nil { return err } - var ( tty = spec.Process.Terminal opts = getNewTaskOpts(context) ioOpts = []cio.Opt{cio.WithFIFODir(context.String("fifo-dir"))} ) - task, err := NewTask(ctx, client, container, "", tty, context.Bool("null-io"), ioOpts, opts...) - if err != nil { - return err - } - defer task.Delete(ctx) - if context.IsSet("pid-file") { - if err := commands.WritePidFile(context.String("pid-file"), int(task.Pid())); err != nil { - return err - } - } - statusC, err := task.Wait(ctx) - if err != nil { - return err - } - var con console.Console if tty { con = console.Current() @@ -94,9 +84,30 @@ var startCommand = cli.Command{ return err } } + + task, err := NewTask(ctx, client, container, "", con, context.Bool("null-io"), ioOpts, opts...) + if err != nil { + return err + } + var statusC <-chan containerd.ExitStatus + if !detach { + defer task.Delete(ctx) + if statusC, err = task.Wait(ctx); err != nil { + return err + } + } + if context.IsSet("pid-file") { + if err := commands.WritePidFile(context.String("pid-file"), int(task.Pid())); err != nil { + return err + } + } + if err := task.Start(ctx); err != nil { return err } + if detach { + return nil + } if tty { if err := HandleConsoleResize(ctx, task, con); err != nil { logrus.WithError(err).Error("console resize") diff --git a/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/tasks_unix.go b/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/tasks_unix.go index f7b111410..e10fe798d 100644 --- a/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/tasks_unix.go +++ b/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/tasks_unix.go @@ -67,7 +67,7 @@ func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Consol } // NewTask creates a new task -func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, tty, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) { +func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, checkpoint string, con console.Console, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) { stdio := cio.NewCreator(append([]cio.Opt{cio.WithStdio}, ioOpts...)...) if checkpoint != "" { im, err := client.GetImage(ctx, checkpoint) @@ -77,11 +77,11 @@ func NewTask(ctx gocontext.Context, client *containerd.Client, container contain opts = append(opts, containerd.WithTaskCheckpoint(im)) } ioCreator := stdio - if tty { - ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio, cio.WithTerminal}, ioOpts...)...) + if con != nil { + ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStreams(con, con, nil), cio.WithTerminal}, ioOpts...)...) } if nullIO { - if tty { + if con != nil { return nil, errors.New("tty and null-io cannot be used together") } ioCreator = cio.NullIO diff --git a/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/tasks_windows.go b/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/tasks_windows.go index 5235467fc..f6ec5563a 100644 --- a/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/tasks_windows.go +++ b/vendor/github.com/containerd/containerd/cmd/ctr/commands/tasks/tasks_windows.go @@ -58,13 +58,13 @@ func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Consol } // NewTask creates a new task -func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, tty, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) { +func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, con console.Console, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) { var ioCreator cio.Creator - if tty { + if con != nil { if nullIO { return nil, errors.New("tty and null-io cannot be used together") } - ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio, cio.WithTerminal}, ioOpts...)...) + ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStreams(con, con, con), cio.WithTerminal}, ioOpts...)...) } else if nullIO { ioCreator = cio.NullIO } else { diff --git a/vendor/github.com/containerd/containerd/container_opts_unix.go b/vendor/github.com/containerd/containerd/container_opts_unix.go index a4935b2b4..c0622f67f 100644 --- a/vendor/github.com/containerd/containerd/container_opts_unix.go +++ b/vendor/github.com/containerd/containerd/container_opts_unix.go @@ -20,25 +20,21 @@ package containerd import ( "context" - "encoding/json" "fmt" "os" "path/filepath" "syscall" - "github.com/containerd/containerd/api/types" "github.com/containerd/containerd/containers" "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/platforms" - "github.com/containerd/containerd/runtime/linux/runctypes" "github.com/gogo/protobuf/proto" protobuf "github.com/gogo/protobuf/types" "github.com/opencontainers/image-spec/identity" "github.com/opencontainers/image-spec/specs-go/v1" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -105,44 +101,6 @@ func WithCheckpoint(im Image, snapshotKey string) NewContainerOpts { } } -// WithTaskCheckpoint allows a task to be created with live runtime and memory data from a -// previous checkpoint. Additional software such as CRIU may be required to -// restore a task from a checkpoint -func WithTaskCheckpoint(im Image) NewTaskOpts { - return func(ctx context.Context, c *Client, info *TaskInfo) error { - desc := im.Target() - id := desc.Digest - index, err := decodeIndex(ctx, c.ContentStore(), desc) - if err != nil { - return err - } - for _, m := range index.Manifests { - if m.MediaType == images.MediaTypeContainerd1Checkpoint { - info.Checkpoint = &types.Descriptor{ - MediaType: m.MediaType, - Size_: m.Size, - Digest: m.Digest, - } - return nil - } - } - return fmt.Errorf("checkpoint not found in index %s", id) - } -} - -func decodeIndex(ctx context.Context, store content.Provider, desc ocispec.Descriptor) (*v1.Index, error) { - var index v1.Index - p, err := content.ReadBlob(ctx, store, desc) - if err != nil { - return nil, err - } - if err := json.Unmarshal(p, &index); err != nil { - return nil, err - } - - return &index, nil -} - // WithRemappedSnapshot creates a new snapshot and remaps the uid/gid for the // filesystem to be used by a container with user namespaces func WithRemappedSnapshot(id string, i Image, uid, gid uint32) NewContainerOpts { @@ -221,19 +179,3 @@ func incrementFS(root string, uidInc, gidInc uint32) filepath.WalkFunc { return os.Lchown(path, u, g) } } - -// WithNoPivotRoot instructs the runtime not to you pivot_root -func WithNoPivotRoot(_ context.Context, _ *Client, info *TaskInfo) error { - if info.Options == nil { - info.Options = &runctypes.CreateOptions{ - NoPivotRoot: true, - } - return nil - } - copts, ok := info.Options.(*runctypes.CreateOptions) - if !ok { - return errors.New("invalid options type, expected runctypes.CreateOptions") - } - copts.NoPivotRoot = true - return nil -} diff --git a/vendor/github.com/containerd/containerd/containers/containers.go b/vendor/github.com/containerd/containerd/containers/containers.go index c624164e8..e6a562730 100644 --- a/vendor/github.com/containerd/containerd/containers/containers.go +++ b/vendor/github.com/containerd/containerd/containers/containers.go @@ -40,7 +40,7 @@ type Container struct { // Image specifies the image reference used for a container. // - // This property is optional but immutable. + // This property is optional and mutable. Image string // Runtime specifies which runtime should be used when launching container @@ -60,7 +60,7 @@ type Container struct { // look up the mounts from the snapshot service and include those on the // task create request. // - // This field is not required but immutable. + // This field is not required but mutable. SnapshotKey string // Snapshotter specifies the snapshotter name used for rootfs diff --git a/vendor/github.com/containerd/containerd/content/local/store.go b/vendor/github.com/containerd/containerd/content/local/store.go index 6df3df618..7fa9bb736 100644 --- a/vendor/github.com/containerd/containerd/content/local/store.go +++ b/vendor/github.com/containerd/containerd/content/local/store.go @@ -33,6 +33,8 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/filters" "github.com/containerd/containerd/log" + + "github.com/containerd/continuity" digest "github.com/opencontainers/go-digest" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" @@ -651,5 +653,5 @@ func writeTimestampFile(p string, t time.Time) error { return err } - return ioutil.WriteFile(p, b, 0666) + return continuity.AtomicWriteFile(p, b, 0666) } diff --git a/vendor/github.com/containerd/containerd/contrib/nvidia/nvidia.go b/vendor/github.com/containerd/containerd/contrib/nvidia/nvidia.go index bf64d3014..e82a51e39 100644 --- a/vendor/github.com/containerd/containerd/contrib/nvidia/nvidia.go +++ b/vendor/github.com/containerd/containerd/contrib/nvidia/nvidia.go @@ -32,7 +32,7 @@ import ( const nvidiaCLI = "nvidia-container-cli" // Capability specifies capabilities for the gpu inside the container -// Detailed explaination of options can be found: +// Detailed explanation of options can be found: // https://github.com/nvidia/nvidia-container-runtime#supported-driver-capabilities type Capability string diff --git a/vendor/github.com/containerd/containerd/contrib/seccomp/seccomp.go b/vendor/github.com/containerd/containerd/contrib/seccomp/seccomp.go index 2a1806cf8..4619681f4 100644 --- a/vendor/github.com/containerd/containerd/contrib/seccomp/seccomp.go +++ b/vendor/github.com/containerd/containerd/contrib/seccomp/seccomp.go @@ -30,7 +30,7 @@ import ( ) // WithProfile receives the name of a file stored on disk comprising a json -// formated seccomp profile, as specified by the opencontainers/runtime-spec. +// formatted seccomp profile, as specified by the opencontainers/runtime-spec. // The profile is read from the file, unmarshaled, and set to the spec. func WithProfile(profile string) oci.SpecOpts { return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { diff --git a/vendor/github.com/containerd/containerd/events/exchange/exchange.go b/vendor/github.com/containerd/containerd/events/exchange/exchange.go index 51c760f04..95d21b7df 100644 --- a/vendor/github.com/containerd/containerd/events/exchange/exchange.go +++ b/vendor/github.com/containerd/containerd/events/exchange/exchange.go @@ -52,7 +52,7 @@ var _ events.Subscriber = &Exchange{} // Forward accepts an envelope to be direcly distributed on the exchange. // -// This is useful when an event is forwaded on behalf of another namespace or +// This is useful when an event is forwarded on behalf of another namespace or // when the event is propagated on behalf of another publisher. func (e *Exchange) Forward(ctx context.Context, envelope *events.Envelope) (err error) { if err := validateEnvelope(envelope); err != nil { diff --git a/vendor/github.com/containerd/containerd/export.go b/vendor/github.com/containerd/containerd/export.go new file mode 100644 index 000000000..7aac309ba --- /dev/null +++ b/vendor/github.com/containerd/containerd/export.go @@ -0,0 +1,57 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package containerd + +import ( + "context" + "io" + + "github.com/containerd/containerd/images" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +type exportOpts struct { +} + +// ExportOpt allows the caller to specify export-specific options +type ExportOpt func(c *exportOpts) error + +func resolveExportOpt(opts ...ExportOpt) (exportOpts, error) { + var eopts exportOpts + for _, o := range opts { + if err := o(&eopts); err != nil { + return eopts, err + } + } + return eopts, nil +} + +// Export exports an image to a Tar stream. +// OCI format is used by default. +// It is up to caller to put "org.opencontainers.image.ref.name" annotation to desc. +// TODO(AkihiroSuda): support exporting multiple descriptors at once to a single archive stream. +func (c *Client) Export(ctx context.Context, exporter images.Exporter, desc ocispec.Descriptor, opts ...ExportOpt) (io.ReadCloser, error) { + _, err := resolveExportOpt(opts...) // unused now + if err != nil { + return nil, err + } + pr, pw := io.Pipe() + go func() { + pw.CloseWithError(exporter.Export(ctx, c.ContentStore(), desc, pw)) + }() + return pr, nil +} diff --git a/vendor/github.com/containerd/containerd/image.go b/vendor/github.com/containerd/containerd/image.go index 6e286fcaf..f12cd59c0 100644 --- a/vendor/github.com/containerd/containerd/image.go +++ b/vendor/github.com/containerd/containerd/image.go @@ -63,7 +63,7 @@ func NewImage(client *Client, i images.Image) Image { } // NewImageWithPlatform returns a client image object from the metadata image -func NewImageWithPlatform(client *Client, i images.Image, platform string) Image { +func NewImageWithPlatform(client *Client, i images.Image, platform platforms.MatchComparer) Image { return &image{ client: client, i: i, @@ -75,7 +75,7 @@ type image struct { client *Client i images.Image - platform string + platform platforms.MatchComparer } func (i *image) Name() string { @@ -186,7 +186,7 @@ func (i *image) Unpack(ctx context.Context, snapshotterName string) error { return nil } -func (i *image) getLayers(ctx context.Context, platform string) ([]rootfs.Layer, error) { +func (i *image) getLayers(ctx context.Context, platform platforms.MatchComparer) ([]rootfs.Layer, error) { cs := i.client.ContentStore() manifest, err := images.Manifest(ctx, cs, i.i.Target, platform) diff --git a/vendor/github.com/containerd/containerd/images/handlers.go b/vendor/github.com/containerd/containerd/images/handlers.go index d313b32d4..230a9caf8 100644 --- a/vendor/github.com/containerd/containerd/images/handlers.go +++ b/vendor/github.com/containerd/containerd/images/handlers.go @@ -19,6 +19,7 @@ package images import ( "context" "fmt" + "sort" "github.com/containerd/containerd/content" "github.com/containerd/containerd/platforms" @@ -183,8 +184,8 @@ func SetChildrenLabels(manager content.Manager, f HandlerFunc) HandlerFunc { } // FilterPlatforms is a handler wrapper which limits the descriptors returned -// by a handler to the specified platforms. -func FilterPlatforms(f HandlerFunc, platformList ...string) HandlerFunc { +// based on matching the specified platform matcher. +func FilterPlatforms(f HandlerFunc, m platforms.Matcher) HandlerFunc { return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { children, err := f(ctx, desc) if err != nil { @@ -193,20 +194,12 @@ func FilterPlatforms(f HandlerFunc, platformList ...string) HandlerFunc { var descs []ocispec.Descriptor - if len(platformList) == 0 { + if m == nil { descs = children } else { - for _, platform := range platformList { - p, err := platforms.Parse(platform) - if err != nil { - return nil, err - } - matcher := platforms.NewMatcher(p) - - for _, d := range children { - if d.Platform == nil || matcher.Match(*d.Platform) { - descs = append(descs, d) - } + for _, d := range children { + if d.Platform == nil || m.Match(*d.Platform) { + descs = append(descs, d) } } } @@ -214,3 +207,37 @@ func FilterPlatforms(f HandlerFunc, platformList ...string) HandlerFunc { return descs, nil } } + +// LimitManifests is a handler wrapper which filters the manifest descriptors +// returned using the provided platform. +// The results will be ordered according to the comparison operator and +// use the ordering in the manifests for equal matches. +// A limit of 0 or less is considered no limit. +func LimitManifests(f HandlerFunc, m platforms.MatchComparer, n int) HandlerFunc { + return func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { + children, err := f(ctx, desc) + if err != nil { + return children, err + } + + switch desc.MediaType { + case ocispec.MediaTypeImageIndex, MediaTypeDockerSchema2ManifestList: + sort.SliceStable(children, func(i, j int) bool { + if children[i].Platform == nil { + return false + } + if children[j].Platform == nil { + return true + } + return m.Less(*children[i].Platform, *children[j].Platform) + }) + + if n > 0 && len(children) > n { + children = children[:n] + } + default: + // only limit manifests from an index + } + return children, nil + } +} diff --git a/vendor/github.com/containerd/containerd/images/image.go b/vendor/github.com/containerd/containerd/images/image.go index a96597c26..4d6979d7a 100644 --- a/vendor/github.com/containerd/containerd/images/image.go +++ b/vendor/github.com/containerd/containerd/images/image.go @@ -19,6 +19,7 @@ package images import ( "context" "encoding/json" + "sort" "strings" "time" @@ -93,7 +94,7 @@ type Store interface { // // The caller can then use the descriptor to resolve and process the // configuration of the image. -func (image *Image) Config(ctx context.Context, provider content.Provider, platform string) (ocispec.Descriptor, error) { +func (image *Image) Config(ctx context.Context, provider content.Provider, platform platforms.MatchComparer) (ocispec.Descriptor, error) { return Config(ctx, provider, image.Target, platform) } @@ -101,7 +102,7 @@ func (image *Image) Config(ctx context.Context, provider content.Provider, platf // // These are used to verify that a set of layers unpacked to the expected // values. -func (image *Image) RootFS(ctx context.Context, provider content.Provider, platform string) ([]digest.Digest, error) { +func (image *Image) RootFS(ctx context.Context, provider content.Provider, platform platforms.MatchComparer) ([]digest.Digest, error) { desc, err := image.Config(ctx, provider, platform) if err != nil { return nil, err @@ -110,7 +111,7 @@ func (image *Image) RootFS(ctx context.Context, provider content.Provider, platf } // Size returns the total size of an image's packed resources. -func (image *Image) Size(ctx context.Context, provider content.Provider, platform string) (int64, error) { +func (image *Image) Size(ctx context.Context, provider content.Provider, platform platforms.MatchComparer) (int64, error) { var size int64 return size, Walk(ctx, Handlers(HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { if desc.Size < 0 { @@ -121,27 +122,22 @@ func (image *Image) Size(ctx context.Context, provider content.Provider, platfor }), FilterPlatforms(ChildrenHandler(provider), platform)), image.Target) } +type platformManifest struct { + p *ocispec.Platform + m *ocispec.Manifest +} + // Manifest resolves a manifest from the image for the given platform. // // TODO(stevvooe): This violates the current platform agnostic approach to this // package by returning a specific manifest type. We'll need to refactor this // to return a manifest descriptor or decide that we want to bring the API in // this direction because this abstraction is not needed.` -func Manifest(ctx context.Context, provider content.Provider, image ocispec.Descriptor, platform string) (ocispec.Manifest, error) { +func Manifest(ctx context.Context, provider content.Provider, image ocispec.Descriptor, platform platforms.MatchComparer) (ocispec.Manifest, error) { var ( - matcher platforms.Matcher - m *ocispec.Manifest - p ocispec.Platform + m []platformManifest wasIndex bool ) - if platform != "" { - var err error - p, err = platforms.Parse(platform) - if err != nil { - return ocispec.Manifest{}, err - } - matcher = platforms.NewMatcher(p) - } if err := Walk(ctx, HandlerFunc(func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) { switch desc.MediaType { @@ -156,8 +152,8 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc return nil, err } - if platform != "" { - if desc.Platform != nil && !matcher.Match(*desc.Platform) { + if platform != nil { + if desc.Platform != nil && !platform.Match(*desc.Platform) { return nil, nil } @@ -172,14 +168,17 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc return nil, err } - if !matcher.Match(platforms.Normalize(ocispec.Platform{OS: image.OS, Architecture: image.Architecture})) { + if !platform.Match(platforms.Normalize(ocispec.Platform{OS: image.OS, Architecture: image.Architecture})) { return nil, nil } } } - m = &manifest + m = append(m, platformManifest{ + p: desc.Platform, + m: &manifest, + }) return nil, nil case MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex: @@ -193,13 +192,13 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc return nil, err } - if platform == "" { + if platform == nil { return idx.Manifests, nil } var descs []ocispec.Descriptor for _, d := range idx.Manifests { - if d.Platform == nil || matcher.Match(*d.Platform) { + if d.Platform == nil || platform.Match(*d.Platform) { descs = append(descs, d) } } @@ -214,15 +213,25 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc return ocispec.Manifest{}, err } - if m == nil { + if len(m) == 0 { err := errors.Wrapf(errdefs.ErrNotFound, "manifest %v", image.Digest) if wasIndex { - err = errors.Wrapf(errdefs.ErrNotFound, "no match for current platform %s in manifest %v", platforms.Format(p), image.Digest) + err = errors.Wrapf(errdefs.ErrNotFound, "no match for platform in manifest %v", image.Digest) } return ocispec.Manifest{}, err } - return *m, nil + sort.SliceStable(m, func(i, j int) bool { + if m[i].p == nil { + return false + } + if m[j].p == nil { + return true + } + return platform.Less(*m[i].p, *m[j].p) + }) + + return *m[0].m, nil } // Config resolves the image configuration descriptor using a content provided @@ -230,7 +239,7 @@ func Manifest(ctx context.Context, provider content.Provider, image ocispec.Desc // // The caller can then use the descriptor to resolve and process the // configuration of the image. -func Config(ctx context.Context, provider content.Provider, image ocispec.Descriptor, platform string) (ocispec.Descriptor, error) { +func Config(ctx context.Context, provider content.Provider, image ocispec.Descriptor, platform platforms.MatchComparer) (ocispec.Descriptor, error) { manifest, err := Manifest(ctx, provider, image, platform) if err != nil { return ocispec.Descriptor{}, err @@ -276,7 +285,7 @@ func Platforms(ctx context.Context, provider content.Provider, image ocispec.Des // in the provider. // // If there is a problem resolving content, an error will be returned. -func Check(ctx context.Context, provider content.Provider, image ocispec.Descriptor, platform string) (available bool, required, present, missing []ocispec.Descriptor, err error) { +func Check(ctx context.Context, provider content.Provider, image ocispec.Descriptor, platform platforms.MatchComparer) (available bool, required, present, missing []ocispec.Descriptor, err error) { mfst, err := Manifest(ctx, provider, image, platform) if err != nil { if errdefs.IsNotFound(err) { diff --git a/vendor/github.com/containerd/containerd/import.go b/vendor/github.com/containerd/containerd/import.go index e4ac00cee..7a69f1d45 100644 --- a/vendor/github.com/containerd/containerd/import.go +++ b/vendor/github.com/containerd/containerd/import.go @@ -22,7 +22,6 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) type importOpts struct { @@ -84,35 +83,3 @@ func (c *Client) Import(ctx context.Context, importer images.Importer, reader io } return images, nil } - -type exportOpts struct { -} - -// ExportOpt allows the caller to specify export-specific options -type ExportOpt func(c *exportOpts) error - -func resolveExportOpt(opts ...ExportOpt) (exportOpts, error) { - var eopts exportOpts - for _, o := range opts { - if err := o(&eopts); err != nil { - return eopts, err - } - } - return eopts, nil -} - -// Export exports an image to a Tar stream. -// OCI format is used by default. -// It is up to caller to put "org.opencontainers.image.ref.name" annotation to desc. -// TODO(AkihiroSuda): support exporting multiple descriptors at once to a single archive stream. -func (c *Client) Export(ctx context.Context, exporter images.Exporter, desc ocispec.Descriptor, opts ...ExportOpt) (io.ReadCloser, error) { - _, err := resolveExportOpt(opts...) // unused now - if err != nil { - return nil, err - } - pr, pw := io.Pipe() - go func() { - pw.CloseWithError(exporter.Export(ctx, c.ContentStore(), desc, pw)) - }() - return pr, nil -} diff --git a/vendor/github.com/containerd/containerd/install.go b/vendor/github.com/containerd/containerd/install.go index 2aa8b0394..5e4c6a2c8 100644 --- a/vendor/github.com/containerd/containerd/install.go +++ b/vendor/github.com/containerd/containerd/install.go @@ -33,25 +33,14 @@ import ( // Install a binary image into the opt service func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) error { - resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{ - Filters: []string{ - "id==opt", - }, - }) - if err != nil { - return err - } - if len(resp.Plugins) != 1 { - return errors.New("opt service not enabled") - } - path := resp.Plugins[0].Exports["path"] - if path == "" { - return errors.New("opt path not exported") - } var config InstallConfig for _, o := range opts { o(&config) } + path, err := c.getInstallPath(ctx, config) + if err != nil { + return err + } var ( cs = image.ContentStore() platform = platforms.Default() @@ -89,3 +78,25 @@ func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) } return nil } + +func (c *Client) getInstallPath(ctx context.Context, config InstallConfig) (string, error) { + if config.Path != "" { + return config.Path, nil + } + resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{ + Filters: []string{ + "id==opt", + }, + }) + if err != nil { + return "", err + } + if len(resp.Plugins) != 1 { + return "", errors.New("opt service not enabled") + } + path := resp.Plugins[0].Exports["path"] + if path == "" { + return "", errors.New("opt path not exported") + } + return path, nil +} diff --git a/vendor/github.com/containerd/containerd/install_opts.go b/vendor/github.com/containerd/containerd/install_opts.go index b11e7f3d6..b0c9213cb 100644 --- a/vendor/github.com/containerd/containerd/install_opts.go +++ b/vendor/github.com/containerd/containerd/install_opts.go @@ -25,6 +25,8 @@ type InstallConfig struct { Libs bool // Replace will overwrite existing binaries or libs in the opt directory Replace bool + // Path to install libs and binaries to + Path string } // WithInstallLibs installs libs from the image @@ -36,3 +38,10 @@ func WithInstallLibs(c *InstallConfig) { func WithInstallReplace(c *InstallConfig) { c.Replace = true } + +// WithInstallPath sets the optional install path +func WithInstallPath(path string) InstallOpts { + return func(c *InstallConfig) { + c.Path = path + } +} diff --git a/vendor/github.com/containerd/containerd/metadata/buckets.go b/vendor/github.com/containerd/containerd/metadata/buckets.go index fcf4c2959..d0e1600d1 100644 --- a/vendor/github.com/containerd/containerd/metadata/buckets.go +++ b/vendor/github.com/containerd/containerd/metadata/buckets.go @@ -21,7 +21,7 @@ import ( digest "github.com/opencontainers/go-digest" ) -// The layout where a "/" delineates a bucket is desribed in the following +// The layout where a "/" delineates a bucket is described in the following // section. Please try to follow this as closely as possible when adding // functionality. We can bolster this with helpers and more structure if that // becomes an issue. diff --git a/vendor/github.com/containerd/containerd/mount/mount_linux.go b/vendor/github.com/containerd/containerd/mount/mount_linux.go index 82fc0b279..b5a16148a 100644 --- a/vendor/github.com/containerd/containerd/mount/mount_linux.go +++ b/vendor/github.com/containerd/containerd/mount/mount_linux.go @@ -17,16 +17,41 @@ package mount import ( + "fmt" + "os" + "path" "strings" "time" + "github.com/containerd/containerd/sys" "github.com/pkg/errors" "golang.org/x/sys/unix" ) +var pagesize = 4096 + +func init() { + pagesize = os.Getpagesize() +} + // Mount to the provided target path func (m *Mount) Mount(target string) error { - flags, data := parseMountOptions(m.Options) + var ( + chdir string + options = m.Options + ) + + // avoid hitting one page limit of mount argument buffer + // + // NOTE: 512 is a buffer during pagesize check. + if m.Type == "overlay" && optionsSize(options) >= pagesize-512 { + chdir, options = compactLowerdirOption(options) + } + + flags, data := parseMountOptions(options) + if len(data) > pagesize { + return errors.Errorf("mount options is too long") + } // propagation types. const ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE @@ -38,7 +63,7 @@ func (m *Mount) Mount(target string) error { if flags&unix.MS_REMOUNT == 0 || data != "" { // Initial call applying all non-propagation flags for mount // or remount with changed data - if err := unix.Mount(m.Source, target, m.Type, uintptr(oflags), data); err != nil { + if err := mountAt(chdir, m.Source, target, m.Type, uintptr(oflags), data); err != nil { return err } } @@ -155,3 +180,129 @@ func parseMountOptions(options []string) (int, string) { } return flag, strings.Join(data, ",") } + +// compactLowerdirOption updates overlay lowdir option and returns the common +// dir among all the lowdirs. +func compactLowerdirOption(opts []string) (string, []string) { + idx, dirs := findOverlayLowerdirs(opts) + if idx == -1 || len(dirs) == 1 { + // no need to compact if there is only one lowerdir + return "", opts + } + + // find out common dir + commondir := longestCommonPrefix(dirs) + if commondir == "" { + return "", opts + } + + // NOTE: the snapshot id is based on digits. + // in order to avoid to get snapshots/x, should be back to parent dir. + // however, there is assumption that the common dir is ${root}/io.containerd.v1.overlayfs/snapshots. + commondir = path.Dir(commondir) + if commondir == "/" { + return "", opts + } + commondir = commondir + "/" + + newdirs := make([]string, 0, len(dirs)) + for _, dir := range dirs { + newdirs = append(newdirs, dir[len(commondir):]) + } + + newopts := copyOptions(opts) + newopts = append(newopts[:idx], newopts[idx+1:]...) + newopts = append(newopts, fmt.Sprintf("lowerdir=%s", strings.Join(newdirs, ":"))) + return commondir, newopts +} + +// findOverlayLowerdirs returns the index of lowerdir in mount's options and +// all the lowerdir target. +func findOverlayLowerdirs(opts []string) (int, []string) { + var ( + idx = -1 + prefix = "lowerdir=" + ) + + for i, opt := range opts { + if strings.HasPrefix(opt, prefix) { + idx = i + break + } + } + + if idx == -1 { + return -1, nil + } + return idx, strings.Split(opts[idx][len(prefix):], ":") +} + +// longestCommonPrefix finds the longest common prefix in the string slice. +func longestCommonPrefix(strs []string) string { + if len(strs) == 0 { + return "" + } else if len(strs) == 1 { + return strs[0] + } + + // find out the min/max value by alphabetical order + min, max := strs[0], strs[0] + for _, str := range strs[1:] { + if min > str { + min = str + } + if max < str { + max = str + } + } + + // find out the common part between min and max + for i := 0; i < len(min) && i < len(max); i++ { + if min[i] != max[i] { + return min[:i] + } + } + return min +} + +// copyOptions copies the options. +func copyOptions(opts []string) []string { + if len(opts) == 0 { + return nil + } + + acopy := make([]string, len(opts)) + copy(acopy, opts) + return acopy +} + +// optionsSize returns the byte size of options of mount. +func optionsSize(opts []string) int { + size := 0 + for _, opt := range opts { + size += len(opt) + } + return size +} + +func mountAt(chdir string, source, target, fstype string, flags uintptr, data string) error { + if chdir == "" { + return unix.Mount(source, target, fstype, flags, data) + } + + f, err := os.Open(chdir) + if err != nil { + return errors.Wrap(err, "failed to mountat") + } + defer f.Close() + + fs, err := f.Stat() + if err != nil { + return errors.Wrap(err, "failed to mountat") + } + + if !fs.IsDir() { + return errors.Wrap(errors.Errorf("%s is not dir", chdir), "failed to mountat") + } + return errors.Wrap(sys.FMountat(f.Fd(), source, target, fstype, flags, data), "failed to mountat") +} diff --git a/vendor/github.com/containerd/containerd/mount/mount_windows.go b/vendor/github.com/containerd/containerd/mount/mount_windows.go index f7c97894b..5de25c4e0 100644 --- a/vendor/github.com/containerd/containerd/mount/mount_windows.go +++ b/vendor/github.com/containerd/containerd/mount/mount_windows.go @@ -32,6 +32,10 @@ var ( // Mount to the provided target func (m *Mount) Mount(target string) error { + if m.Type != "windows-layer" { + return errors.Errorf("invalid windows mount type: '%s'", m.Type) + } + home, layerID := filepath.Split(m.Source) parentLayerPaths, err := m.GetParentPaths() diff --git a/vendor/github.com/containerd/containerd/oci/spec.go b/vendor/github.com/containerd/containerd/oci/spec.go index ffd0bffca..6fb31e454 100644 --- a/vendor/github.com/containerd/containerd/oci/spec.go +++ b/vendor/github.com/containerd/containerd/oci/spec.go @@ -18,11 +18,27 @@ package oci import ( "context" + "path/filepath" + "runtime" + + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/containers" specs "github.com/opencontainers/runtime-spec/specs-go" ) +const ( + rwm = "rwm" + defaultRootfsPath = "rootfs" +) + +var ( + defaultUnixEnv = []string{ + "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + } +) + // Spec is a type alias to the OCI runtime spec to allow third part SpecOpts // to be created without the "issues" with go vendoring and package imports type Spec = specs.Spec @@ -30,12 +46,36 @@ type Spec = specs.Spec // GenerateSpec will generate a default spec from the provided image // for use as a containerd container func GenerateSpec(ctx context.Context, client Client, c *containers.Container, opts ...SpecOpts) (*Spec, error) { - s, err := createDefaultSpec(ctx, c.ID) - if err != nil { + return GenerateSpecWithPlatform(ctx, client, platforms.DefaultString(), c, opts...) +} + +// GenerateSpecWithPlatform will generate a default spec from the provided image +// for use as a containerd container in the platform requested. +func GenerateSpecWithPlatform(ctx context.Context, client Client, platform string, c *containers.Container, opts ...SpecOpts) (*Spec, error) { + var s Spec + if err := generateDefaultSpecWithPlatform(ctx, platform, c.ID, &s); err != nil { return nil, err } - return s, ApplyOpts(ctx, client, c, s, opts...) + return &s, ApplyOpts(ctx, client, c, &s, opts...) +} + +func generateDefaultSpecWithPlatform(ctx context.Context, platform, id string, s *Spec) error { + plat, err := platforms.Parse(platform) + if err != nil { + return err + } + + if plat.OS == "windows" { + err = populateDefaultWindowsSpec(ctx, s, id) + } else { + err = populateDefaultUnixSpec(ctx, s, id) + if err == nil && runtime.GOOS == "windows" { + // To run LCOW we have a Linux and Windows section. Add an empty one now. + s.Windows = &specs.Windows{} + } + } + return err } // ApplyOpts applys the options to the given spec, injecting data from the @@ -50,7 +90,173 @@ func ApplyOpts(ctx context.Context, client Client, c *containers.Container, s *S return nil } -func createDefaultSpec(ctx context.Context, id string) (*Spec, error) { - var s Spec - return &s, populateDefaultSpec(ctx, &s, id) +func defaultUnixCaps() []string { + return []string{ + "CAP_CHOWN", + "CAP_DAC_OVERRIDE", + "CAP_FSETID", + "CAP_FOWNER", + "CAP_MKNOD", + "CAP_NET_RAW", + "CAP_SETGID", + "CAP_SETUID", + "CAP_SETFCAP", + "CAP_SETPCAP", + "CAP_NET_BIND_SERVICE", + "CAP_SYS_CHROOT", + "CAP_KILL", + "CAP_AUDIT_WRITE", + } +} + +func defaultUnixNamespaces() []specs.LinuxNamespace { + return []specs.LinuxNamespace{ + { + Type: specs.PIDNamespace, + }, + { + Type: specs.IPCNamespace, + }, + { + Type: specs.UTSNamespace, + }, + { + Type: specs.MountNamespace, + }, + { + Type: specs.NetworkNamespace, + }, + } +} + +func populateDefaultUnixSpec(ctx context.Context, s *Spec, id string) error { + ns, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return err + } + + *s = Spec{ + Version: specs.Version, + Root: &specs.Root{ + Path: defaultRootfsPath, + }, + Process: &specs.Process{ + Env: defaultUnixEnv, + Cwd: "/", + NoNewPrivileges: true, + User: specs.User{ + UID: 0, + GID: 0, + }, + Capabilities: &specs.LinuxCapabilities{ + Bounding: defaultUnixCaps(), + Permitted: defaultUnixCaps(), + Inheritable: defaultUnixCaps(), + Effective: defaultUnixCaps(), + }, + Rlimits: []specs.POSIXRlimit{ + { + Type: "RLIMIT_NOFILE", + Hard: uint64(1024), + Soft: uint64(1024), + }, + }, + }, + Mounts: []specs.Mount{ + { + Destination: "/proc", + Type: "proc", + Source: "proc", + }, + { + Destination: "/dev", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, + }, + { + Destination: "/dev/pts", + Type: "devpts", + Source: "devpts", + Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, + }, + { + Destination: "/dev/shm", + Type: "tmpfs", + Source: "shm", + Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, + }, + { + Destination: "/dev/mqueue", + Type: "mqueue", + Source: "mqueue", + Options: []string{"nosuid", "noexec", "nodev"}, + }, + { + Destination: "/sys", + Type: "sysfs", + Source: "sysfs", + Options: []string{"nosuid", "noexec", "nodev", "ro"}, + }, + { + Destination: "/run", + Type: "tmpfs", + Source: "tmpfs", + Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, + }, + }, + Linux: &specs.Linux{ + MaskedPaths: []string{ + "/proc/acpi", + "/proc/kcore", + "/proc/keys", + "/proc/latency_stats", + "/proc/timer_list", + "/proc/timer_stats", + "/proc/sched_debug", + "/sys/firmware", + "/proc/scsi", + }, + ReadonlyPaths: []string{ + "/proc/asound", + "/proc/bus", + "/proc/fs", + "/proc/irq", + "/proc/sys", + "/proc/sysrq-trigger", + }, + CgroupsPath: filepath.Join("/", ns, id), + Resources: &specs.LinuxResources{ + Devices: []specs.LinuxDeviceCgroup{ + { + Allow: false, + Access: rwm, + }, + }, + }, + Namespaces: defaultUnixNamespaces(), + }, + } + return nil +} + +func populateDefaultWindowsSpec(ctx context.Context, s *Spec, id string) error { + *s = Spec{ + Version: specs.Version, + Root: &specs.Root{}, + Process: &specs.Process{ + Cwd: `C:\`, + ConsoleSize: &specs.Box{ + Width: 80, + Height: 20, + }, + }, + Windows: &specs.Windows{ + IgnoreFlushesDuringBoot: true, + Network: &specs.WindowsNetwork{ + AllowUnqualifiedDNSQuery: true, + }, + }, + } + return nil } diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts.go b/vendor/github.com/containerd/containerd/oci/spec_opts.go index fd2cfb039..d7fe4a29f 100644 --- a/vendor/github.com/containerd/containerd/oci/spec_opts.go +++ b/vendor/github.com/containerd/containerd/oci/spec_opts.go @@ -19,12 +19,25 @@ package oci import ( "context" "encoding/json" + "fmt" "io/ioutil" + "os" + "path/filepath" + "strconv" "strings" "github.com/containerd/containerd/containers" + "github.com/containerd/containerd/content" + "github.com/containerd/containerd/images" + "github.com/containerd/containerd/mount" + "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/platforms" + "github.com/containerd/continuity/fs" + "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/opencontainers/runc/libcontainer/user" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" + "github.com/syndtr/gocapability/capability" ) // SpecOpts sets spec specific information to a newly generated OCI spec @@ -49,13 +62,45 @@ func setProcess(s *Spec) { } } +// setRoot sets Root to empty if unset +func setRoot(s *Spec) { + if s.Root == nil { + s.Root = &specs.Root{} + } +} + +// setLinux sets Linux to empty if unset +func setLinux(s *Spec) { + if s.Linux == nil { + s.Linux = &specs.Linux{} + } +} + +// setCapabilities sets Linux Capabilities to empty if unset +func setCapabilities(s *Spec) { + setProcess(s) + if s.Process.Capabilities == nil { + s.Process.Capabilities = &specs.LinuxCapabilities{} + } +} + // WithDefaultSpec returns a SpecOpts that will populate the spec with default // values. // // Use as the first option to clear the spec, then apply options afterwards. func WithDefaultSpec() SpecOpts { return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { - return populateDefaultSpec(ctx, s, c.ID) + return generateDefaultSpecWithPlatform(ctx, platforms.DefaultString(), c.ID, s) + } +} + +// WithDefaultSpecForPlatform returns a SpecOpts that will populate the spec +// with default values for a given platform. +// +// Use as the first option to clear the spec, then apply options afterwards. +func WithDefaultSpecForPlatform(platform string) SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + return generateDefaultSpecWithPlatform(ctx, platform, c.ID, s) } } @@ -81,32 +126,6 @@ func WithSpecFromFile(filename string) SpecOpts { } } -// WithProcessArgs replaces the args on the generated spec -func WithProcessArgs(args ...string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.Args = args - return nil - } -} - -// WithProcessCwd replaces the current working directory on the generated spec -func WithProcessCwd(cwd string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.Cwd = cwd - return nil - } -} - -// WithHostname sets the container's hostname -func WithHostname(name string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - s.Hostname = name - return nil - } -} - // WithEnv appends environment variables func WithEnv(environmentVariables []string) SpecOpts { return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { @@ -118,14 +137,6 @@ func WithEnv(environmentVariables []string) SpecOpts { } } -// WithMounts appends mounts -func WithMounts(mounts []specs.Mount) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - s.Mounts = append(s.Mounts, mounts...) - return nil - } -} - // replaceOrAppendEnvValues returns the defaults with the overrides either // replaced by env key or appended to the list func replaceOrAppendEnvValues(defaults, overrides []string) []string { @@ -163,3 +174,826 @@ func replaceOrAppendEnvValues(defaults, overrides []string) []string { return defaults } + +// WithProcessArgs replaces the args on the generated spec +func WithProcessArgs(args ...string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.Args = args + return nil + } +} + +// WithProcessCwd replaces the current working directory on the generated spec +func WithProcessCwd(cwd string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.Cwd = cwd + return nil + } +} + +// WithTTY sets the information on the spec as well as the environment variables for +// using a TTY +func WithTTY(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.Terminal = true + if s.Linux != nil { + s.Process.Env = append(s.Process.Env, "TERM=xterm") + } + + return nil +} + +// WithTTYSize sets the information on the spec as well as the environment variables for +// using a TTY +func WithTTYSize(width, height int) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + if s.Process.ConsoleSize == nil { + s.Process.ConsoleSize = &specs.Box{} + } + s.Process.ConsoleSize.Width = uint(width) + s.Process.ConsoleSize.Height = uint(height) + return nil + } +} + +// WithHostname sets the container's hostname +func WithHostname(name string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Hostname = name + return nil + } +} + +// WithMounts appends mounts +func WithMounts(mounts []specs.Mount) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Mounts = append(s.Mounts, mounts...) + return nil + } +} + +// WithHostNamespace allows a task to run inside the host's linux namespace +func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + for i, n := range s.Linux.Namespaces { + if n.Type == ns { + s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...) + return nil + } + } + return nil + } +} + +// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the +// spec, the existing namespace is replaced by the one provided. +func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + for i, n := range s.Linux.Namespaces { + if n.Type == ns.Type { + before := s.Linux.Namespaces[:i] + after := s.Linux.Namespaces[i+1:] + s.Linux.Namespaces = append(before, ns) + s.Linux.Namespaces = append(s.Linux.Namespaces, after...) + return nil + } + } + s.Linux.Namespaces = append(s.Linux.Namespaces, ns) + return nil + } +} + +// WithImageConfig configures the spec to from the configuration of an Image +func WithImageConfig(image Image) SpecOpts { + return WithImageConfigArgs(image, nil) +} + +// WithImageConfigArgs configures the spec to from the configuration of an Image with additional args that +// replaces the CMD of the image +func WithImageConfigArgs(image Image, args []string) SpecOpts { + return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { + ic, err := image.Config(ctx) + if err != nil { + return err + } + var ( + ociimage v1.Image + config v1.ImageConfig + ) + switch ic.MediaType { + case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: + p, err := content.ReadBlob(ctx, image.ContentStore(), ic) + if err != nil { + return err + } + + if err := json.Unmarshal(p, &ociimage); err != nil { + return err + } + config = ociimage.Config + default: + return fmt.Errorf("unknown image config media type %s", ic.MediaType) + } + + setProcess(s) + if s.Linux != nil { + s.Process.Env = append(s.Process.Env, config.Env...) + cmd := config.Cmd + if len(args) > 0 { + cmd = args + } + s.Process.Args = append(config.Entrypoint, cmd...) + + cwd := config.WorkingDir + if cwd == "" { + cwd = "/" + } + s.Process.Cwd = cwd + if config.User != "" { + return WithUser(config.User)(ctx, client, c, s) + } + } else if s.Windows != nil { + s.Process.Env = config.Env + s.Process.Args = append(config.Entrypoint, config.Cmd...) + s.Process.User = specs.User{ + Username: config.User, + } + } else { + return errors.New("spec does not contain Linux or Windows section") + } + return nil + } +} + +// WithRootFSPath specifies unmanaged rootfs path. +func WithRootFSPath(path string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setRoot(s) + s.Root.Path = path + // Entrypoint is not set here (it's up to caller) + return nil + } +} + +// WithRootFSReadonly sets specs.Root.Readonly to true +func WithRootFSReadonly() SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setRoot(s) + s.Root.Readonly = true + return nil + } +} + +// WithNoNewPrivileges sets no_new_privileges on the process for the container +func WithNoNewPrivileges(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.NoNewPrivileges = true + return nil +} + +// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly +func WithHostHostsFile(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Mounts = append(s.Mounts, specs.Mount{ + Destination: "/etc/hosts", + Type: "bind", + Source: "/etc/hosts", + Options: []string{"rbind", "ro"}, + }) + return nil +} + +// WithHostResolvconf bind-mounts the host's /etc/resolv.conf into the container as readonly +func WithHostResolvconf(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Mounts = append(s.Mounts, specs.Mount{ + Destination: "/etc/resolv.conf", + Type: "bind", + Source: "/etc/resolv.conf", + Options: []string{"rbind", "ro"}, + }) + return nil +} + +// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly +func WithHostLocaltime(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + s.Mounts = append(s.Mounts, specs.Mount{ + Destination: "/etc/localtime", + Type: "bind", + Source: "/etc/localtime", + Options: []string{"rbind", "ro"}, + }) + return nil +} + +// WithUserNamespace sets the uid and gid mappings for the task +// this can be called multiple times to add more mappings to the generated spec +func WithUserNamespace(container, host, size uint32) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + var hasUserns bool + setLinux(s) + for _, ns := range s.Linux.Namespaces { + if ns.Type == specs.UserNamespace { + hasUserns = true + break + } + } + if !hasUserns { + s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{ + Type: specs.UserNamespace, + }) + } + mapping := specs.LinuxIDMapping{ + ContainerID: container, + HostID: host, + Size: size, + } + s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping) + s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping) + return nil + } +} + +// WithCgroup sets the container's cgroup path +func WithCgroup(path string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + s.Linux.CgroupsPath = path + return nil + } +} + +// WithNamespacedCgroup uses the namespace set on the context to create a +// root directory for containers in the cgroup with the id as the subcgroup +func WithNamespacedCgroup() SpecOpts { + return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { + namespace, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return err + } + setLinux(s) + s.Linux.CgroupsPath = filepath.Join("/", namespace, c.ID) + return nil + } +} + +// WithUser sets the user to be used within the container. +// It accepts a valid user string in OCI Image Spec v1.0.0: +// user, uid, user:group, uid:gid, uid:group, user:gid +func WithUser(userstr string) SpecOpts { + return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { + setProcess(s) + parts := strings.Split(userstr, ":") + switch len(parts) { + case 1: + v, err := strconv.Atoi(parts[0]) + if err != nil { + // if we cannot parse as a uint they try to see if it is a username + return WithUsername(userstr)(ctx, client, c, s) + } + return WithUserID(uint32(v))(ctx, client, c, s) + case 2: + var ( + username string + groupname string + ) + var uid, gid uint32 + v, err := strconv.Atoi(parts[0]) + if err != nil { + username = parts[0] + } else { + uid = uint32(v) + } + if v, err = strconv.Atoi(parts[1]); err != nil { + groupname = parts[1] + } else { + gid = uint32(v) + } + if username == "" && groupname == "" { + s.Process.User.UID, s.Process.User.GID = uid, gid + return nil + } + f := func(root string) error { + if username != "" { + user, err := getUserFromPath(root, func(u user.User) bool { + return u.Name == username + }) + if err != nil { + return err + } + uid = uint32(user.Uid) + } + if groupname != "" { + gid, err = getGIDFromPath(root, func(g user.Group) bool { + return g.Name == groupname + }) + if err != nil { + return err + } + } + s.Process.User.UID, s.Process.User.GID = uid, gid + return nil + } + if c.Snapshotter == "" && c.SnapshotKey == "" { + if !isRootfsAbs(s.Root.Path) { + return errors.New("rootfs absolute path is required") + } + return f(s.Root.Path) + } + if c.Snapshotter == "" { + return errors.New("no snapshotter set for container") + } + if c.SnapshotKey == "" { + return errors.New("rootfs snapshot not created for container") + } + snapshotter := client.SnapshotService(c.Snapshotter) + mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) + if err != nil { + return err + } + return mount.WithTempMount(ctx, mounts, f) + default: + return fmt.Errorf("invalid USER value %s", userstr) + } + } +} + +// WithUIDGID allows the UID and GID for the Process to be set +func WithUIDGID(uid, gid uint32) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.User.UID = uid + s.Process.User.GID = gid + return nil + } +} + +// WithUserID sets the correct UID and GID for the container based +// on the image's /etc/passwd contents. If /etc/passwd does not exist, +// or uid is not found in /etc/passwd, it sets the requested uid, +// additionally sets the gid to 0, and does not return an error. +func WithUserID(uid uint32) SpecOpts { + return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { + setProcess(s) + if c.Snapshotter == "" && c.SnapshotKey == "" { + if !isRootfsAbs(s.Root.Path) { + return errors.Errorf("rootfs absolute path is required") + } + user, err := getUserFromPath(s.Root.Path, func(u user.User) bool { + return u.Uid == int(uid) + }) + if err != nil { + if os.IsNotExist(err) || err == errNoUsersFound { + s.Process.User.UID, s.Process.User.GID = uid, 0 + return nil + } + return err + } + s.Process.User.UID, s.Process.User.GID = uint32(user.Uid), uint32(user.Gid) + return nil + + } + if c.Snapshotter == "" { + return errors.Errorf("no snapshotter set for container") + } + if c.SnapshotKey == "" { + return errors.Errorf("rootfs snapshot not created for container") + } + snapshotter := client.SnapshotService(c.Snapshotter) + mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) + if err != nil { + return err + } + return mount.WithTempMount(ctx, mounts, func(root string) error { + user, err := getUserFromPath(root, func(u user.User) bool { + return u.Uid == int(uid) + }) + if err != nil { + if os.IsNotExist(err) || err == errNoUsersFound { + s.Process.User.UID, s.Process.User.GID = uid, 0 + return nil + } + return err + } + s.Process.User.UID, s.Process.User.GID = uint32(user.Uid), uint32(user.Gid) + return nil + }) + } +} + +// WithUsername sets the correct UID and GID for the container +// based on the the image's /etc/passwd contents. If /etc/passwd +// does not exist, or the username is not found in /etc/passwd, +// it returns error. +func WithUsername(username string) SpecOpts { + return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { + setProcess(s) + if s.Linux != nil { + if c.Snapshotter == "" && c.SnapshotKey == "" { + if !isRootfsAbs(s.Root.Path) { + return errors.Errorf("rootfs absolute path is required") + } + user, err := getUserFromPath(s.Root.Path, func(u user.User) bool { + return u.Name == username + }) + if err != nil { + return err + } + s.Process.User.UID, s.Process.User.GID = uint32(user.Uid), uint32(user.Gid) + return nil + } + if c.Snapshotter == "" { + return errors.Errorf("no snapshotter set for container") + } + if c.SnapshotKey == "" { + return errors.Errorf("rootfs snapshot not created for container") + } + snapshotter := client.SnapshotService(c.Snapshotter) + mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) + if err != nil { + return err + } + return mount.WithTempMount(ctx, mounts, func(root string) error { + user, err := getUserFromPath(root, func(u user.User) bool { + return u.Name == username + }) + if err != nil { + return err + } + s.Process.User.UID, s.Process.User.GID = uint32(user.Uid), uint32(user.Gid) + return nil + }) + } else if s.Windows != nil { + s.Process.User.Username = username + } else { + return errors.New("spec does not contain Linux or Windows section") + } + return nil + } +} + +// WithAdditionalGIDs sets the OCI spec's additionalGids array to any additional groups listed +// for a particular user in the /etc/groups file of the image's root filesystem +// The passed in user can be either a uid or a username. +func WithAdditionalGIDs(userstr string) SpecOpts { + return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { + setProcess(s) + setAdditionalGids := func(root string) error { + var username string + uid, err := strconv.Atoi(userstr) + if err == nil { + user, err := getUserFromPath(root, func(u user.User) bool { + return u.Uid == uid + }) + if err != nil { + if os.IsNotExist(err) || err == errNoUsersFound { + return nil + } + return err + } + username = user.Name + } else { + username = userstr + } + gids, err := getSupplementalGroupsFromPath(root, func(g user.Group) bool { + // we only want supplemental groups + if g.Name == username { + return false + } + for _, entry := range g.List { + if entry == username { + return true + } + } + return false + }) + if err != nil { + if os.IsNotExist(err) { + return nil + } + return err + } + s.Process.User.AdditionalGids = gids + return nil + } + if c.Snapshotter == "" && c.SnapshotKey == "" { + if !isRootfsAbs(s.Root.Path) { + return errors.Errorf("rootfs absolute path is required") + } + return setAdditionalGids(s.Root.Path) + } + if c.Snapshotter == "" { + return errors.Errorf("no snapshotter set for container") + } + if c.SnapshotKey == "" { + return errors.Errorf("rootfs snapshot not created for container") + } + snapshotter := client.SnapshotService(c.Snapshotter) + mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) + if err != nil { + return err + } + return mount.WithTempMount(ctx, mounts, setAdditionalGids) + } +} + +// WithCapabilities sets Linux capabilities on the process +func WithCapabilities(caps []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setCapabilities(s) + + s.Process.Capabilities.Bounding = caps + s.Process.Capabilities.Effective = caps + s.Process.Capabilities.Permitted = caps + s.Process.Capabilities.Inheritable = caps + + return nil + } +} + +// WithAllCapabilities sets all linux capabilities for the process +var WithAllCapabilities = WithCapabilities(getAllCapabilities()) + +func getAllCapabilities() []string { + last := capability.CAP_LAST_CAP + // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap + if last == capability.Cap(63) { + last = capability.CAP_BLOCK_SUSPEND + } + var caps []string + for _, cap := range capability.List() { + if cap > last { + continue + } + caps = append(caps, "CAP_"+strings.ToUpper(cap.String())) + } + return caps +} + +// WithAmbientCapabilities set the Linux ambient capabilities for the process +// Ambient capabilities should only be set for non-root users or the caller should +// understand how these capabilities are used and set +func WithAmbientCapabilities(caps []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setCapabilities(s) + + s.Process.Capabilities.Ambient = caps + return nil + } +} + +var errNoUsersFound = errors.New("no users found") + +func getUserFromPath(root string, filter func(user.User) bool) (user.User, error) { + ppath, err := fs.RootPath(root, "/etc/passwd") + if err != nil { + return user.User{}, err + } + users, err := user.ParsePasswdFileFilter(ppath, filter) + if err != nil { + return user.User{}, err + } + if len(users) == 0 { + return user.User{}, errNoUsersFound + } + return users[0], nil +} + +var errNoGroupsFound = errors.New("no groups found") + +func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) { + gpath, err := fs.RootPath(root, "/etc/group") + if err != nil { + return 0, err + } + groups, err := user.ParseGroupFileFilter(gpath, filter) + if err != nil { + return 0, err + } + if len(groups) == 0 { + return 0, errNoGroupsFound + } + g := groups[0] + return uint32(g.Gid), nil +} + +func getSupplementalGroupsFromPath(root string, filter func(user.Group) bool) ([]uint32, error) { + gpath, err := fs.RootPath(root, "/etc/group") + if err != nil { + return []uint32{}, err + } + groups, err := user.ParseGroupFileFilter(gpath, filter) + if err != nil { + return []uint32{}, err + } + if len(groups) == 0 { + // if there are no additional groups; just return an empty set + return []uint32{}, nil + } + addlGids := []uint32{} + for _, grp := range groups { + addlGids = append(addlGids, uint32(grp.Gid)) + } + return addlGids, nil +} + +func isRootfsAbs(root string) bool { + return filepath.IsAbs(root) +} + +// WithMaskedPaths sets the masked paths option +func WithMaskedPaths(paths []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + s.Linux.MaskedPaths = paths + return nil + } +} + +// WithReadonlyPaths sets the read only paths option +func WithReadonlyPaths(paths []string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + s.Linux.ReadonlyPaths = paths + return nil + } +} + +// WithWriteableSysfs makes any sysfs mounts writeable +func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + for i, m := range s.Mounts { + if m.Type == "sysfs" { + var options []string + for _, o := range m.Options { + if o == "ro" { + o = "rw" + } + options = append(options, o) + } + s.Mounts[i].Options = options + } + } + return nil +} + +// WithWriteableCgroupfs makes any cgroup mounts writeable +func WithWriteableCgroupfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + for i, m := range s.Mounts { + if m.Type == "cgroup" { + var options []string + for _, o := range m.Options { + if o == "ro" { + o = "rw" + } + options = append(options, o) + } + s.Mounts[i].Options = options + } + } + return nil +} + +// WithSelinuxLabel sets the process SELinux label +func WithSelinuxLabel(label string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.SelinuxLabel = label + return nil + } +} + +// WithApparmorProfile sets the Apparmor profile for the process +func WithApparmorProfile(profile string) SpecOpts { + return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setProcess(s) + s.Process.ApparmorProfile = profile + return nil + } +} + +// WithSeccompUnconfined clears the seccomp profile +func WithSeccompUnconfined(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + s.Linux.Seccomp = nil + return nil +} + +// WithParentCgroupDevices uses the default cgroup setup to inherit the container's parent cgroup's +// allowed and denied devices +func WithParentCgroupDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + if s.Linux.Resources == nil { + s.Linux.Resources = &specs.LinuxResources{} + } + s.Linux.Resources.Devices = nil + return nil +} + +// WithDefaultUnixDevices adds the default devices for unix such as /dev/null, /dev/random to +// the container's resource cgroup spec +func WithDefaultUnixDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { + setLinux(s) + if s.Linux.Resources == nil { + s.Linux.Resources = &specs.LinuxResources{} + } + intptr := func(i int64) *int64 { + return &i + } + s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, []specs.LinuxDeviceCgroup{ + { + // "/dev/null", + Type: "c", + Major: intptr(1), + Minor: intptr(3), + Access: rwm, + Allow: true, + }, + { + // "/dev/random", + Type: "c", + Major: intptr(1), + Minor: intptr(8), + Access: rwm, + Allow: true, + }, + { + // "/dev/full", + Type: "c", + Major: intptr(1), + Minor: intptr(7), + Access: rwm, + Allow: true, + }, + { + // "/dev/tty", + Type: "c", + Major: intptr(5), + Minor: intptr(0), + Access: rwm, + Allow: true, + }, + { + // "/dev/zero", + Type: "c", + Major: intptr(1), + Minor: intptr(5), + Access: rwm, + Allow: true, + }, + { + // "/dev/urandom", + Type: "c", + Major: intptr(1), + Minor: intptr(9), + Access: rwm, + Allow: true, + }, + { + // "/dev/console", + Type: "c", + Major: intptr(5), + Minor: intptr(1), + Access: rwm, + Allow: true, + }, + // /dev/pts/ - pts namespaces are "coming soon" + { + Type: "c", + Major: intptr(136), + Access: rwm, + Allow: true, + }, + { + Type: "c", + Major: intptr(5), + Minor: intptr(2), + Access: rwm, + Allow: true, + }, + { + // tuntap + Type: "c", + Major: intptr(10), + Minor: intptr(200), + Access: rwm, + Allow: true, + }, + }...) + return nil +} + +// WithPrivileged sets up options for a privileged container +// TODO(justincormack) device handling +var WithPrivileged = Compose( + WithAllCapabilities, + WithMaskedPaths(nil), + WithReadonlyPaths(nil), + WithWriteableSysfs, + WithWriteableCgroupfs, + WithSelinuxLabel(""), + WithApparmorProfile(""), + WithSeccompUnconfined, +) diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go b/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go deleted file mode 100644 index 5a6c66301..000000000 --- a/vendor/github.com/containerd/containerd/oci/spec_opts_unix.go +++ /dev/null @@ -1,616 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package oci - -import ( - "context" - "encoding/json" - "fmt" - "os" - "path/filepath" - "strconv" - "strings" - - "github.com/containerd/containerd/containers" - "github.com/containerd/containerd/content" - "github.com/containerd/containerd/images" - "github.com/containerd/containerd/mount" - "github.com/containerd/containerd/namespaces" - "github.com/containerd/continuity/fs" - "github.com/opencontainers/image-spec/specs-go/v1" - "github.com/opencontainers/runc/libcontainer/user" - specs "github.com/opencontainers/runtime-spec/specs-go" - "github.com/pkg/errors" - "github.com/syndtr/gocapability/capability" -) - -// WithTTY sets the information on the spec as well as the environment variables for -// using a TTY -func WithTTY(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.Terminal = true - s.Process.Env = append(s.Process.Env, "TERM=xterm") - return nil -} - -// setRoot sets Root to empty if unset -func setRoot(s *Spec) { - if s.Root == nil { - s.Root = &specs.Root{} - } -} - -// setLinux sets Linux to empty if unset -func setLinux(s *Spec) { - if s.Linux == nil { - s.Linux = &specs.Linux{} - } -} - -// setCapabilities sets Linux Capabilities to empty if unset -func setCapabilities(s *Spec) { - setProcess(s) - if s.Process.Capabilities == nil { - s.Process.Capabilities = &specs.LinuxCapabilities{} - } -} - -// WithHostNamespace allows a task to run inside the host's linux namespace -func WithHostNamespace(ns specs.LinuxNamespaceType) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - for i, n := range s.Linux.Namespaces { - if n.Type == ns { - s.Linux.Namespaces = append(s.Linux.Namespaces[:i], s.Linux.Namespaces[i+1:]...) - return nil - } - } - return nil - } -} - -// WithLinuxNamespace uses the passed in namespace for the spec. If a namespace of the same type already exists in the -// spec, the existing namespace is replaced by the one provided. -func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - for i, n := range s.Linux.Namespaces { - if n.Type == ns.Type { - before := s.Linux.Namespaces[:i] - after := s.Linux.Namespaces[i+1:] - s.Linux.Namespaces = append(before, ns) - s.Linux.Namespaces = append(s.Linux.Namespaces, after...) - return nil - } - } - s.Linux.Namespaces = append(s.Linux.Namespaces, ns) - return nil - } -} - -// WithImageConfig configures the spec to from the configuration of an Image -func WithImageConfig(image Image) SpecOpts { - return WithImageConfigArgs(image, nil) -} - -// WithImageConfigArgs configures the spec to from the configuration of an Image with additional args that -// replaces the CMD of the image -func WithImageConfigArgs(image Image, args []string) SpecOpts { - return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { - ic, err := image.Config(ctx) - if err != nil { - return err - } - var ( - ociimage v1.Image - config v1.ImageConfig - ) - switch ic.MediaType { - case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: - p, err := content.ReadBlob(ctx, image.ContentStore(), ic) - if err != nil { - return err - } - - if err := json.Unmarshal(p, &ociimage); err != nil { - return err - } - config = ociimage.Config - default: - return fmt.Errorf("unknown image config media type %s", ic.MediaType) - } - - setProcess(s) - s.Process.Env = append(s.Process.Env, config.Env...) - cmd := config.Cmd - if len(args) > 0 { - cmd = args - } - s.Process.Args = append(config.Entrypoint, cmd...) - cwd := config.WorkingDir - if cwd == "" { - cwd = "/" - } - s.Process.Cwd = cwd - if config.User != "" { - return WithUser(config.User)(ctx, client, c, s) - } - return nil - } -} - -// WithRootFSPath specifies unmanaged rootfs path. -func WithRootFSPath(path string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setRoot(s) - s.Root.Path = path - // Entrypoint is not set here (it's up to caller) - return nil - } -} - -// WithRootFSReadonly sets specs.Root.Readonly to true -func WithRootFSReadonly() SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setRoot(s) - s.Root.Readonly = true - return nil - } -} - -// WithNoNewPrivileges sets no_new_privileges on the process for the container -func WithNoNewPrivileges(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.NoNewPrivileges = true - return nil -} - -// WithHostHostsFile bind-mounts the host's /etc/hosts into the container as readonly -func WithHostHostsFile(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - s.Mounts = append(s.Mounts, specs.Mount{ - Destination: "/etc/hosts", - Type: "bind", - Source: "/etc/hosts", - Options: []string{"rbind", "ro"}, - }) - return nil -} - -// WithHostResolvconf bind-mounts the host's /etc/resolv.conf into the container as readonly -func WithHostResolvconf(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - s.Mounts = append(s.Mounts, specs.Mount{ - Destination: "/etc/resolv.conf", - Type: "bind", - Source: "/etc/resolv.conf", - Options: []string{"rbind", "ro"}, - }) - return nil -} - -// WithHostLocaltime bind-mounts the host's /etc/localtime into the container as readonly -func WithHostLocaltime(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - s.Mounts = append(s.Mounts, specs.Mount{ - Destination: "/etc/localtime", - Type: "bind", - Source: "/etc/localtime", - Options: []string{"rbind", "ro"}, - }) - return nil -} - -// WithUserNamespace sets the uid and gid mappings for the task -// this can be called multiple times to add more mappings to the generated spec -func WithUserNamespace(container, host, size uint32) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - var hasUserns bool - setLinux(s) - for _, ns := range s.Linux.Namespaces { - if ns.Type == specs.UserNamespace { - hasUserns = true - break - } - } - if !hasUserns { - s.Linux.Namespaces = append(s.Linux.Namespaces, specs.LinuxNamespace{ - Type: specs.UserNamespace, - }) - } - mapping := specs.LinuxIDMapping{ - ContainerID: container, - HostID: host, - Size: size, - } - s.Linux.UIDMappings = append(s.Linux.UIDMappings, mapping) - s.Linux.GIDMappings = append(s.Linux.GIDMappings, mapping) - return nil - } -} - -// WithCgroup sets the container's cgroup path -func WithCgroup(path string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - s.Linux.CgroupsPath = path - return nil - } -} - -// WithNamespacedCgroup uses the namespace set on the context to create a -// root directory for containers in the cgroup with the id as the subcgroup -func WithNamespacedCgroup() SpecOpts { - return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error { - namespace, err := namespaces.NamespaceRequired(ctx) - if err != nil { - return err - } - setLinux(s) - s.Linux.CgroupsPath = filepath.Join("/", namespace, c.ID) - return nil - } -} - -// WithUser sets the user to be used within the container. -// It accepts a valid user string in OCI Image Spec v1.0.0: -// user, uid, user:group, uid:gid, uid:group, user:gid -func WithUser(userstr string) SpecOpts { - return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { - setProcess(s) - parts := strings.Split(userstr, ":") - switch len(parts) { - case 1: - v, err := strconv.Atoi(parts[0]) - if err != nil { - // if we cannot parse as a uint they try to see if it is a username - return WithUsername(userstr)(ctx, client, c, s) - } - return WithUserID(uint32(v))(ctx, client, c, s) - case 2: - var ( - username string - groupname string - ) - var uid, gid uint32 - v, err := strconv.Atoi(parts[0]) - if err != nil { - username = parts[0] - } else { - uid = uint32(v) - } - if v, err = strconv.Atoi(parts[1]); err != nil { - groupname = parts[1] - } else { - gid = uint32(v) - } - if username == "" && groupname == "" { - s.Process.User.UID, s.Process.User.GID = uid, gid - return nil - } - f := func(root string) error { - if username != "" { - uid, _, err = getUIDGIDFromPath(root, func(u user.User) bool { - return u.Name == username - }) - if err != nil { - return err - } - } - if groupname != "" { - gid, err = getGIDFromPath(root, func(g user.Group) bool { - return g.Name == groupname - }) - if err != nil { - return err - } - } - s.Process.User.UID, s.Process.User.GID = uid, gid - return nil - } - if c.Snapshotter == "" && c.SnapshotKey == "" { - if !isRootfsAbs(s.Root.Path) { - return errors.New("rootfs absolute path is required") - } - return f(s.Root.Path) - } - if c.Snapshotter == "" { - return errors.New("no snapshotter set for container") - } - if c.SnapshotKey == "" { - return errors.New("rootfs snapshot not created for container") - } - snapshotter := client.SnapshotService(c.Snapshotter) - mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) - if err != nil { - return err - } - return mount.WithTempMount(ctx, mounts, f) - default: - return fmt.Errorf("invalid USER value %s", userstr) - } - } -} - -// WithUIDGID allows the UID and GID for the Process to be set -func WithUIDGID(uid, gid uint32) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.User.UID = uid - s.Process.User.GID = gid - return nil - } -} - -// WithUserID sets the correct UID and GID for the container based -// on the image's /etc/passwd contents. If /etc/passwd does not exist, -// or uid is not found in /etc/passwd, it sets the requested uid, -// additionally sets the gid to 0, and does not return an error. -func WithUserID(uid uint32) SpecOpts { - return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { - setProcess(s) - if c.Snapshotter == "" && c.SnapshotKey == "" { - if !isRootfsAbs(s.Root.Path) { - return errors.Errorf("rootfs absolute path is required") - } - uuid, ugid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool { - return u.Uid == int(uid) - }) - if err != nil { - if os.IsNotExist(err) || err == errNoUsersFound { - s.Process.User.UID, s.Process.User.GID = uid, 0 - return nil - } - return err - } - s.Process.User.UID, s.Process.User.GID = uuid, ugid - return nil - - } - if c.Snapshotter == "" { - return errors.Errorf("no snapshotter set for container") - } - if c.SnapshotKey == "" { - return errors.Errorf("rootfs snapshot not created for container") - } - snapshotter := client.SnapshotService(c.Snapshotter) - mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) - if err != nil { - return err - } - return mount.WithTempMount(ctx, mounts, func(root string) error { - uuid, ugid, err := getUIDGIDFromPath(root, func(u user.User) bool { - return u.Uid == int(uid) - }) - if err != nil { - if os.IsNotExist(err) || err == errNoUsersFound { - s.Process.User.UID, s.Process.User.GID = uid, 0 - return nil - } - return err - } - s.Process.User.UID, s.Process.User.GID = uuid, ugid - return nil - }) - } -} - -// WithUsername sets the correct UID and GID for the container -// based on the the image's /etc/passwd contents. If /etc/passwd -// does not exist, or the username is not found in /etc/passwd, -// it returns error. -func WithUsername(username string) SpecOpts { - return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { - setProcess(s) - if c.Snapshotter == "" && c.SnapshotKey == "" { - if !isRootfsAbs(s.Root.Path) { - return errors.Errorf("rootfs absolute path is required") - } - uid, gid, err := getUIDGIDFromPath(s.Root.Path, func(u user.User) bool { - return u.Name == username - }) - if err != nil { - return err - } - s.Process.User.UID, s.Process.User.GID = uid, gid - return nil - } - if c.Snapshotter == "" { - return errors.Errorf("no snapshotter set for container") - } - if c.SnapshotKey == "" { - return errors.Errorf("rootfs snapshot not created for container") - } - snapshotter := client.SnapshotService(c.Snapshotter) - mounts, err := snapshotter.Mounts(ctx, c.SnapshotKey) - if err != nil { - return err - } - return mount.WithTempMount(ctx, mounts, func(root string) error { - uid, gid, err := getUIDGIDFromPath(root, func(u user.User) bool { - return u.Name == username - }) - if err != nil { - return err - } - s.Process.User.UID, s.Process.User.GID = uid, gid - return nil - }) - } -} - -// WithCapabilities sets Linux capabilities on the process -func WithCapabilities(caps []string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setCapabilities(s) - - s.Process.Capabilities.Bounding = caps - s.Process.Capabilities.Effective = caps - s.Process.Capabilities.Permitted = caps - s.Process.Capabilities.Inheritable = caps - - return nil - } -} - -// WithAllCapabilities sets all linux capabilities for the process -var WithAllCapabilities = WithCapabilities(getAllCapabilities()) - -func getAllCapabilities() []string { - last := capability.CAP_LAST_CAP - // hack for RHEL6 which has no /proc/sys/kernel/cap_last_cap - if last == capability.Cap(63) { - last = capability.CAP_BLOCK_SUSPEND - } - var caps []string - for _, cap := range capability.List() { - if cap > last { - continue - } - caps = append(caps, "CAP_"+strings.ToUpper(cap.String())) - } - return caps -} - -var errNoUsersFound = errors.New("no users found") - -func getUIDGIDFromPath(root string, filter func(user.User) bool) (uid, gid uint32, err error) { - ppath, err := fs.RootPath(root, "/etc/passwd") - if err != nil { - return 0, 0, err - } - users, err := user.ParsePasswdFileFilter(ppath, filter) - if err != nil { - return 0, 0, err - } - if len(users) == 0 { - return 0, 0, errNoUsersFound - } - u := users[0] - return uint32(u.Uid), uint32(u.Gid), nil -} - -var errNoGroupsFound = errors.New("no groups found") - -func getGIDFromPath(root string, filter func(user.Group) bool) (gid uint32, err error) { - gpath, err := fs.RootPath(root, "/etc/group") - if err != nil { - return 0, err - } - groups, err := user.ParseGroupFileFilter(gpath, filter) - if err != nil { - return 0, err - } - if len(groups) == 0 { - return 0, errNoGroupsFound - } - g := groups[0] - return uint32(g.Gid), nil -} - -func isRootfsAbs(root string) bool { - return filepath.IsAbs(root) -} - -// WithMaskedPaths sets the masked paths option -func WithMaskedPaths(paths []string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - s.Linux.MaskedPaths = paths - return nil - } -} - -// WithReadonlyPaths sets the read only paths option -func WithReadonlyPaths(paths []string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - s.Linux.ReadonlyPaths = paths - return nil - } -} - -// WithWriteableSysfs makes any sysfs mounts writeable -func WithWriteableSysfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - for i, m := range s.Mounts { - if m.Type == "sysfs" { - var options []string - for _, o := range m.Options { - if o == "ro" { - o = "rw" - } - options = append(options, o) - } - s.Mounts[i].Options = options - } - } - return nil -} - -// WithWriteableCgroupfs makes any cgroup mounts writeable -func WithWriteableCgroupfs(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - for i, m := range s.Mounts { - if m.Type == "cgroup" { - var options []string - for _, o := range m.Options { - if o == "ro" { - o = "rw" - } - options = append(options, o) - } - s.Mounts[i].Options = options - } - } - return nil -} - -// WithSelinuxLabel sets the process SELinux label -func WithSelinuxLabel(label string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.SelinuxLabel = label - return nil - } -} - -// WithApparmorProfile sets the Apparmor profile for the process -func WithApparmorProfile(profile string) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.ApparmorProfile = profile - return nil - } -} - -// WithSeccompUnconfined clears the seccomp profile -func WithSeccompUnconfined(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setLinux(s) - s.Linux.Seccomp = nil - return nil -} - -// WithPrivileged sets up options for a privileged container -// TODO(justincormack) device handling -var WithPrivileged = Compose( - WithAllCapabilities, - WithMaskedPaths(nil), - WithReadonlyPaths(nil), - WithWriteableSysfs, - WithWriteableCgroupfs, - WithSelinuxLabel(""), - WithApparmorProfile(""), - WithSeccompUnconfined, -) diff --git a/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go b/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go deleted file mode 100644 index 3688a582d..000000000 --- a/vendor/github.com/containerd/containerd/oci/spec_opts_windows.go +++ /dev/null @@ -1,89 +0,0 @@ -// +build windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package oci - -import ( - "context" - "encoding/json" - "fmt" - - "github.com/containerd/containerd/containers" - "github.com/containerd/containerd/content" - "github.com/containerd/containerd/images" - "github.com/opencontainers/image-spec/specs-go/v1" - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -// WithImageConfig configures the spec to from the configuration of an Image -func WithImageConfig(image Image) SpecOpts { - return func(ctx context.Context, client Client, _ *containers.Container, s *Spec) error { - setProcess(s) - ic, err := image.Config(ctx) - if err != nil { - return err - } - var ( - ociimage v1.Image - config v1.ImageConfig - ) - switch ic.MediaType { - case v1.MediaTypeImageConfig, images.MediaTypeDockerSchema2Config: - p, err := content.ReadBlob(ctx, image.ContentStore(), ic) - if err != nil { - return err - } - if err := json.Unmarshal(p, &ociimage); err != nil { - return err - } - config = ociimage.Config - default: - return fmt.Errorf("unknown image config media type %s", ic.MediaType) - } - s.Process.Env = config.Env - s.Process.Args = append(config.Entrypoint, config.Cmd...) - s.Process.User = specs.User{ - Username: config.User, - } - return nil - } -} - -// WithTTY sets the information on the spec as well as the environment variables for -// using a TTY -func WithTTY(width, height int) SpecOpts { - return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { - setProcess(s) - s.Process.Terminal = true - if s.Process.ConsoleSize == nil { - s.Process.ConsoleSize = &specs.Box{} - } - s.Process.ConsoleSize.Width = uint(width) - s.Process.ConsoleSize.Height = uint(height) - return nil - } -} - -// WithUsername sets the username on the process -func WithUsername(username string) SpecOpts { - return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { - setProcess(s) - s.Process.User.Username = username - return nil - } -} diff --git a/vendor/github.com/containerd/containerd/oci/spec_unix.go b/vendor/github.com/containerd/containerd/oci/spec_unix.go deleted file mode 100644 index cb69434cb..000000000 --- a/vendor/github.com/containerd/containerd/oci/spec_unix.go +++ /dev/null @@ -1,188 +0,0 @@ -// +build !windows - -/* - Copyright The containerd Authors. - - 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. -*/ - -package oci - -import ( - "context" - "path/filepath" - - "github.com/containerd/containerd/namespaces" - specs "github.com/opencontainers/runtime-spec/specs-go" -) - -const ( - rwm = "rwm" - defaultRootfsPath = "rootfs" -) - -var ( - defaultEnv = []string{ - "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", - } -) - -func defaultCaps() []string { - return []string{ - "CAP_CHOWN", - "CAP_DAC_OVERRIDE", - "CAP_FSETID", - "CAP_FOWNER", - "CAP_MKNOD", - "CAP_NET_RAW", - "CAP_SETGID", - "CAP_SETUID", - "CAP_SETFCAP", - "CAP_SETPCAP", - "CAP_NET_BIND_SERVICE", - "CAP_SYS_CHROOT", - "CAP_KILL", - "CAP_AUDIT_WRITE", - } -} - -func defaultNamespaces() []specs.LinuxNamespace { - return []specs.LinuxNamespace{ - { - Type: specs.PIDNamespace, - }, - { - Type: specs.IPCNamespace, - }, - { - Type: specs.UTSNamespace, - }, - { - Type: specs.MountNamespace, - }, - { - Type: specs.NetworkNamespace, - }, - } -} - -func populateDefaultSpec(ctx context.Context, s *Spec, id string) error { - ns, err := namespaces.NamespaceRequired(ctx) - if err != nil { - return err - } - - *s = Spec{ - Version: specs.Version, - Root: &specs.Root{ - Path: defaultRootfsPath, - }, - Process: &specs.Process{ - Env: defaultEnv, - Cwd: "/", - NoNewPrivileges: true, - User: specs.User{ - UID: 0, - GID: 0, - }, - Capabilities: &specs.LinuxCapabilities{ - Bounding: defaultCaps(), - Permitted: defaultCaps(), - Inheritable: defaultCaps(), - Effective: defaultCaps(), - }, - Rlimits: []specs.POSIXRlimit{ - { - Type: "RLIMIT_NOFILE", - Hard: uint64(1024), - Soft: uint64(1024), - }, - }, - }, - Mounts: []specs.Mount{ - { - Destination: "/proc", - Type: "proc", - Source: "proc", - }, - { - Destination: "/dev", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - { - Destination: "/dev/pts", - Type: "devpts", - Source: "devpts", - Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"}, - }, - { - Destination: "/dev/shm", - Type: "tmpfs", - Source: "shm", - Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"}, - }, - { - Destination: "/dev/mqueue", - Type: "mqueue", - Source: "mqueue", - Options: []string{"nosuid", "noexec", "nodev"}, - }, - { - Destination: "/sys", - Type: "sysfs", - Source: "sysfs", - Options: []string{"nosuid", "noexec", "nodev", "ro"}, - }, - { - Destination: "/run", - Type: "tmpfs", - Source: "tmpfs", - Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"}, - }, - }, - Linux: &specs.Linux{ - MaskedPaths: []string{ - "/proc/acpi", - "/proc/kcore", - "/proc/keys", - "/proc/latency_stats", - "/proc/timer_list", - "/proc/timer_stats", - "/proc/sched_debug", - "/sys/firmware", - "/proc/scsi", - }, - ReadonlyPaths: []string{ - "/proc/asound", - "/proc/bus", - "/proc/fs", - "/proc/irq", - "/proc/sys", - "/proc/sysrq-trigger", - }, - CgroupsPath: filepath.Join("/", ns, id), - Resources: &specs.LinuxResources{ - Devices: []specs.LinuxDeviceCgroup{ - { - Allow: false, - Access: rwm, - }, - }, - }, - Namespaces: defaultNamespaces(), - }, - } - return nil -} diff --git a/vendor/github.com/containerd/containerd/platforms/compare.go b/vendor/github.com/containerd/containerd/platforms/compare.go new file mode 100644 index 000000000..8259bbc85 --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/compare.go @@ -0,0 +1,192 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package platforms + +import specs "github.com/opencontainers/image-spec/specs-go/v1" + +// MatchComparer is able to match and compare platforms to +// filter and sort platforms. +type MatchComparer interface { + Matcher + + Less(specs.Platform, specs.Platform) bool +} + +// Only returns a match comparer for a single platform +// using default resolution logic for the platform. +// +// For ARMv7, will also match ARMv6 and ARMv5 +// For ARMv6, will also match ARMv5 +func Only(platform specs.Platform) MatchComparer { + platform = Normalize(platform) + if platform.Architecture == "arm" { + if platform.Variant == "v7" { + return orderedPlatformComparer{ + matchers: []Matcher{ + &matcher{ + Platform: platform, + }, + &matcher{ + Platform: specs.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: "v6", + }, + }, + &matcher{ + Platform: specs.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: "v5", + }, + }, + }, + } + } + if platform.Variant == "v6" { + return orderedPlatformComparer{ + matchers: []Matcher{ + &matcher{ + Platform: platform, + }, + &matcher{ + Platform: specs.Platform{ + Architecture: platform.Architecture, + OS: platform.OS, + OSVersion: platform.OSVersion, + OSFeatures: platform.OSFeatures, + Variant: "v5", + }, + }, + }, + } + } + } + + return singlePlatformComparer{ + Matcher: &matcher{ + Platform: platform, + }, + } +} + +// Ordered returns a platform MatchComparer which matches any of the platforms +// but orders them in order they are provided. +func Ordered(platforms ...specs.Platform) MatchComparer { + matchers := make([]Matcher, len(platforms)) + for i := range platforms { + matchers[i] = NewMatcher(platforms[i]) + } + return orderedPlatformComparer{ + matchers: matchers, + } +} + +// Any returns a platform MatchComparer which matches any of the platforms +// with no preference for ordering. +func Any(platforms ...specs.Platform) MatchComparer { + matchers := make([]Matcher, len(platforms)) + for i := range platforms { + matchers[i] = NewMatcher(platforms[i]) + } + return anyPlatformComparer{ + matchers: matchers, + } +} + +// All is a platform MatchComparer which matches all platforms +// with preference for ordering. +var All MatchComparer = allPlatformComparer{} + +type singlePlatformComparer struct { + Matcher +} + +func (c singlePlatformComparer) Less(p1, p2 specs.Platform) bool { + return c.Match(p1) && !c.Match(p2) +} + +type orderedPlatformComparer struct { + matchers []Matcher +} + +func (c orderedPlatformComparer) Match(platform specs.Platform) bool { + for _, m := range c.matchers { + if m.Match(platform) { + return true + } + } + return false +} + +func (c orderedPlatformComparer) Less(p1 specs.Platform, p2 specs.Platform) bool { + for _, m := range c.matchers { + p1m := m.Match(p1) + p2m := m.Match(p2) + if p1m && !p2m { + return true + } + if p1m || p2m { + return false + } + } + return false +} + +type anyPlatformComparer struct { + matchers []Matcher +} + +func (c anyPlatformComparer) Match(platform specs.Platform) bool { + for _, m := range c.matchers { + if m.Match(platform) { + return true + } + } + return false +} + +func (c anyPlatformComparer) Less(p1, p2 specs.Platform) bool { + var p1m, p2m bool + for _, m := range c.matchers { + if !p1m && m.Match(p1) { + p1m = true + } + if !p2m && m.Match(p2) { + p2m = true + } + if p1m && p2m { + return false + } + } + // If one matches, and the other does, sort match first + return p1m && !p2m +} + +type allPlatformComparer struct{} + +func (allPlatformComparer) Match(specs.Platform) bool { + return true +} + +func (allPlatformComparer) Less(specs.Platform, specs.Platform) bool { + return false +} diff --git a/vendor/github.com/containerd/containerd/platforms/defaults.go b/vendor/github.com/containerd/containerd/platforms/defaults.go index dee59abad..a14d80e58 100644 --- a/vendor/github.com/containerd/containerd/platforms/defaults.go +++ b/vendor/github.com/containerd/containerd/platforms/defaults.go @@ -22,8 +22,8 @@ import ( specs "github.com/opencontainers/image-spec/specs-go/v1" ) -// Default returns the default specifier for the platform. -func Default() string { +// DefaultString returns the default string specifier for the platform. +func DefaultString() string { return Format(DefaultSpec()) } diff --git a/vendor/github.com/containerd/containerd/platforms/defaults_unix.go b/vendor/github.com/containerd/containerd/platforms/defaults_unix.go new file mode 100644 index 000000000..e8a7d5ffa --- /dev/null +++ b/vendor/github.com/containerd/containerd/platforms/defaults_unix.go @@ -0,0 +1,24 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package platforms + +// Default returns the default matcher for the platform. +func Default() MatchComparer { + return Only(DefaultSpec()) +} diff --git a/vendor/github.com/containerd/containerd/oci/spec_windows.go b/vendor/github.com/containerd/containerd/platforms/defaults_windows.go similarity index 55% rename from vendor/github.com/containerd/containerd/oci/spec_windows.go rename to vendor/github.com/containerd/containerd/platforms/defaults_windows.go index d0236585d..0defbd36c 100644 --- a/vendor/github.com/containerd/containerd/oci/spec_windows.go +++ b/vendor/github.com/containerd/containerd/platforms/defaults_windows.go @@ -1,3 +1,5 @@ +// +build windows + /* Copyright The containerd Authors. @@ -14,31 +16,16 @@ limitations under the License. */ -package oci +package platforms import ( - "context" - - specs "github.com/opencontainers/runtime-spec/specs-go" + specs "github.com/opencontainers/image-spec/specs-go/v1" ) -func populateDefaultSpec(ctx context.Context, s *Spec, id string) error { - *s = Spec{ - Version: specs.Version, - Root: &specs.Root{}, - Process: &specs.Process{ - Cwd: `C:\`, - ConsoleSize: &specs.Box{ - Width: 80, - Height: 20, - }, - }, - Windows: &specs.Windows{ - IgnoreFlushesDuringBoot: true, - Network: &specs.WindowsNetwork{ - AllowUnqualifiedDNSQuery: true, - }, - }, - } - return nil +// Default returns the default matcher for the platform. +func Default() MatchComparer { + return Ordered(DefaultSpec(), specs.Platform{ + OS: "linux", + Architecture: "amd64", + }) } diff --git a/vendor/github.com/containerd/containerd/platforms/platforms.go b/vendor/github.com/containerd/containerd/platforms/platforms.go index 29b7ad6b5..2c2cc1102 100644 --- a/vendor/github.com/containerd/containerd/platforms/platforms.go +++ b/vendor/github.com/containerd/containerd/platforms/platforms.go @@ -109,6 +109,7 @@ package platforms import ( "regexp" "runtime" + "strconv" "strings" "github.com/containerd/containerd/errdefs" @@ -230,6 +231,16 @@ func Parse(specifier string) (specs.Platform, error) { return specs.Platform{}, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: cannot parse platform specifier", specifier) } +// MustParse is like Parses but panics if the specifier cannot be parsed. +// Simplifies initialization of global variables. +func MustParse(specifier string) specs.Platform { + p, err := Parse(specifier) + if err != nil { + panic("platform: Parse(" + strconv.Quote(specifier) + "): " + err.Error()) + } + return p +} + // Format returns a string specifier from the provided platform specification. func Format(platform specs.Platform) string { if platform.OS == "" { diff --git a/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go b/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go index 1509e696c..4a2ce3c39 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/fetcher.go @@ -117,7 +117,7 @@ func (r dockerFetcher) open(ctx context.Context, u, mediatype string, offset int } } else { // TODO: Should any cases where use of content range - // without the proper header be considerd? + // without the proper header be considered? // 206 responses? // Discard up to offset diff --git a/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go b/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go index 5a7778953..9175b6a7a 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/httpreadseeker.go @@ -134,7 +134,7 @@ func (hrs *httpReadSeeker) reader() (io.Reader, error) { // There is an edge case here where offset == size of the content. If // we seek, we will probably get an error for content that cannot be // sought (?). In that case, we should err on committing the content, - // as the length is already satisified but we just return the empty + // as the length is already satisfied but we just return the empty // reader instead. hrs.rc = ioutil.NopCloser(bytes.NewReader([]byte{})) diff --git a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go index 3155d6ec3..45ac1933f 100644 --- a/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go +++ b/vendor/github.com/containerd/containerd/remotes/docker/schema1/converter.go @@ -272,8 +272,14 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro return err } - // TODO: Check if blob -> diff id mapping already exists - // TODO: Check if blob empty label exists + reuse, err := c.reuseLabelBlobState(ctx, desc) + if err != nil { + return err + } + + if reuse { + return nil + } ra, err := c.contentStore.ReaderAt(ctx, desc) if err != nil { @@ -343,6 +349,17 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro state := calc.State() + cinfo := content.Info{ + Digest: desc.Digest, + Labels: map[string]string{ + "containerd.io/uncompressed": state.diffID.String(), + }, + } + + if _, err := c.contentStore.Update(ctx, cinfo, "labels.containerd.io/uncompressed"); err != nil { + return errors.Wrap(err, "failed to update uncompressed label") + } + c.mu.Lock() c.blobMap[desc.Digest] = state c.layerBlobs[state.diffID] = desc @@ -351,6 +368,40 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro return nil } +func (c *Converter) reuseLabelBlobState(ctx context.Context, desc ocispec.Descriptor) (bool, error) { + cinfo, err := c.contentStore.Info(ctx, desc.Digest) + if err != nil { + return false, errors.Wrap(err, "failed to get blob info") + } + desc.Size = cinfo.Size + + diffID, ok := cinfo.Labels["containerd.io/uncompressed"] + if !ok { + return false, nil + } + + bState := blobState{empty: false} + + if bState.diffID, err = digest.Parse(diffID); err != nil { + log.G(ctx).WithField("id", desc.Digest).Warnf("failed to parse digest from label containerd.io/uncompressed: %v", diffID) + return false, nil + } + + // NOTE: there is no need to read header to get compression method + // because there are only two kinds of methods. + if bState.diffID == desc.Digest { + desc.MediaType = images.MediaTypeDockerSchema2Layer + } else { + desc.MediaType = images.MediaTypeDockerSchema2LayerGzip + } + + c.mu.Lock() + c.blobMap[desc.Digest] = bState + c.layerBlobs[bState.diffID] = desc + c.mu.Unlock() + return true, nil +} + func (c *Converter) schema1ManifestHistory() ([]ocispec.History, []digest.Digest, error) { if c.pulledManifest == nil { return nil, nil, errors.New("missing schema 1 manifest for conversion") diff --git a/vendor/github.com/containerd/containerd/remotes/handlers.go b/vendor/github.com/containerd/containerd/remotes/handlers.go index 5c2d84ce4..77310fb62 100644 --- a/vendor/github.com/containerd/containerd/remotes/handlers.go +++ b/vendor/github.com/containerd/containerd/remotes/handlers.go @@ -27,6 +27,7 @@ import ( "github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/images" "github.com/containerd/containerd/log" + "github.com/containerd/containerd/platforms" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" "github.com/sirupsen/logrus" @@ -155,7 +156,7 @@ func push(ctx context.Context, provider content.Provider, pusher Pusher, desc oc // // Base handlers can be provided which will be called before any push specific // handlers. -func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, provider content.Provider, platforms []string, baseHandlers ...images.Handler) error { +func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, provider content.Provider, platform platforms.MatchComparer, baseHandlers ...images.Handler) error { var m sync.Mutex manifestStack := []ocispec.Descriptor{} @@ -175,7 +176,7 @@ func PushContent(ctx context.Context, pusher Pusher, desc ocispec.Descriptor, pr pushHandler := PushHandler(pusher, provider) handlers := append(baseHandlers, - images.FilterPlatforms(images.ChildrenHandler(provider), platforms...), + images.FilterPlatforms(images.ChildrenHandler(provider), platform), filterHandler, pushHandler, ) diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec.go index 4a3fefddc..96c425dd9 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec.go @@ -147,7 +147,7 @@ func (e *execProcess) start(ctx context.Context) (err error) { return errors.Wrap(err, "creating new NULL IO") } } else { - if e.io, err = runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID); err != nil { + if e.io, err = runc.NewPipeIO(e.parent.IoUID, e.parent.IoGID, withConditionalIO(e.stdio)); err != nil { return errors.Wrap(err, "failed to create runc io pipes") } } @@ -164,10 +164,7 @@ func (e *execProcess) start(ctx context.Context) (err error) { return e.parent.runtimeError(err, "OCI runtime exec failed") } if e.stdio.Stdin != "" { - fifoCtx, cancel := context.WithTimeout(ctx, 15*time.Second) - defer cancel() - - sc, err := fifo.OpenFifo(fifoCtx, e.stdio.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0) + sc, err := fifo.OpenFifo(ctx, e.stdio.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0) if err != nil { return errors.Wrapf(err, "failed to open stdin fifo %s", e.stdio.Stdin) } @@ -184,10 +181,7 @@ func (e *execProcess) start(ctx context.Context) (err error) { return errors.Wrap(err, "failed to start console copy") } } else if !e.stdio.IsNull() { - fifoCtx, cancel := context.WithTimeout(ctx, 15*time.Second) - defer cancel() - - if err := copyPipes(fifoCtx, e.io, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, ©WaitGroup); err != nil { + if err := copyPipes(ctx, e.io, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, ©WaitGroup); err != nil { return errors.Wrap(err, "failed to start io pipe copy") } } diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec_state.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec_state.go index 617ec0d97..ac5467552 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec_state.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/exec_state.go @@ -60,11 +60,11 @@ func (s *execCreatedState) Start(ctx context.Context) error { } func (s *execCreatedState) Delete(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() if err := s.p.delete(ctx); err != nil { return err } + s.p.mu.Lock() + defer s.p.mu.Unlock() return s.transition("deleted") } @@ -168,11 +168,11 @@ func (s *execStoppedState) Start(ctx context.Context) error { } func (s *execStoppedState) Delete(ctx context.Context) error { - s.p.mu.Lock() - defer s.p.mu.Unlock() if err := s.p.delete(ctx); err != nil { return err } + s.p.mu.Lock() + defer s.p.mu.Unlock() return s.transition("deleted") } diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init.go index fe11285c7..5bf5f8344 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/init.go @@ -123,7 +123,7 @@ func (p *Init) Create(ctx context.Context, r *CreateConfig) error { return errors.Wrap(err, "creating new NULL IO") } } else { - if p.io, err = runc.NewPipeIO(p.IoUID, p.IoGID); err != nil { + if p.io, err = runc.NewPipeIO(p.IoUID, p.IoGID, withConditionalIO(p.stdio)); err != nil { return errors.Wrap(err, "failed to create OCI runtime io pipes") } } @@ -228,7 +228,7 @@ func (p *Init) Status(ctx context.Context) (string, error) { defer p.mu.Unlock() c, err := p.runtime.State(ctx, p.id) if err != nil { - if os.IsNotExist(err) { + if strings.Contains(err.Error(), "does not exist") { return "stopped", nil } return "", p.runtimeError(err, "OCI runtime state failed") @@ -249,7 +249,6 @@ func (p *Init) setExited(status int) { } func (p *Init) delete(context context.Context) error { - p.KillAll(context) p.wg.Wait() err := p.runtime.Delete(context, p.id, nil) // ignore errors if a runtime has already deleted the process @@ -400,3 +399,11 @@ func (p *Init) runtimeError(rErr error, msg string) error { return errors.Errorf("%s: %s", msg, rMsg) } } + +func withConditionalIO(c proc.Stdio) runc.IOOpt { + return func(o *runc.IOOption) { + o.OpenStdin = c.Stdin != "" + o.OpenStdout = c.Stdout != "" + o.OpenStderr = c.Stderr != "" + } +} diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/io.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/io.go index 96b759cf9..71f6ee1bb 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/io.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/linux/proc/io.go @@ -109,10 +109,9 @@ func copyPipes(ctx context.Context, rio runc.IO, stdin, stdout, stderr string, w i.dest(fw, fr) } if stdin == "" { - rio.Stdin().Close() return nil } - f, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY, 0) + f, err := fifo.OpenFifo(ctx, stdin, syscall.O_RDONLY|syscall.O_NONBLOCK, 0) if err != nil { return fmt.Errorf("containerd-shim: opening %s failed: %s", stdin, err) } diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go index 6c08fe978..24322f07e 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/linux/runtime.go @@ -204,7 +204,7 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts log.G(ctx).WithError(err).WithFields(logrus.Fields{ "id": id, "namespace": namespace, - }).Warn("failed to clen up after killed shim") + }).Warn("failed to clean up after killed shim") } } shimopt = ShimRemote(r.config, r.address, cgroup, exitHandler) @@ -248,8 +248,7 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts if err != nil { return nil, errdefs.FromGRPC(err) } - t, err := newTask(id, namespace, int(cr.Pid), s, r.events, - proc.NewRunc(ropts.RuntimeRoot, sopts.Bundle, namespace, rt, ropts.CriuPath, ropts.SystemdCgroup), r.tasks, bundle) + t, err := newTask(id, namespace, int(cr.Pid), s, r.events, r.tasks, bundle) if err != nil { return nil, err } @@ -341,15 +340,8 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) { } continue } - ropts, err := r.getRuncOptions(ctx, id) - if err != nil { - log.G(ctx).WithError(err).WithField("id", id). - Error("get runtime options") - continue - } - t, err := newTask(id, ns, pid, s, r.events, - proc.NewRunc(ropts.RuntimeRoot, bundle.path, ns, ropts.Runtime, ropts.CriuPath, ropts.SystemdCgroup), r.tasks, bundle) + t, err := newTask(id, ns, pid, s, r.events, r.tasks, bundle) if err != nil { log.G(ctx).WithError(err).Error("loading task type") continue diff --git a/vendor/github.com/containerd/containerd/runtime/v1/linux/task.go b/vendor/github.com/containerd/containerd/runtime/v1/linux/task.go index e90110997..38da35c08 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/linux/task.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/linux/task.go @@ -31,8 +31,7 @@ import ( "github.com/containerd/containerd/log" "github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime/v1/shim/client" - shim "github.com/containerd/containerd/runtime/v1/shim/v1" - runc "github.com/containerd/go-runc" + "github.com/containerd/containerd/runtime/v1/shim/v1" "github.com/containerd/ttrpc" "github.com/containerd/typeurl" "github.com/gogo/protobuf/types" @@ -52,7 +51,7 @@ type Task struct { bundle *bundle } -func newTask(id, namespace string, pid int, shim *client.Client, events *exchange.Exchange, runtime *runc.Runc, list *runtime.TaskList, bundle *bundle) (*Task, error) { +func newTask(id, namespace string, pid int, shim *client.Client, events *exchange.Exchange, list *runtime.TaskList, bundle *bundle) (*Task, error) { var ( err error cg cgroups.Cgroup diff --git a/vendor/github.com/containerd/containerd/runtime/v1/shim/service.go b/vendor/github.com/containerd/containerd/runtime/v1/shim/service.go index c0e7c868a..d76d5803d 100644 --- a/vendor/github.com/containerd/containerd/runtime/v1/shim/service.go +++ b/vendor/github.com/containerd/containerd/runtime/v1/shim/service.go @@ -20,7 +20,9 @@ package shim import ( "context" + "encoding/json" "fmt" + "io/ioutil" "os" "path/filepath" "sync" @@ -41,6 +43,7 @@ import ( runc "github.com/containerd/go-runc" "github.com/containerd/typeurl" ptypes "github.com/gogo/protobuf/types" + specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" "github.com/sirupsen/logrus" "google.golang.org/grpc/codes" @@ -221,19 +224,21 @@ func (s *Service) Delete(ctx context.Context, r *ptypes.Empty) (*shimapi.DeleteR // DeleteProcess deletes an exec'd process func (s *Service) DeleteProcess(ctx context.Context, r *shimapi.DeleteProcessRequest) (*shimapi.DeleteResponse, error) { - s.mu.Lock() - defer s.mu.Unlock() if r.ID == s.id { return nil, status.Errorf(codes.InvalidArgument, "cannot delete init process with DeleteProcess") } + s.mu.Lock() p := s.processes[r.ID] + s.mu.Unlock() if p == nil { return nil, errors.Wrapf(errdefs.ErrNotFound, "process %s", r.ID) } if err := p.Delete(ctx); err != nil { return nil, err } + s.mu.Lock() delete(s.processes, r.ID) + s.mu.Unlock() return &shimapi.DeleteResponse{ ExitStatus: uint32(p.ExitStatus()), ExitedAt: p.ExitedAt(), @@ -507,13 +512,22 @@ func (s *Service) processExits() { func (s *Service) checkProcesses(e runc.Exit) { s.mu.Lock() defer s.mu.Unlock() + + shouldKillAll, err := shouldKillAllOnExit(s.bundle) + if err != nil { + log.G(s.context).WithError(err).Error("failed to check shouldKillAll") + } + for _, p := range s.processes { if p.Pid() == e.Pid { - if ip, ok := p.(*proc.Init); ok { - // Ensure all children are killed - if err := ip.KillAll(s.context); err != nil { - log.G(s.context).WithError(err).WithField("id", ip.ID()). - Error("failed to kill init's children") + + if shouldKillAll { + if ip, ok := p.(*proc.Init); ok { + // Ensure all children are killed + if err := ip.KillAll(s.context); err != nil { + log.G(s.context).WithError(err).WithField("id", ip.ID()). + Error("failed to kill init's children") + } } } p.SetExited(e.Status) @@ -529,6 +543,25 @@ func (s *Service) checkProcesses(e runc.Exit) { } } +func shouldKillAllOnExit(bundlePath string) (bool, error) { + var bundleSpec specs.Spec + bundleConfigContents, err := ioutil.ReadFile(filepath.Join(bundlePath, "config.json")) + if err != nil { + return false, err + } + json.Unmarshal(bundleConfigContents, &bundleSpec) + + if bundleSpec.Linux != nil { + for _, ns := range bundleSpec.Linux.Namespaces { + if ns.Type == specs.PIDNamespace { + return false, nil + } + } + } + + return true, nil +} + func (s *Service) getContainerPids(ctx context.Context, id string) ([]uint32, error) { s.mu.Lock() defer s.mu.Unlock() diff --git a/vendor/github.com/containerd/containerd/runtime/v2/binary.go b/vendor/github.com/containerd/containerd/runtime/v2/binary.go index 29fb1d2c7..0743c4ed9 100644 --- a/vendor/github.com/containerd/containerd/runtime/v2/binary.go +++ b/vendor/github.com/containerd/containerd/runtime/v2/binary.go @@ -73,7 +73,7 @@ func (b *binary) Start(ctx context.Context) (_ *shim, err error) { } }() // open the log pipe and block until the writer is ready - // this helps with syncronization of the shim + // this helps with synchronization of the shim // copy the shim's logs to containerd's output go func() { defer f.Close() diff --git a/vendor/github.com/containerd/containerd/runtime/v2/bundle.go b/vendor/github.com/containerd/containerd/runtime/v2/bundle.go index ad2c894b4..85eeee444 100644 --- a/vendor/github.com/containerd/containerd/runtime/v2/bundle.go +++ b/vendor/github.com/containerd/containerd/runtime/v2/bundle.go @@ -58,16 +58,7 @@ func NewBundle(ctx context.Context, root, state, id string, spec []byte) (b *Bun Path: filepath.Join(state, ns, id), Namespace: ns, } - paths := []string{b.Path, work} - // create base directories - for _, d := range paths { - if err := os.MkdirAll(filepath.Dir(d), 0711); err != nil { - return nil, err - } - if err := os.Mkdir(d, 0711); err != nil { - return nil, err - } - } + var paths []string defer func() { if err != nil { for _, d := range paths { @@ -75,6 +66,28 @@ func NewBundle(ctx context.Context, root, state, id string, spec []byte) (b *Bun } } }() + // create state directory for the bundle + if err := os.MkdirAll(filepath.Dir(b.Path), 0711); err != nil { + return nil, err + } + if err := os.Mkdir(b.Path, 0711); err != nil { + return nil, err + } + paths = append(paths, b.Path) + // create working directory for the bundle + if err := os.MkdirAll(filepath.Dir(work), 0711); err != nil { + return nil, err + } + if err := os.Mkdir(work, 0711); err != nil { + if !os.IsExist(err) { + return nil, err + } + os.RemoveAll(work) + if err := os.Mkdir(work, 0711); err != nil { + return nil, err + } + } + paths = append(paths, work) // create rootfs dir if err := os.Mkdir(filepath.Join(b.Path, "rootfs"), 0711); err != nil { return nil, err diff --git a/vendor/github.com/containerd/containerd/runtime/v2/manager.go b/vendor/github.com/containerd/containerd/runtime/v2/manager.go index fb45823ea..3827bd762 100644 --- a/vendor/github.com/containerd/containerd/runtime/v2/manager.go +++ b/vendor/github.com/containerd/containerd/runtime/v2/manager.go @@ -30,10 +30,8 @@ import ( "github.com/containerd/containerd/metadata" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/namespaces" - "github.com/containerd/containerd/platforms" "github.com/containerd/containerd/plugin" "github.com/containerd/containerd/runtime" - ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) func init() { @@ -44,7 +42,7 @@ func init() { plugin.MetadataPlugin, }, InitFn: func(ic *plugin.InitContext) (interface{}, error) { - ic.Meta.Platforms = []ocispec.Platform{platforms.DefaultSpec()} + ic.Meta.Platforms = supportedPlatforms() if err := os.MkdirAll(ic.Root, 0711); err != nil { return nil, err } diff --git a/vendor/github.com/containerd/containerd/runtime/v2/manager_unix.go b/vendor/github.com/containerd/containerd/runtime/v2/manager_unix.go new file mode 100644 index 000000000..a447f000a --- /dev/null +++ b/vendor/github.com/containerd/containerd/runtime/v2/manager_unix.go @@ -0,0 +1,28 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package v2 + +import ( + "github.com/containerd/containerd/platforms" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" +) + +func supportedPlatforms() []ocispec.Platform { + return []ocispec.Platform{platforms.DefaultSpec()} +} diff --git a/vendor/github.com/containerd/containerd/task_opts_windows.go b/vendor/github.com/containerd/containerd/runtime/v2/manager_windows.go similarity index 63% rename from vendor/github.com/containerd/containerd/task_opts_windows.go rename to vendor/github.com/containerd/containerd/runtime/v2/manager_windows.go index 60836bc8f..7f1648ed7 100644 --- a/vendor/github.com/containerd/containerd/task_opts_windows.go +++ b/vendor/github.com/containerd/containerd/runtime/v2/manager_windows.go @@ -1,3 +1,5 @@ +// +build windows + /* Copyright The containerd Authors. @@ -14,18 +16,19 @@ limitations under the License. */ -package containerd +package v2 import ( - "context" - - specs "github.com/opencontainers/runtime-spec/specs-go" + "github.com/containerd/containerd/platforms" + ocispec "github.com/opencontainers/image-spec/specs-go/v1" ) -// WithResources sets the provided resources on the spec for task updates -func WithResources(resources *specs.WindowsResources) UpdateTaskOpts { - return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error { - r.Resources = resources - return nil +func supportedPlatforms() []ocispec.Platform { + return []ocispec.Platform{ + platforms.DefaultSpec(), + { + OS: "linux", + Architecture: "amd64", + }, } } diff --git a/vendor/github.com/containerd/containerd/runtime/v2/shim.go b/vendor/github.com/containerd/containerd/runtime/v2/shim.go index 78182d60f..982d1bb34 100644 --- a/vendor/github.com/containerd/containerd/runtime/v2/shim.go +++ b/vendor/github.com/containerd/containerd/runtime/v2/shim.go @@ -66,7 +66,7 @@ func loadShim(ctx context.Context, bundle *Bundle, events *exchange.Exchange, rt } }() // open the log pipe and block until the writer is ready - // this helps with syncronization of the shim + // this helps with synchronization of the shim // copy the shim's logs to containerd's output go func() { defer f.Close() diff --git a/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_windows.go b/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_windows.go index 7cffc0265..4e94e7b5d 100644 --- a/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_windows.go +++ b/vendor/github.com/containerd/containerd/runtime/v2/shim/shim_windows.go @@ -27,6 +27,7 @@ import ( "os" "os/exec" "sync" + "unsafe" winio "github.com/Microsoft/go-winio" "github.com/containerd/containerd/events" @@ -35,6 +36,7 @@ import ( "github.com/containerd/typeurl" "github.com/pkg/errors" "github.com/sirupsen/logrus" + "golang.org/x/sys/windows" ) // setupSignals creates a new signal handler for all signals @@ -51,8 +53,43 @@ func subreaper() error { return nil } +type fakeSignal struct { +} + +func (fs *fakeSignal) String() string { + return "" +} + +func (fs *fakeSignal) Signal() { +} + func setupDumpStacks(dump chan<- os.Signal) { - // TODO: JTERRY75: Make this based on events. signal.Notify(dump, syscall.SIGUSR1) + // Windows does not support signals like *nix systems. So instead of + // trapping on SIGUSR1 to dump stacks, we wait on a Win32 event to be + // signaled. ACL'd to builtin administrators and local system + event := "Global\\containerd-shim-runhcs-v1-" + fmt.Sprint(os.Getpid()) + ev, _ := windows.UTF16PtrFromString(event) + sd, err := winio.SddlToSecurityDescriptor("D:P(A;;GA;;;BA)(A;;GA;;;SY)") + if err != nil { + logrus.Errorf("failed to get security descriptor for debug stackdump event %s: %s", event, err.Error()) + return + } + var sa windows.SecurityAttributes + sa.Length = uint32(unsafe.Sizeof(sa)) + sa.InheritHandle = 1 + sa.SecurityDescriptor = uintptr(unsafe.Pointer(&sd[0])) + h, err := windows.CreateEvent(&sa, 0, 0, ev) + if h == 0 || err != nil { + logrus.Errorf("failed to create debug stackdump event %s: %s", event, err.Error()) + return + } + go func() { + logrus.Debugf("Stackdump - waiting signal at %s", event) + for { + windows.WaitForSingleObject(h, windows.INFINITE) + dump <- new(fakeSignal) + } + }() } // serve serves the ttrpc API over a unix socket at the provided path diff --git a/vendor/github.com/containerd/containerd/services/content/service.go b/vendor/github.com/containerd/containerd/services/content/service.go index 42926890e..a27e8ee98 100644 --- a/vendor/github.com/containerd/containerd/services/content/service.go +++ b/vendor/github.com/containerd/containerd/services/content/service.go @@ -70,13 +70,13 @@ func init() { if err != nil { return nil, err } - return newService(cs.(content.Store)), nil + return NewService(cs.(content.Store)), nil }, }) } -// newService returns the content GRPC server -func newService(cs content.Store) api.ContentServer { +// NewService returns the content GRPC server +func NewService(cs content.Store) api.ContentServer { return &service{store: cs} } diff --git a/vendor/github.com/containerd/containerd/services/diff/service_windows.go b/vendor/github.com/containerd/containerd/services/diff/service_windows.go index 91f54cca0..00584ecb5 100644 --- a/vendor/github.com/containerd/containerd/services/diff/service_windows.go +++ b/vendor/github.com/containerd/containerd/services/diff/service_windows.go @@ -19,5 +19,5 @@ package diff var defaultDifferConfig = &config{ - Order: []string{"windows"}, + Order: []string{"windows", "windows-lcow"}, } diff --git a/vendor/github.com/containerd/containerd/snapshots/overlay/check.go b/vendor/github.com/containerd/containerd/snapshots/overlay/check.go index 6ec59b7ed..cec46df03 100644 --- a/vendor/github.com/containerd/containerd/snapshots/overlay/check.go +++ b/vendor/github.com/containerd/containerd/snapshots/overlay/check.go @@ -71,7 +71,7 @@ func supportsMultipleLowerDir(d string) error { } // Supported returns nil when the overlayfs is functional on the system with the root directory. -// Suppported is not called during plugin initialization, but exposed for downstream projects which uses +// Supported is not called during plugin initialization, but exposed for downstream projects which uses // this snapshotter as a library. func Supported(root string) error { if err := os.MkdirAll(root, 0700); err != nil { diff --git a/vendor/github.com/containerd/containerd/snapshots/overlay/overlay.go b/vendor/github.com/containerd/containerd/snapshots/overlay/overlay.go index 0650e7878..2c296adbe 100644 --- a/vendor/github.com/containerd/containerd/snapshots/overlay/overlay.go +++ b/vendor/github.com/containerd/containerd/snapshots/overlay/overlay.go @@ -168,7 +168,7 @@ func (o *snapshotter) Usage(ctx context.Context, key string) (snapshots.Usage, e upperPath := o.upperPath(id) if info.Kind == snapshots.KindActive { - du, err := fs.DiskUsage(upperPath) + du, err := fs.DiskUsage(ctx, upperPath) if err != nil { // TODO(stevvooe): Consider not reporting an error in this case. return snapshots.Usage{}, err @@ -225,7 +225,7 @@ func (o *snapshotter) Commit(ctx context.Context, name, key string, opts ...snap return err } - usage, err := fs.DiskUsage(o.upperPath(id)) + usage, err := fs.DiskUsage(ctx, o.upperPath(id)) if err != nil { return err } diff --git a/vendor/github.com/containerd/containerd/sys/mount_linux.go b/vendor/github.com/containerd/containerd/sys/mount_linux.go new file mode 100644 index 000000000..a9eee9b73 --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/mount_linux.go @@ -0,0 +1,119 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import ( + "runtime" + "syscall" + "unsafe" + + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// FMountat performs mount from the provided directory. +func FMountat(dirfd uintptr, source, target, fstype string, flags uintptr, data string) error { + var ( + sourceP, targetP, fstypeP, dataP *byte + pid uintptr + ws unix.WaitStatus + err error + errno syscall.Errno + ) + + sourceP, err = syscall.BytePtrFromString(source) + if err != nil { + return err + } + + targetP, err = syscall.BytePtrFromString(target) + if err != nil { + return err + } + + fstypeP, err = syscall.BytePtrFromString(fstype) + if err != nil { + return err + } + + if data != "" { + dataP, err = syscall.BytePtrFromString(data) + if err != nil { + return err + } + } + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + + pid, errno = forkAndMountat(dirfd, + uintptr(unsafe.Pointer(sourceP)), + uintptr(unsafe.Pointer(targetP)), + uintptr(unsafe.Pointer(fstypeP)), + flags, + uintptr(unsafe.Pointer(dataP))) + + if errno != 0 { + return errors.Wrap(errno, "failed to fork thread") + } + + _, err = unix.Wait4(int(pid), &ws, 0, nil) + for err == syscall.EINTR { + _, err = unix.Wait4(int(pid), &ws, 0, nil) + } + + if err != nil { + return errors.Wrapf(err, "failed to find pid=%d process", pid) + } + + errno = syscall.Errno(ws.ExitStatus()) + if errno != 0 { + return errors.Wrap(errno, "failed to mount") + } + return nil +} + +// forkAndMountat will fork thread, change working dir and mount. +// +// precondition: the runtime OS thread must be locked. +func forkAndMountat(dirfd uintptr, source, target, fstype, flags, data uintptr) (pid uintptr, errno syscall.Errno) { + // block signal during clone + beforeFork() + + // the cloned thread shares the open file descriptor, but the thread + // never be reused by runtime. + pid, _, errno = syscall.RawSyscall6(syscall.SYS_CLONE, uintptr(syscall.SIGCHLD)|syscall.CLONE_FILES, 0, 0, 0, 0, 0) + if errno != 0 || pid != 0 { + // restore all signals + afterFork() + return + } + + // restore all signals + afterForkInChild() + + // change working dir + _, _, errno = syscall.RawSyscall(syscall.SYS_FCHDIR, dirfd, 0, 0) + if errno != 0 { + goto childerr + } + _, _, errno = syscall.RawSyscall6(syscall.SYS_MOUNT, source, target, fstype, flags, data, 0) + +childerr: + syscall.RawSyscall(syscall.SYS_EXIT, uintptr(errno), 0, 0) + panic("unreachable") +} diff --git a/vendor/github.com/containerd/containerd/sys/socket_unix.go b/vendor/github.com/containerd/containerd/sys/socket_unix.go index 0dbca0e33..90fa55c48 100644 --- a/vendor/github.com/containerd/containerd/sys/socket_unix.go +++ b/vendor/github.com/containerd/containerd/sys/socket_unix.go @@ -42,7 +42,7 @@ func CreateUnixSocket(path string) (net.Listener, error) { return net.Listen("unix", path) } -// GetLocalListener returns a listerner out of a unix socket. +// GetLocalListener returns a listener out of a unix socket. func GetLocalListener(path string, uid, gid int) (net.Listener, error) { // Ensure parent directory is created if err := mkdirAs(filepath.Dir(path), uid, gid); err != nil { diff --git a/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.go b/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.go new file mode 100644 index 000000000..6e40a9c7d --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.go @@ -0,0 +1,30 @@ +/* + Copyright The containerd Authors. + + 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. +*/ + +package sys + +import ( + _ "unsafe" // required for go:linkname. +) + +//go:linkname beforeFork syscall.runtime_BeforeFork +func beforeFork() + +//go:linkname afterFork syscall.runtime_AfterFork +func afterFork() + +//go:linkname afterForkInChild syscall.runtime_AfterForkInChild +func afterForkInChild() diff --git a/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.s b/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.s new file mode 100644 index 000000000..c073fa4ad --- /dev/null +++ b/vendor/github.com/containerd/containerd/sys/subprocess_unsafe_linux.s @@ -0,0 +1,15 @@ +/* + Copyright The containerd Authors. + + 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/containerd/containerd/task_opts.go b/vendor/github.com/containerd/containerd/task_opts.go index 9e998a349..ce861ea51 100644 --- a/vendor/github.com/containerd/containerd/task_opts.go +++ b/vendor/github.com/containerd/containerd/task_opts.go @@ -18,10 +18,18 @@ package containerd import ( "context" + "encoding/json" + "fmt" "syscall" + "github.com/containerd/containerd/api/types" + "github.com/containerd/containerd/content" "github.com/containerd/containerd/errdefs" + "github.com/containerd/containerd/images" "github.com/containerd/containerd/mount" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" ) // NewTaskOpts allows the caller to set options on a new task @@ -35,6 +43,44 @@ func WithRootFS(mounts []mount.Mount) NewTaskOpts { } } +// WithTaskCheckpoint allows a task to be created with live runtime and memory data from a +// previous checkpoint. Additional software such as CRIU may be required to +// restore a task from a checkpoint +func WithTaskCheckpoint(im Image) NewTaskOpts { + return func(ctx context.Context, c *Client, info *TaskInfo) error { + desc := im.Target() + id := desc.Digest + index, err := decodeIndex(ctx, c.ContentStore(), desc) + if err != nil { + return err + } + for _, m := range index.Manifests { + if m.MediaType == images.MediaTypeContainerd1Checkpoint { + info.Checkpoint = &types.Descriptor{ + MediaType: m.MediaType, + Size_: m.Size, + Digest: m.Digest, + } + return nil + } + } + return fmt.Errorf("checkpoint not found in index %s", id) + } +} + +func decodeIndex(ctx context.Context, store content.Provider, desc imagespec.Descriptor) (*imagespec.Index, error) { + var index imagespec.Index + p, err := content.ReadBlob(ctx, store, desc) + if err != nil { + return nil, err + } + if err := json.Unmarshal(p, &index); err != nil { + return nil, err + } + + return &index, nil +} + // WithCheckpointName sets the image name for the checkpoint func WithCheckpointName(name string) CheckpointTaskOpts { return func(r *CheckpointTaskInfo) error { @@ -92,3 +138,19 @@ func WithKillExecID(execID string) KillOpts { return nil } } + +// WithResources sets the provided resources for task updates. Resources must be +// either a *specs.LinuxResources or a *specs.WindowsResources +func WithResources(resources interface{}) UpdateTaskOpts { + return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error { + switch resources.(type) { + case *specs.LinuxResources: + case *specs.WindowsResources: + default: + return errors.New("WithResources requires a *specs.LinuxResources or *specs.WindowsResources") + } + + r.Resources = resources + return nil + } +} diff --git a/vendor/github.com/containerd/containerd/task_opts_linux.go b/vendor/github.com/containerd/containerd/task_opts_unix.go similarity index 72% rename from vendor/github.com/containerd/containerd/task_opts_linux.go rename to vendor/github.com/containerd/containerd/task_opts_unix.go index 551cb996c..f8652be3b 100644 --- a/vendor/github.com/containerd/containerd/task_opts_linux.go +++ b/vendor/github.com/containerd/containerd/task_opts_unix.go @@ -1,3 +1,5 @@ +// +build !windows + /* Copyright The containerd Authors. @@ -18,20 +20,11 @@ package containerd import ( "context" - "errors" "github.com/containerd/containerd/runtime/linux/runctypes" - "github.com/opencontainers/runtime-spec/specs-go" + "github.com/pkg/errors" ) -// WithResources sets the provided resources for task updates -func WithResources(resources *specs.LinuxResources) UpdateTaskOpts { - return func(ctx context.Context, client *Client, r *UpdateTaskInfo) error { - r.Resources = resources - return nil - } -} - // WithNoNewKeyring causes tasks not to be created with a new keyring for secret storage. // There is an upper limit on the number of keyrings in a linux system func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error { @@ -46,3 +39,19 @@ func WithNoNewKeyring(ctx context.Context, c *Client, ti *TaskInfo) error { opts.NoNewKeyring = true return nil } + +// WithNoPivotRoot instructs the runtime not to you pivot_root +func WithNoPivotRoot(_ context.Context, _ *Client, info *TaskInfo) error { + if info.Options == nil { + info.Options = &runctypes.CreateOptions{ + NoPivotRoot: true, + } + return nil + } + opts, ok := info.Options.(*runctypes.CreateOptions) + if !ok { + return errors.New("invalid options type, expected runctypes.CreateOptions") + } + opts.NoPivotRoot = true + return nil +} diff --git a/vendor/github.com/containerd/containerd/vendor.conf b/vendor/github.com/containerd/containerd/vendor.conf index 99f3b7b16..657cd20ae 100644 --- a/vendor/github.com/containerd/containerd/vendor.conf +++ b/vendor/github.com/containerd/containerd/vendor.conf @@ -1,10 +1,10 @@ -github.com/containerd/go-runc edcf3de1f4971445c42d61f20d506b30612aa031 -github.com/containerd/console 4d8a41f4ce5b9bae77c41786ea2458330f43f081 +github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 +github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244 -github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b +github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537 github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6 github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 @@ -20,7 +20,7 @@ github.com/gogo/protobuf v1.0.0 github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef github.com/golang/protobuf v1.1.0 github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce # v1.0.1-43-gd810dbc -github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340 +github.com/opencontainers/runc 20aff4f0488c6d4b8df4d85b4f63f1f704c11abd github.com/sirupsen/logrus v1.0.0 github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac @@ -32,8 +32,8 @@ github.com/opencontainers/image-spec v1.0.1 golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 -github.com/Microsoft/go-winio v0.4.7 -github.com/Microsoft/hcsshim v0.6.11 +github.com/Microsoft/go-winio v0.4.10 +github.com/Microsoft/hcsshim 44c060121b68e8bdc40b411beba551f3b4ee9e55 github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 @@ -43,7 +43,7 @@ gotest.tools v2.1.0 github.com/google/go-cmp v0.1.0 # cri dependencies -github.com/containerd/cri 661f3b0377db409fe0e5677115f02ce7b89fd17d https://github.com/dmcgowan/cri-containerd +github.com/containerd/cri v1.11.1 github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534 github.com/blang/semver v3.1.0 github.com/containernetworking/cni v0.6.0 @@ -71,7 +71,7 @@ github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874 golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 -gopkg.in/yaml.v2 53feefa2559fb8dfa8d81baad31be332c97d6c77 +gopkg.in/yaml.v2 v2.2.1 k8s.io/api 9e5ffd1f1320950b238cfce291b926411f0af722 k8s.io/apimachinery ed135c5b96450fd24e5e981c708114fbbd950697 k8s.io/apiserver a90e3a95c2e91b944bfca8225c4e0d12e42a9eb5 @@ -85,4 +85,4 @@ github.com/mistifyio/go-zfs 166add352731e515512690329794ee593f1aaff2 github.com/pborman/uuid c65b2f87fee37d1c7854c9164a450713c28d50cd # aufs dependencies -github.com/containerd/aufs a7fbd554da7a9eafbe5a460a421313a9fd18d988 +github.com/containerd/aufs ffa39970e26ad01d81f540b21e65f9c1841a5f92 diff --git a/vendor/github.com/containerd/containerd/version/version.go b/vendor/github.com/containerd/containerd/version/version.go index 051919a68..a34595c7c 100644 --- a/vendor/github.com/containerd/containerd/version/version.go +++ b/vendor/github.com/containerd/containerd/version/version.go @@ -21,7 +21,7 @@ var ( Package = "github.com/containerd/containerd" // Version holds the complete version number. Filled in at linking time. - Version = "1.1.0+unknown" + Version = "1.2.0-beta.2+unknown" // Revision is filled with the VCS (e.g. git) revision being used to build // the program at linking time. diff --git a/vendor/github.com/containerd/continuity/context.go b/vendor/github.com/containerd/continuity/context.go new file mode 100644 index 000000000..45b73dc9e --- /dev/null +++ b/vendor/github.com/containerd/continuity/context.go @@ -0,0 +1,657 @@ +package continuity + +import ( + "bytes" + "fmt" + "io" + "log" + "os" + "path/filepath" + "strings" + + "github.com/containerd/continuity/devices" + driverpkg "github.com/containerd/continuity/driver" + "github.com/containerd/continuity/pathdriver" + + "github.com/opencontainers/go-digest" +) + +var ( + // ErrNotFound represents the resource not found + ErrNotFound = fmt.Errorf("not found") + // ErrNotSupported represents the resource not supported + ErrNotSupported = fmt.Errorf("not supported") +) + +// Context represents a file system context for accessing resources. The +// responsibility of the context is to convert system specific resources to +// generic Resource objects. Most of this is safe path manipulation, as well +// as extraction of resource details. +type Context interface { + Apply(Resource) error + Verify(Resource) error + Resource(string, os.FileInfo) (Resource, error) + Walk(filepath.WalkFunc) error +} + +// SymlinkPath is intended to give the symlink target value +// in a root context. Target and linkname are absolute paths +// not under the given root. +type SymlinkPath func(root, linkname, target string) (string, error) + +// ContextOptions represents options to create a new context. +type ContextOptions struct { + Digester Digester + Driver driverpkg.Driver + PathDriver pathdriver.PathDriver + Provider ContentProvider +} + +// context represents a file system context for accessing resources. +// Generally, all path qualified access and system considerations should land +// here. +type context struct { + driver driverpkg.Driver + pathDriver pathdriver.PathDriver + root string + digester Digester + provider ContentProvider +} + +// NewContext returns a Context associated with root. The default driver will +// be used, as returned by NewDriver. +func NewContext(root string) (Context, error) { + return NewContextWithOptions(root, ContextOptions{}) +} + +// NewContextWithOptions returns a Context associate with the root. +func NewContextWithOptions(root string, options ContextOptions) (Context, error) { + // normalize to absolute path + pathDriver := options.PathDriver + if pathDriver == nil { + pathDriver = pathdriver.LocalPathDriver + } + + root = pathDriver.FromSlash(root) + root, err := pathDriver.Abs(pathDriver.Clean(root)) + if err != nil { + return nil, err + } + + driver := options.Driver + if driver == nil { + driver, err = driverpkg.NewSystemDriver() + if err != nil { + return nil, err + } + } + + digester := options.Digester + if digester == nil { + digester = simpleDigester{digest.Canonical} + } + + // Check the root directory. Need to be a little careful here. We are + // allowing a link for now, but this may have odd behavior when + // canonicalizing paths. As long as all files are opened through the link + // path, this should be okay. + fi, err := driver.Stat(root) + if err != nil { + return nil, err + } + + if !fi.IsDir() { + return nil, &os.PathError{Op: "NewContext", Path: root, Err: os.ErrInvalid} + } + + return &context{ + root: root, + driver: driver, + pathDriver: pathDriver, + digester: digester, + provider: options.Provider, + }, nil +} + +// Resource returns the resource as path p, populating the entry with info +// from fi. The path p should be the path of the resource in the context, +// typically obtained through Walk or from the value of Resource.Path(). If fi +// is nil, it will be resolved. +func (c *context) Resource(p string, fi os.FileInfo) (Resource, error) { + fp, err := c.fullpath(p) + if err != nil { + return nil, err + } + + if fi == nil { + fi, err = c.driver.Lstat(fp) + if err != nil { + return nil, err + } + } + + base, err := newBaseResource(p, fi) + if err != nil { + return nil, err + } + + base.xattrs, err = c.resolveXAttrs(fp, fi, base) + if err == ErrNotSupported { + log.Printf("resolving xattrs on %s not supported", fp) + } else if err != nil { + return nil, err + } + + // TODO(stevvooe): Handle windows alternate data streams. + + if fi.Mode().IsRegular() { + dgst, err := c.digest(p) + if err != nil { + return nil, err + } + + return newRegularFile(*base, base.paths, fi.Size(), dgst) + } + + if fi.Mode().IsDir() { + return newDirectory(*base) + } + + if fi.Mode()&os.ModeSymlink != 0 { + // We handle relative links vs absolute links by including a + // beginning slash for absolute links. Effectively, the bundle's + // root is treated as the absolute link anchor. + target, err := c.driver.Readlink(fp) + if err != nil { + return nil, err + } + + return newSymLink(*base, target) + } + + if fi.Mode()&os.ModeNamedPipe != 0 { + return newNamedPipe(*base, base.paths) + } + + if fi.Mode()&os.ModeDevice != 0 { + deviceDriver, ok := c.driver.(driverpkg.DeviceInfoDriver) + if !ok { + log.Printf("device extraction not supported %s", fp) + return nil, ErrNotSupported + } + + // character and block devices merely need to recover the + // major/minor device number. + major, minor, err := deviceDriver.DeviceInfo(fi) + if err != nil { + return nil, err + } + + return newDevice(*base, base.paths, major, minor) + } + + log.Printf("%q (%v) is not supported", fp, fi.Mode()) + return nil, ErrNotFound +} + +func (c *context) verifyMetadata(resource, target Resource) error { + if target.Mode() != resource.Mode() { + return fmt.Errorf("resource %q has incorrect mode: %v != %v", target.Path(), target.Mode(), resource.Mode()) + } + + if target.UID() != resource.UID() { + return fmt.Errorf("unexpected uid for %q: %v != %v", target.Path(), target.UID(), resource.GID()) + } + + if target.GID() != resource.GID() { + return fmt.Errorf("unexpected gid for %q: %v != %v", target.Path(), target.GID(), target.GID()) + } + + if xattrer, ok := resource.(XAttrer); ok { + txattrer, tok := target.(XAttrer) + if !tok { + return fmt.Errorf("resource %q has xattrs but target does not support them", resource.Path()) + } + + // For xattrs, only ensure that we have those defined in the resource + // and their values match. We can ignore other xattrs. In other words, + // we only verify that target has the subset defined by resource. + txattrs := txattrer.XAttrs() + for attr, value := range xattrer.XAttrs() { + tvalue, ok := txattrs[attr] + if !ok { + return fmt.Errorf("resource %q target missing xattr %q", resource.Path(), attr) + } + + if !bytes.Equal(value, tvalue) { + return fmt.Errorf("xattr %q value differs for resource %q", attr, resource.Path()) + } + } + } + + switch r := resource.(type) { + case RegularFile: + // TODO(stevvooe): Another reason to use a record-based approach. We + // have to do another type switch to get this to work. This could be + // fixed with an Equal function, but let's study this a little more to + // be sure. + t, ok := target.(RegularFile) + if !ok { + return fmt.Errorf("resource %q target not a regular file", r.Path()) + } + + if t.Size() != r.Size() { + return fmt.Errorf("resource %q target has incorrect size: %v != %v", t.Path(), t.Size(), r.Size()) + } + case Directory: + t, ok := target.(Directory) + if !ok { + return fmt.Errorf("resource %q target not a directory", t.Path()) + } + case SymLink: + t, ok := target.(SymLink) + if !ok { + return fmt.Errorf("resource %q target not a symlink", t.Path()) + } + + if t.Target() != r.Target() { + return fmt.Errorf("resource %q target has mismatched target: %q != %q", t.Path(), t.Target(), r.Target()) + } + case Device: + t, ok := target.(Device) + if !ok { + return fmt.Errorf("resource %q is not a device", t.Path()) + } + + if t.Major() != r.Major() || t.Minor() != r.Minor() { + return fmt.Errorf("resource %q has mismatched major/minor numbers: %d,%d != %d,%d", t.Path(), t.Major(), t.Minor(), r.Major(), r.Minor()) + } + case NamedPipe: + t, ok := target.(NamedPipe) + if !ok { + return fmt.Errorf("resource %q is not a named pipe", t.Path()) + } + default: + return fmt.Errorf("cannot verify resource: %v", resource) + } + + return nil +} + +// Verify the resource in the context. An error will be returned a discrepancy +// is found. +func (c *context) Verify(resource Resource) error { + fp, err := c.fullpath(resource.Path()) + if err != nil { + return err + } + + fi, err := c.driver.Lstat(fp) + if err != nil { + return err + } + + target, err := c.Resource(resource.Path(), fi) + if err != nil { + return err + } + + if target.Path() != resource.Path() { + return fmt.Errorf("resource paths do not match: %q != %q", target.Path(), resource.Path()) + } + + if err := c.verifyMetadata(resource, target); err != nil { + return err + } + + if h, isHardlinkable := resource.(Hardlinkable); isHardlinkable { + hardlinkKey, err := newHardlinkKey(fi) + if err == errNotAHardLink { + if len(h.Paths()) > 1 { + return fmt.Errorf("%q is not a hardlink to %q", h.Paths()[1], resource.Path()) + } + } else if err != nil { + return err + } + + for _, path := range h.Paths()[1:] { + fpLink, err := c.fullpath(path) + if err != nil { + return err + } + + fiLink, err := c.driver.Lstat(fpLink) + if err != nil { + return err + } + + targetLink, err := c.Resource(path, fiLink) + if err != nil { + return err + } + + hardlinkKeyLink, err := newHardlinkKey(fiLink) + if err != nil { + return err + } + + if hardlinkKeyLink != hardlinkKey { + return fmt.Errorf("%q is not a hardlink to %q", path, resource.Path()) + } + + if err := c.verifyMetadata(resource, targetLink); err != nil { + return err + } + } + } + + switch r := resource.(type) { + case RegularFile: + t, ok := target.(RegularFile) + if !ok { + return fmt.Errorf("resource %q target not a regular file", r.Path()) + } + + // TODO(stevvooe): This may need to get a little more sophisticated + // for digest comparison. We may want to actually calculate the + // provided digests, rather than the implementations having an + // overlap. + if !digestsMatch(t.Digests(), r.Digests()) { + return fmt.Errorf("digests for resource %q do not match: %v != %v", t.Path(), t.Digests(), r.Digests()) + } + } + + return nil +} + +func (c *context) checkoutFile(fp string, rf RegularFile) error { + if c.provider == nil { + return fmt.Errorf("no file provider") + } + var ( + r io.ReadCloser + err error + ) + for _, dgst := range rf.Digests() { + r, err = c.provider.Reader(dgst) + if err == nil { + break + } + } + if err != nil { + return fmt.Errorf("file content could not be provided: %v", err) + } + defer r.Close() + + return atomicWriteFile(fp, r, rf.Size(), rf.Mode()) +} + +// Apply the resource to the contexts. An error will be returned if the +// operation fails. Depending on the resource type, the resource may be +// created. For resource that cannot be resolved, an error will be returned. +func (c *context) Apply(resource Resource) error { + fp, err := c.fullpath(resource.Path()) + if err != nil { + return err + } + + if !strings.HasPrefix(fp, c.root) { + return fmt.Errorf("resource %v escapes root", resource) + } + + var chmod = true + fi, err := c.driver.Lstat(fp) + if err != nil { + if !os.IsNotExist(err) { + return err + } + } + + switch r := resource.(type) { + case RegularFile: + if fi == nil { + if err := c.checkoutFile(fp, r); err != nil { + return fmt.Errorf("error checking out file %q: %v", resource.Path(), err) + } + chmod = false + } else { + if !fi.Mode().IsRegular() { + return fmt.Errorf("file %q should be a regular file, but is not", resource.Path()) + } + if fi.Size() != r.Size() { + if err := c.checkoutFile(fp, r); err != nil { + return fmt.Errorf("error checking out file %q: %v", resource.Path(), err) + } + } else { + for _, dgst := range r.Digests() { + f, err := os.Open(fp) + if err != nil { + return fmt.Errorf("failure opening file for read %q: %v", resource.Path(), err) + } + compared, err := dgst.Algorithm().FromReader(f) + if err == nil && dgst != compared { + if err := c.checkoutFile(fp, r); err != nil { + return fmt.Errorf("error checking out file %q: %v", resource.Path(), err) + } + break + } + if err1 := f.Close(); err == nil { + err = err1 + } + if err != nil { + return fmt.Errorf("error checking digest for %q: %v", resource.Path(), err) + } + } + } + } + case Directory: + if fi == nil { + if err := c.driver.Mkdir(fp, resource.Mode()); err != nil { + return err + } + } else if !fi.Mode().IsDir() { + return fmt.Errorf("%q should be a directory, but is not", resource.Path()) + } + + case SymLink: + var target string // only possibly set if target resource is a symlink + + if fi != nil { + if fi.Mode()&os.ModeSymlink != 0 { + target, err = c.driver.Readlink(fp) + if err != nil { + return err + } + } + } + + if target != r.Target() { + if fi != nil { + if err := c.driver.Remove(fp); err != nil { // RemoveAll in case of directory? + return err + } + } + + if err := c.driver.Symlink(r.Target(), fp); err != nil { + return err + } + } + + case Device: + if fi == nil { + if err := c.driver.Mknod(fp, resource.Mode(), int(r.Major()), int(r.Minor())); err != nil { + return err + } + } else if (fi.Mode() & os.ModeDevice) == 0 { + return fmt.Errorf("%q should be a device, but is not", resource.Path()) + } else { + major, minor, err := devices.DeviceInfo(fi) + if err != nil { + return err + } + if major != r.Major() || minor != r.Minor() { + if err := c.driver.Remove(fp); err != nil { + return err + } + + if err := c.driver.Mknod(fp, resource.Mode(), int(r.Major()), int(r.Minor())); err != nil { + return err + } + } + } + + case NamedPipe: + if fi == nil { + if err := c.driver.Mkfifo(fp, resource.Mode()); err != nil { + return err + } + } else if (fi.Mode() & os.ModeNamedPipe) == 0 { + return fmt.Errorf("%q should be a named pipe, but is not", resource.Path()) + } + } + + if h, isHardlinkable := resource.(Hardlinkable); isHardlinkable { + for _, path := range h.Paths() { + if path == resource.Path() { + continue + } + + lp, err := c.fullpath(path) + if err != nil { + return err + } + + if _, fi := c.driver.Lstat(lp); fi == nil { + c.driver.Remove(lp) + } + if err := c.driver.Link(fp, lp); err != nil { + return err + } + } + } + + // Update filemode if file was not created + if chmod { + if err := c.driver.Lchmod(fp, resource.Mode()); err != nil { + return err + } + } + + if err := c.driver.Lchown(fp, resource.UID(), resource.GID()); err != nil { + return err + } + + if xattrer, ok := resource.(XAttrer); ok { + // For xattrs, only ensure that we have those defined in the resource + // and their values are set. We can ignore other xattrs. In other words, + // we only set xattres defined by resource but never remove. + + if _, ok := resource.(SymLink); ok { + lxattrDriver, ok := c.driver.(driverpkg.LXAttrDriver) + if !ok { + return fmt.Errorf("unsupported symlink xattr for resource %q", resource.Path()) + } + if err := lxattrDriver.LSetxattr(fp, xattrer.XAttrs()); err != nil { + return err + } + } else { + xattrDriver, ok := c.driver.(driverpkg.XAttrDriver) + if !ok { + return fmt.Errorf("unsupported xattr for resource %q", resource.Path()) + } + if err := xattrDriver.Setxattr(fp, xattrer.XAttrs()); err != nil { + return err + } + } + } + + return nil +} + +// Walk provides a convenience function to call filepath.Walk correctly for +// the context. Otherwise identical to filepath.Walk, the path argument is +// corrected to be contained within the context. +func (c *context) Walk(fn filepath.WalkFunc) error { + root := c.root + fi, err := c.driver.Lstat(c.root) + if err == nil && fi.Mode()&os.ModeSymlink != 0 { + root, err = c.driver.Readlink(c.root) + if err != nil { + return err + } + } + return c.pathDriver.Walk(root, func(p string, fi os.FileInfo, err error) error { + contained, err := c.containWithRoot(p, root) + return fn(contained, fi, err) + }) +} + +// fullpath returns the system path for the resource, joined with the context +// root. The path p must be a part of the context. +func (c *context) fullpath(p string) (string, error) { + p = c.pathDriver.Join(c.root, p) + if !strings.HasPrefix(p, c.root) { + return "", fmt.Errorf("invalid context path") + } + + return p, nil +} + +// contain cleans and santizes the filesystem path p to be an absolute path, +// effectively relative to the context root. +func (c *context) contain(p string) (string, error) { + return c.containWithRoot(p, c.root) +} + +// containWithRoot cleans and santizes the filesystem path p to be an absolute path, +// effectively relative to the passed root. Extra care should be used when calling this +// instead of contain. This is needed for Walk, as if context root is a symlink, +// it must be evaluated prior to the Walk +func (c *context) containWithRoot(p string, root string) (string, error) { + sanitized, err := c.pathDriver.Rel(root, p) + if err != nil { + return "", err + } + + // ZOMBIES(stevvooe): In certain cases, we may want to remap these to a + // "containment error", so the caller can decide what to do. + return c.pathDriver.Join("/", c.pathDriver.Clean(sanitized)), nil +} + +// digest returns the digest of the file at path p, relative to the root. +func (c *context) digest(p string) (digest.Digest, error) { + f, err := c.driver.Open(c.pathDriver.Join(c.root, p)) + if err != nil { + return "", err + } + defer f.Close() + + return c.digester.Digest(f) +} + +// resolveXAttrs attempts to resolve the extended attributes for the resource +// at the path fp, which is the full path to the resource. If the resource +// cannot have xattrs, nil will be returned. +func (c *context) resolveXAttrs(fp string, fi os.FileInfo, base *resource) (map[string][]byte, error) { + if fi.Mode().IsRegular() || fi.Mode().IsDir() { + xattrDriver, ok := c.driver.(driverpkg.XAttrDriver) + if !ok { + log.Println("xattr extraction not supported") + return nil, ErrNotSupported + } + + return xattrDriver.Getxattr(fp) + } + + if fi.Mode()&os.ModeSymlink != 0 { + lxattrDriver, ok := c.driver.(driverpkg.LXAttrDriver) + if !ok { + log.Println("xattr extraction for symlinks not supported") + return nil, ErrNotSupported + } + + return lxattrDriver.LGetxattr(fp) + } + + return nil, nil +} diff --git a/vendor/github.com/containerd/continuity/devices/devices.go b/vendor/github.com/containerd/continuity/devices/devices.go new file mode 100644 index 000000000..708640704 --- /dev/null +++ b/vendor/github.com/containerd/continuity/devices/devices.go @@ -0,0 +1,5 @@ +package devices + +import "fmt" + +var ErrNotSupported = fmt.Errorf("not supported") diff --git a/vendor/github.com/containerd/continuity/devices/devices_unix.go b/vendor/github.com/containerd/continuity/devices/devices_unix.go new file mode 100644 index 000000000..97fe6b19d --- /dev/null +++ b/vendor/github.com/containerd/continuity/devices/devices_unix.go @@ -0,0 +1,58 @@ +// +build linux darwin freebsd solaris + +package devices + +import ( + "fmt" + "os" + "syscall" + + "golang.org/x/sys/unix" +) + +func DeviceInfo(fi os.FileInfo) (uint64, uint64, error) { + sys, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return 0, 0, fmt.Errorf("cannot extract device from os.FileInfo") + } + + dev := uint64(sys.Rdev) + return uint64(unix.Major(dev)), uint64(unix.Minor(dev)), nil +} + +// mknod provides a shortcut for syscall.Mknod +func Mknod(p string, mode os.FileMode, maj, min int) error { + var ( + m = syscallMode(mode.Perm()) + dev uint64 + ) + + if mode&os.ModeDevice != 0 { + dev = unix.Mkdev(uint32(maj), uint32(min)) + + if mode&os.ModeCharDevice != 0 { + m |= unix.S_IFCHR + } else { + m |= unix.S_IFBLK + } + } else if mode&os.ModeNamedPipe != 0 { + m |= unix.S_IFIFO + } + + return unix.Mknod(p, m, int(dev)) +} + +// syscallMode returns the syscall-specific mode bits from Go's portable mode bits. +func syscallMode(i os.FileMode) (o uint32) { + o |= uint32(i.Perm()) + if i&os.ModeSetuid != 0 { + o |= unix.S_ISUID + } + if i&os.ModeSetgid != 0 { + o |= unix.S_ISGID + } + if i&os.ModeSticky != 0 { + o |= unix.S_ISVTX + } + return +} diff --git a/vendor/github.com/containerd/continuity/devices/devices_windows.go b/vendor/github.com/containerd/continuity/devices/devices_windows.go new file mode 100644 index 000000000..6099d1d77 --- /dev/null +++ b/vendor/github.com/containerd/continuity/devices/devices_windows.go @@ -0,0 +1,11 @@ +package devices + +import ( + "os" + + "github.com/pkg/errors" +) + +func DeviceInfo(fi os.FileInfo) (uint64, uint64, error) { + return 0, 0, errors.Wrap(ErrNotSupported, "cannot get device info on windows") +} diff --git a/vendor/github.com/containerd/continuity/digests.go b/vendor/github.com/containerd/continuity/digests.go new file mode 100644 index 000000000..355b08039 --- /dev/null +++ b/vendor/github.com/containerd/continuity/digests.go @@ -0,0 +1,88 @@ +package continuity + +import ( + "fmt" + "io" + "sort" + + "github.com/opencontainers/go-digest" +) + +// Digester produces a digest for a given read stream +type Digester interface { + Digest(io.Reader) (digest.Digest, error) +} + +// ContentProvider produces a read stream for a given digest +type ContentProvider interface { + Reader(digest.Digest) (io.ReadCloser, error) +} + +type simpleDigester struct { + algorithm digest.Algorithm +} + +func (sd simpleDigester) Digest(r io.Reader) (digest.Digest, error) { + digester := sd.algorithm.Digester() + + if _, err := io.Copy(digester.Hash(), r); err != nil { + return "", err + } + + return digester.Digest(), nil +} + +// uniqifyDigests sorts and uniqifies the provided digest, ensuring that the +// digests are not repeated and no two digests with the same algorithm have +// different values. Because a stable sort is used, this has the effect of +// "zipping" digest collections from multiple resources. +func uniqifyDigests(digests ...digest.Digest) ([]digest.Digest, error) { + sort.Stable(digestSlice(digests)) // stable sort is important for the behavior here. + seen := map[digest.Digest]struct{}{} + algs := map[digest.Algorithm][]digest.Digest{} // detect different digests. + + var out []digest.Digest + // uniqify the digests + for _, d := range digests { + if _, ok := seen[d]; ok { + continue + } + + seen[d] = struct{}{} + algs[d.Algorithm()] = append(algs[d.Algorithm()], d) + + if len(algs[d.Algorithm()]) > 1 { + return nil, fmt.Errorf("conflicting digests for %v found", d.Algorithm()) + } + + out = append(out, d) + } + + return out, nil +} + +// digestsMatch compares the two sets of digests to see if they match. +func digestsMatch(as, bs []digest.Digest) bool { + all := append(as, bs...) + + uniqified, err := uniqifyDigests(all...) + if err != nil { + // the only error uniqifyDigests returns is when the digests disagree. + return false + } + + disjoint := len(as) + len(bs) + if len(uniqified) == disjoint { + // if these two sets have the same cardinality, we know both sides + // didn't share any digests. + return false + } + + return true +} + +type digestSlice []digest.Digest + +func (p digestSlice) Len() int { return len(p) } +func (p digestSlice) Less(i, j int) bool { return p[i] < p[j] } +func (p digestSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/vendor/github.com/containerd/continuity/driver/driver.go b/vendor/github.com/containerd/continuity/driver/driver.go new file mode 100644 index 000000000..6a0f76dba --- /dev/null +++ b/vendor/github.com/containerd/continuity/driver/driver.go @@ -0,0 +1,158 @@ +package driver + +import ( + "fmt" + "io" + "os" +) + +var ErrNotSupported = fmt.Errorf("not supported") + +// Driver provides all of the system-level functions in a common interface. +// The context should call these with full paths and should never use the `os` +// package or any other package to access resources on the filesystem. This +// mechanism let's us carefully control access to the context and maintain +// path and resource integrity. It also gives us an interface to reason about +// direct resource access. +// +// Implementations don't need to do much other than meet the interface. For +// example, it is not required to wrap os.FileInfo to return correct paths for +// the call to Name(). +type Driver interface { + // Note that Open() returns a File interface instead of *os.File. This + // is because os.File is a struct, so if Open was to return *os.File, + // the only way to fulfill the interface would be to call os.Open() + Open(path string) (File, error) + OpenFile(path string, flag int, perm os.FileMode) (File, error) + + Stat(path string) (os.FileInfo, error) + Lstat(path string) (os.FileInfo, error) + Readlink(p string) (string, error) + Mkdir(path string, mode os.FileMode) error + Remove(path string) error + + Link(oldname, newname string) error + Lchmod(path string, mode os.FileMode) error + Lchown(path string, uid, gid int64) error + Symlink(oldname, newname string) error + + MkdirAll(path string, perm os.FileMode) error + RemoveAll(path string) error + + // TODO(aaronl): These methods might move outside the main Driver + // interface in the future as more platforms are added. + Mknod(path string, mode os.FileMode, major int, minor int) error + Mkfifo(path string, mode os.FileMode) error +} + +// File is the interface for interacting with files returned by continuity's Open +// This is needed since os.File is a struct, instead of an interface, so it can't +// be used. +type File interface { + io.ReadWriteCloser + io.Seeker + Readdir(n int) ([]os.FileInfo, error) +} + +func NewSystemDriver() (Driver, error) { + // TODO(stevvooe): Consider having this take a "hint" path argument, which + // would be the context root. The hint could be used to resolve required + // filesystem support when assembling the driver to use. + return &driver{}, nil +} + +// XAttrDriver should be implemented on operation systems and filesystems that +// have xattr support for regular files and directories. +type XAttrDriver interface { + // Getxattr returns all of the extended attributes for the file at path. + // Typically, this takes a syscall call to Listxattr and Getxattr. + Getxattr(path string) (map[string][]byte, error) + + // Setxattr sets all of the extended attributes on file at path, following + // any symbolic links, if necessary. All attributes on the target are + // replaced by the values from attr. If the operation fails to set any + // attribute, those already applied will not be rolled back. + Setxattr(path string, attr map[string][]byte) error +} + +// LXAttrDriver should be implemented by drivers on operating systems and +// filesystems that support setting and getting extended attributes on +// symbolic links. If this is not implemented, extended attributes will be +// ignored on symbolic links. +type LXAttrDriver interface { + // LGetxattr returns all of the extended attributes for the file at path + // and does not follow symlinks. Typically, this takes a syscall call to + // Llistxattr and Lgetxattr. + LGetxattr(path string) (map[string][]byte, error) + + // LSetxattr sets all of the extended attributes on file at path, without + // following symbolic links. All attributes on the target are replaced by + // the values from attr. If the operation fails to set any attribute, + // those already applied will not be rolled back. + LSetxattr(path string, attr map[string][]byte) error +} + +type DeviceInfoDriver interface { + DeviceInfo(fi os.FileInfo) (maj uint64, min uint64, err error) +} + +// driver is a simple default implementation that sends calls out to the "os" +// package. Extend the "driver" type in system-specific files to add support, +// such as xattrs, which can add support at compile time. +type driver struct{} + +var _ File = &os.File{} + +// LocalDriver is the exported Driver struct for convenience. +var LocalDriver Driver = &driver{} + +func (d *driver) Open(p string) (File, error) { + return os.Open(p) +} + +func (d *driver) OpenFile(path string, flag int, perm os.FileMode) (File, error) { + return os.OpenFile(path, flag, perm) +} + +func (d *driver) Stat(p string) (os.FileInfo, error) { + return os.Stat(p) +} + +func (d *driver) Lstat(p string) (os.FileInfo, error) { + return os.Lstat(p) +} + +func (d *driver) Mkdir(p string, mode os.FileMode) error { + return os.Mkdir(p, mode) +} + +// Remove is used to unlink files and remove directories. +// This is following the golang os package api which +// combines the operations into a higher level Remove +// function. If explicit unlinking or directory removal +// to mirror system call is required, they should be +// split up at that time. +func (d *driver) Remove(path string) error { + return os.Remove(path) +} + +func (d *driver) Link(oldname, newname string) error { + return os.Link(oldname, newname) +} + +func (d *driver) Lchown(name string, uid, gid int64) error { + // TODO: error out if uid excesses int bit width? + return os.Lchown(name, int(uid), int(gid)) +} + +func (d *driver) Symlink(oldname, newname string) error { + return os.Symlink(oldname, newname) +} + +func (d *driver) MkdirAll(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) +} + +func (d *driver) RemoveAll(path string) error { + return os.RemoveAll(path) +} diff --git a/vendor/github.com/containerd/continuity/driver/driver_unix.go b/vendor/github.com/containerd/continuity/driver/driver_unix.go new file mode 100644 index 000000000..c7d4e6ba1 --- /dev/null +++ b/vendor/github.com/containerd/continuity/driver/driver_unix.go @@ -0,0 +1,114 @@ +// +build linux darwin freebsd solaris + +package driver + +import ( + "errors" + "fmt" + "os" + "sort" + + "github.com/containerd/continuity/devices" + "github.com/containerd/continuity/sysx" +) + +func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error { + return devices.Mknod(path, mode, major, minor) +} + +func (d *driver) Mkfifo(path string, mode os.FileMode) error { + if mode&os.ModeNamedPipe == 0 { + return errors.New("mode passed to Mkfifo does not have the named pipe bit set") + } + // mknod with a mode that has ModeNamedPipe set creates a fifo, not a + // device. + return devices.Mknod(path, mode, 0, 0) +} + +// Getxattr returns all of the extended attributes for the file at path p. +func (d *driver) Getxattr(p string) (map[string][]byte, error) { + xattrs, err := sysx.Listxattr(p) + if err != nil { + return nil, fmt.Errorf("listing %s xattrs: %v", p, err) + } + + sort.Strings(xattrs) + m := make(map[string][]byte, len(xattrs)) + + for _, attr := range xattrs { + value, err := sysx.Getxattr(p, attr) + if err != nil { + return nil, fmt.Errorf("getting %q xattr on %s: %v", attr, p, err) + } + + // NOTE(stevvooe): This append/copy tricky relies on unique + // xattrs. Break this out into an alloc/copy if xattrs are no + // longer unique. + m[attr] = append(m[attr], value...) + } + + return m, nil +} + +// Setxattr sets all of the extended attributes on file at path, following +// any symbolic links, if necessary. All attributes on the target are +// replaced by the values from attr. If the operation fails to set any +// attribute, those already applied will not be rolled back. +func (d *driver) Setxattr(path string, attrMap map[string][]byte) error { + for attr, value := range attrMap { + if err := sysx.Setxattr(path, attr, value, 0); err != nil { + return fmt.Errorf("error setting xattr %q on %s: %v", attr, path, err) + } + } + + return nil +} + +// LGetxattr returns all of the extended attributes for the file at path p +// not following symbolic links. +func (d *driver) LGetxattr(p string) (map[string][]byte, error) { + xattrs, err := sysx.LListxattr(p) + if err != nil { + return nil, fmt.Errorf("listing %s xattrs: %v", p, err) + } + + sort.Strings(xattrs) + m := make(map[string][]byte, len(xattrs)) + + for _, attr := range xattrs { + value, err := sysx.LGetxattr(p, attr) + if err != nil { + return nil, fmt.Errorf("getting %q xattr on %s: %v", attr, p, err) + } + + // NOTE(stevvooe): This append/copy tricky relies on unique + // xattrs. Break this out into an alloc/copy if xattrs are no + // longer unique. + m[attr] = append(m[attr], value...) + } + + return m, nil +} + +// LSetxattr sets all of the extended attributes on file at path, not +// following any symbolic links. All attributes on the target are +// replaced by the values from attr. If the operation fails to set any +// attribute, those already applied will not be rolled back. +func (d *driver) LSetxattr(path string, attrMap map[string][]byte) error { + for attr, value := range attrMap { + if err := sysx.LSetxattr(path, attr, value, 0); err != nil { + return fmt.Errorf("error setting xattr %q on %s: %v", attr, path, err) + } + } + + return nil +} + +func (d *driver) DeviceInfo(fi os.FileInfo) (maj uint64, min uint64, err error) { + return devices.DeviceInfo(fi) +} + +// Readlink was forked on Windows to fix a Golang bug, use the "os" package here +func (d *driver) Readlink(p string) (string, error) { + return os.Readlink(p) +} diff --git a/vendor/github.com/containerd/continuity/driver/driver_windows.go b/vendor/github.com/containerd/continuity/driver/driver_windows.go new file mode 100644 index 000000000..21c9cf961 --- /dev/null +++ b/vendor/github.com/containerd/continuity/driver/driver_windows.go @@ -0,0 +1,28 @@ +package driver + +import ( + "os" + + "github.com/containerd/continuity/sysx" + "github.com/pkg/errors" +) + +func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error { + return errors.Wrap(ErrNotSupported, "cannot create device node on Windows") +} + +func (d *driver) Mkfifo(path string, mode os.FileMode) error { + return errors.Wrap(ErrNotSupported, "cannot create fifo on Windows") +} + +// Lchmod changes the mode of an file not following symlinks. +func (d *driver) Lchmod(path string, mode os.FileMode) (err error) { + // TODO: Use Window's equivalent + return os.Chmod(path, mode) +} + +// Readlink is forked in order to support Volume paths which are used +// in container layers. +func (d *driver) Readlink(p string) (string, error) { + return sysx.Readlink(p) +} diff --git a/vendor/github.com/containerd/continuity/driver/lchmod_linux.go b/vendor/github.com/containerd/continuity/driver/lchmod_linux.go new file mode 100644 index 000000000..39ffe9cc3 --- /dev/null +++ b/vendor/github.com/containerd/continuity/driver/lchmod_linux.go @@ -0,0 +1,19 @@ +package driver + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// Lchmod changes the mode of a file not following symlinks. +func (d *driver) Lchmod(path string, mode os.FileMode) error { + // On Linux, file mode is not supported for symlinks, + // and fchmodat() does not support AT_SYMLINK_NOFOLLOW, + // so symlinks need to be skipped entirely. + if st, err := os.Stat(path); err == nil && st.Mode()&os.ModeSymlink != 0 { + return nil + } + + return unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), 0) +} diff --git a/vendor/github.com/containerd/continuity/driver/lchmod_unix.go b/vendor/github.com/containerd/continuity/driver/lchmod_unix.go new file mode 100644 index 000000000..1b539f78e --- /dev/null +++ b/vendor/github.com/containerd/continuity/driver/lchmod_unix.go @@ -0,0 +1,14 @@ +// +build darwin freebsd solaris + +package driver + +import ( + "os" + + "golang.org/x/sys/unix" +) + +// Lchmod changes the mode of a file not following symlinks. +func (d *driver) Lchmod(path string, mode os.FileMode) error { + return unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), unix.AT_SYMLINK_NOFOLLOW) +} diff --git a/vendor/github.com/containerd/continuity/driver/utils.go b/vendor/github.com/containerd/continuity/driver/utils.go new file mode 100644 index 000000000..9e0edd7bc --- /dev/null +++ b/vendor/github.com/containerd/continuity/driver/utils.go @@ -0,0 +1,74 @@ +package driver + +import ( + "io" + "io/ioutil" + "os" + "sort" +) + +// ReadFile works the same as ioutil.ReadFile with the Driver abstraction +func ReadFile(r Driver, filename string) ([]byte, error) { + f, err := r.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + + data, err := ioutil.ReadAll(f) + if err != nil { + return nil, err + } + + return data, nil +} + +// WriteFile works the same as ioutil.WriteFile with the Driver abstraction +func WriteFile(r Driver, filename string, data []byte, perm os.FileMode) error { + f, err := r.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) + if err != nil { + return err + } + defer f.Close() + + n, err := f.Write(data) + if err != nil { + return err + } else if n != len(data) { + return io.ErrShortWrite + } + + return nil +} + +// ReadDir works the same as ioutil.ReadDir with the Driver abstraction +func ReadDir(r Driver, dirname string) ([]os.FileInfo, error) { + f, err := r.Open(dirname) + if err != nil { + return nil, err + } + defer f.Close() + + dirs, err := f.Readdir(-1) + if err != nil { + return nil, err + } + + sort.Sort(fileInfos(dirs)) + return dirs, nil +} + +// Simple implementation of the sort.Interface for os.FileInfo +type fileInfos []os.FileInfo + +func (fis fileInfos) Len() int { + return len(fis) +} + +func (fis fileInfos) Less(i, j int) bool { + return fis[i].Name() < fis[j].Name() +} + +func (fis fileInfos) Swap(i, j int) { + fis[i], fis[j] = fis[j], fis[i] +} diff --git a/vendor/github.com/containerd/continuity/fs/du.go b/vendor/github.com/containerd/continuity/fs/du.go index 26f533315..f8fc9a994 100644 --- a/vendor/github.com/containerd/continuity/fs/du.go +++ b/vendor/github.com/containerd/continuity/fs/du.go @@ -10,8 +10,8 @@ type Usage struct { // DiskUsage counts the number of inodes and disk usage for the resources under // path. -func DiskUsage(roots ...string) (Usage, error) { - return diskUsage(roots...) +func DiskUsage(ctx context.Context, roots ...string) (Usage, error) { + return diskUsage(ctx, roots...) } // DiffUsage counts the numbers of inodes and disk usage in the diff --git a/vendor/github.com/containerd/continuity/fs/du_unix.go b/vendor/github.com/containerd/continuity/fs/du_unix.go index fe3426d27..9f6bc55fd 100644 --- a/vendor/github.com/containerd/continuity/fs/du_unix.go +++ b/vendor/github.com/containerd/continuity/fs/du_unix.go @@ -24,7 +24,7 @@ func newInode(stat *syscall.Stat_t) inode { } } -func diskUsage(roots ...string) (Usage, error) { +func diskUsage(ctx context.Context, roots ...string) (Usage, error) { var ( size int64 @@ -37,6 +37,12 @@ func diskUsage(roots ...string) (Usage, error) { return err } + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + inoKey := newInode(fi.Sys().(*syscall.Stat_t)) if _, ok := inodes[inoKey]; !ok { inodes[inoKey] = struct{}{} diff --git a/vendor/github.com/containerd/continuity/fs/du_windows.go b/vendor/github.com/containerd/continuity/fs/du_windows.go index 3f852fc15..faa443fed 100644 --- a/vendor/github.com/containerd/continuity/fs/du_windows.go +++ b/vendor/github.com/containerd/continuity/fs/du_windows.go @@ -8,7 +8,7 @@ import ( "path/filepath" ) -func diskUsage(roots ...string) (Usage, error) { +func diskUsage(ctx context.Context, roots ...string) (Usage, error) { var ( size int64 ) @@ -21,6 +21,12 @@ func diskUsage(roots ...string) (Usage, error) { return err } + select { + case <-ctx.Done(): + return ctx.Err() + default: + } + size += fi.Size() return nil }); err != nil { diff --git a/vendor/github.com/containerd/continuity/groups_unix.go b/vendor/github.com/containerd/continuity/groups_unix.go new file mode 100644 index 000000000..e15c14ff8 --- /dev/null +++ b/vendor/github.com/containerd/continuity/groups_unix.go @@ -0,0 +1,113 @@ +package continuity + +import ( + "bufio" + "fmt" + "io" + "os" + "strconv" + "strings" +) + +// TODO(stevvooe): This needs a lot of work before we can call it useful. + +type groupIndex struct { + byName map[string]*group + byGID map[int]*group +} + +func getGroupIndex() (*groupIndex, error) { + f, err := os.Open("/etc/group") + if err != nil { + return nil, err + } + defer f.Close() + + groups, err := parseGroups(f) + if err != nil { + return nil, err + } + + return newGroupIndex(groups), nil +} + +func newGroupIndex(groups []group) *groupIndex { + gi := &groupIndex{ + byName: make(map[string]*group), + byGID: make(map[int]*group), + } + + for i, group := range groups { + gi.byGID[group.gid] = &groups[i] + gi.byName[group.name] = &groups[i] + } + + return gi +} + +type group struct { + name string + gid int + members []string +} + +func getGroupName(gid int) (string, error) { + f, err := os.Open("/etc/group") + if err != nil { + return "", err + } + defer f.Close() + + groups, err := parseGroups(f) + if err != nil { + return "", err + } + + for _, group := range groups { + if group.gid == gid { + return group.name, nil + } + } + + return "", fmt.Errorf("no group for gid") +} + +// parseGroups parses an /etc/group file for group names, ids and membership. +// This is unix specific. +func parseGroups(rd io.Reader) ([]group, error) { + var groups []group + scanner := bufio.NewScanner(rd) + + for scanner.Scan() { + if strings.HasPrefix(scanner.Text(), "#") { + continue // skip comment + } + + parts := strings.SplitN(scanner.Text(), ":", 4) + + if len(parts) != 4 { + return nil, fmt.Errorf("bad entry: %q", scanner.Text()) + } + + name, _, sgid, smembers := parts[0], parts[1], parts[2], parts[3] + + gid, err := strconv.Atoi(sgid) + if err != nil { + return nil, fmt.Errorf("bad gid: %q", gid) + } + + members := strings.Split(smembers, ",") + + groups = append(groups, group{ + name: name, + gid: gid, + members: members, + }) + } + + if scanner.Err() != nil { + return nil, scanner.Err() + } + + return groups, nil +} diff --git a/vendor/github.com/containerd/continuity/hardlinks.go b/vendor/github.com/containerd/continuity/hardlinks.go new file mode 100644 index 000000000..8b39bd061 --- /dev/null +++ b/vendor/github.com/containerd/continuity/hardlinks.go @@ -0,0 +1,57 @@ +package continuity + +import ( + "fmt" + "os" +) + +var ( + errNotAHardLink = fmt.Errorf("invalid hardlink") +) + +type hardlinkManager struct { + hardlinks map[hardlinkKey][]Resource +} + +func newHardlinkManager() *hardlinkManager { + return &hardlinkManager{ + hardlinks: map[hardlinkKey][]Resource{}, + } +} + +// Add attempts to add the resource to the hardlink manager. If the resource +// cannot be considered as a hardlink candidate, errNotAHardLink is returned. +func (hlm *hardlinkManager) Add(fi os.FileInfo, resource Resource) error { + if _, ok := resource.(Hardlinkable); !ok { + return errNotAHardLink + } + + key, err := newHardlinkKey(fi) + if err != nil { + return err + } + + hlm.hardlinks[key] = append(hlm.hardlinks[key], resource) + + return nil +} + +// Merge processes the current state of the hardlink manager and merges any +// shared nodes into hardlinked resources. +func (hlm *hardlinkManager) Merge() ([]Resource, error) { + var resources []Resource + for key, linked := range hlm.hardlinks { + if len(linked) < 1 { + return nil, fmt.Errorf("no hardlink entrys for dev, inode pair: %#v", key) + } + + merged, err := Merge(linked...) + if err != nil { + return nil, fmt.Errorf("error merging hardlink: %v", err) + } + + resources = append(resources, merged) + } + + return resources, nil +} diff --git a/vendor/github.com/containerd/continuity/hardlinks_unix.go b/vendor/github.com/containerd/continuity/hardlinks_unix.go new file mode 100644 index 000000000..1d81a3f96 --- /dev/null +++ b/vendor/github.com/containerd/continuity/hardlinks_unix.go @@ -0,0 +1,36 @@ +// +build linux darwin freebsd solaris + +package continuity + +import ( + "fmt" + "os" + "syscall" +) + +// hardlinkKey provides a tuple-key for managing hardlinks. This is system- +// specific. +type hardlinkKey struct { + dev uint64 + inode uint64 +} + +// newHardlinkKey returns a hardlink key for the provided file info. If the +// resource does not represent a possible hardlink, errNotAHardLink will be +// returned. +func newHardlinkKey(fi os.FileInfo) (hardlinkKey, error) { + sys, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + return hardlinkKey{}, fmt.Errorf("cannot resolve (*syscall.Stat_t) from os.FileInfo") + } + + if sys.Nlink < 2 { + // NOTE(stevvooe): This is not always true for all filesystems. We + // should somehow detect this and provided a slow "polyfill" that + // leverages os.SameFile if we detect a filesystem where link counts + // is not really supported. + return hardlinkKey{}, errNotAHardLink + } + + return hardlinkKey{dev: uint64(sys.Dev), inode: uint64(sys.Ino)}, nil +} diff --git a/vendor/github.com/containerd/continuity/hardlinks_windows.go b/vendor/github.com/containerd/continuity/hardlinks_windows.go new file mode 100644 index 000000000..be516c560 --- /dev/null +++ b/vendor/github.com/containerd/continuity/hardlinks_windows.go @@ -0,0 +1,12 @@ +package continuity + +import "os" + +type hardlinkKey struct{} + +func newHardlinkKey(fi os.FileInfo) (hardlinkKey, error) { + // NOTE(stevvooe): Obviously, this is not yet implemented. However, the + // makings of an implementation are available in src/os/types_windows.go. More + // investigation needs to be done to figure out exactly how to do this. + return hardlinkKey{}, errNotAHardLink +} diff --git a/vendor/github.com/containerd/continuity/ioutils.go b/vendor/github.com/containerd/continuity/ioutils.go new file mode 100644 index 000000000..3a25bde39 --- /dev/null +++ b/vendor/github.com/containerd/continuity/ioutils.go @@ -0,0 +1,47 @@ +package continuity + +import ( + "bytes" + "io" + "io/ioutil" + "os" + "path/filepath" +) + +// AtomicWriteFile atomically writes data to a file by first writing to a +// temp file and calling rename. +func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { + buf := bytes.NewBuffer(data) + return atomicWriteFile(filename, buf, int64(len(data)), perm) +} + +// atomicWriteFile writes data to a file by first writing to a temp +// file and calling rename. +func atomicWriteFile(filename string, r io.Reader, dataSize int64, perm os.FileMode) error { + f, err := ioutil.TempFile(filepath.Dir(filename), ".tmp-"+filepath.Base(filename)) + if err != nil { + return err + } + err = os.Chmod(f.Name(), perm) + if err != nil { + f.Close() + return err + } + n, err := io.Copy(f, r) + if err == nil && n < dataSize { + f.Close() + return io.ErrShortWrite + } + if err != nil { + f.Close() + return err + } + if err := f.Sync(); err != nil { + f.Close() + return err + } + if err := f.Close(); err != nil { + return err + } + return os.Rename(f.Name(), filename) +} diff --git a/vendor/github.com/containerd/continuity/manifest.go b/vendor/github.com/containerd/continuity/manifest.go new file mode 100644 index 000000000..f704f048b --- /dev/null +++ b/vendor/github.com/containerd/continuity/manifest.go @@ -0,0 +1,144 @@ +package continuity + +import ( + "fmt" + "io" + "log" + "os" + "sort" + + pb "github.com/containerd/continuity/proto" + "github.com/golang/protobuf/proto" +) + +// Manifest provides the contents of a manifest. Users of this struct should +// not typically modify any fields directly. +type Manifest struct { + // Resources specifies all the resources for a manifest in order by path. + Resources []Resource +} + +func Unmarshal(p []byte) (*Manifest, error) { + var bm pb.Manifest + + if err := proto.Unmarshal(p, &bm); err != nil { + return nil, err + } + + var m Manifest + for _, b := range bm.Resource { + r, err := fromProto(b) + if err != nil { + return nil, err + } + + m.Resources = append(m.Resources, r) + } + + return &m, nil +} + +func Marshal(m *Manifest) ([]byte, error) { + var bm pb.Manifest + for _, resource := range m.Resources { + bm.Resource = append(bm.Resource, toProto(resource)) + } + + return proto.Marshal(&bm) +} + +func MarshalText(w io.Writer, m *Manifest) error { + var bm pb.Manifest + for _, resource := range m.Resources { + bm.Resource = append(bm.Resource, toProto(resource)) + } + + return proto.MarshalText(w, &bm) +} + +// BuildManifest creates the manifest for the given context +func BuildManifest(ctx Context) (*Manifest, error) { + resourcesByPath := map[string]Resource{} + hardlinks := newHardlinkManager() + + if err := ctx.Walk(func(p string, fi os.FileInfo, err error) error { + if err != nil { + return fmt.Errorf("error walking %s: %v", p, err) + } + + if p == string(os.PathSeparator) { + // skip root + return nil + } + + resource, err := ctx.Resource(p, fi) + if err != nil { + if err == ErrNotFound { + return nil + } + log.Printf("error getting resource %q: %v", p, err) + return err + } + + // add to the hardlink manager + if err := hardlinks.Add(fi, resource); err == nil { + // Resource has been accepted by hardlink manager so we don't add + // it to the resourcesByPath until we merge at the end. + return nil + } else if err != errNotAHardLink { + // handle any other case where we have a proper error. + return fmt.Errorf("adding hardlink %s: %v", p, err) + } + + resourcesByPath[p] = resource + + return nil + }); err != nil { + return nil, err + } + + // merge and post-process the hardlinks. + hardlinked, err := hardlinks.Merge() + if err != nil { + return nil, err + } + + for _, resource := range hardlinked { + resourcesByPath[resource.Path()] = resource + } + + var resources []Resource + for _, resource := range resourcesByPath { + resources = append(resources, resource) + } + + sort.Stable(ByPath(resources)) + + return &Manifest{ + Resources: resources, + }, nil +} + +// VerifyManifest verifies all the resources in a manifest +// against files from the given context. +func VerifyManifest(ctx Context, manifest *Manifest) error { + for _, resource := range manifest.Resources { + if err := ctx.Verify(resource); err != nil { + return err + } + } + + return nil +} + +// ApplyManifest applies on the resources in a manifest to +// the given context. +func ApplyManifest(ctx Context, manifest *Manifest) error { + for _, resource := range manifest.Resources { + if err := ctx.Apply(resource); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/containerd/continuity/proto/gen.go b/vendor/github.com/containerd/continuity/proto/gen.go new file mode 100644 index 000000000..8f26ff501 --- /dev/null +++ b/vendor/github.com/containerd/continuity/proto/gen.go @@ -0,0 +1,3 @@ +package proto + +//go:generate protoc --go_out=. manifest.proto diff --git a/vendor/github.com/containerd/continuity/proto/manifest.pb.go b/vendor/github.com/containerd/continuity/proto/manifest.pb.go new file mode 100644 index 000000000..243177662 --- /dev/null +++ b/vendor/github.com/containerd/continuity/proto/manifest.pb.go @@ -0,0 +1,181 @@ +// Code generated by protoc-gen-go. +// source: manifest.proto +// DO NOT EDIT! + +/* +Package proto is a generated protocol buffer package. + +It is generated from these files: + manifest.proto + +It has these top-level messages: + Manifest + Resource + XAttr + ADSEntry +*/ +package proto + +import proto1 "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto1.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto1.ProtoPackageIsVersion2 // please upgrade the proto package + +// Manifest specifies the entries in a container bundle, keyed and sorted by +// path. +type Manifest struct { + Resource []*Resource `protobuf:"bytes,1,rep,name=resource" json:"resource,omitempty"` +} + +func (m *Manifest) Reset() { *m = Manifest{} } +func (m *Manifest) String() string { return proto1.CompactTextString(m) } +func (*Manifest) ProtoMessage() {} +func (*Manifest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } + +func (m *Manifest) GetResource() []*Resource { + if m != nil { + return m.Resource + } + return nil +} + +type Resource struct { + // Path specifies the path from the bundle root. If more than one + // path is present, the entry may represent a hardlink, rather than using + // a link target. The path format is operating system specific. + Path []string `protobuf:"bytes,1,rep,name=path" json:"path,omitempty"` + // Uid specifies the user id for the resource. + Uid int64 `protobuf:"varint,2,opt,name=uid" json:"uid,omitempty"` + // Gid specifies the group id for the resource. + Gid int64 `protobuf:"varint,3,opt,name=gid" json:"gid,omitempty"` + // user and group are not currently used but their field numbers have been + // reserved for future use. As such, they are marked as deprecated. + User string `protobuf:"bytes,4,opt,name=user" json:"user,omitempty"` + Group string `protobuf:"bytes,5,opt,name=group" json:"group,omitempty"` + // Mode defines the file mode and permissions. We've used the same + // bit-packing from Go's os package, + // http://golang.org/pkg/os/#FileMode, since they've done the work of + // creating a cross-platform layout. + Mode uint32 `protobuf:"varint,6,opt,name=mode" json:"mode,omitempty"` + // Size specifies the size in bytes of the resource. This is only valid + // for regular files. + Size uint64 `protobuf:"varint,7,opt,name=size" json:"size,omitempty"` + // Digest specifies the content digest of the target file. Only valid for + // regular files. The strings are formatted in OCI style, i.e. :. + // For detailed information about the format, please refer to OCI Image Spec: + // https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests-and-verification + // The digests are sorted in lexical order and implementations may choose + // which algorithms they prefer. + Digest []string `protobuf:"bytes,8,rep,name=digest" json:"digest,omitempty"` + // Target defines the target of a hard or soft link. Absolute links start + // with a slash and specify the resource relative to the bundle root. + // Relative links do not start with a slash and are relative to the + // resource path. + Target string `protobuf:"bytes,9,opt,name=target" json:"target,omitempty"` + // Major specifies the major device number for character and block devices. + Major uint64 `protobuf:"varint,10,opt,name=major" json:"major,omitempty"` + // Minor specifies the minor device number for character and block devices. + Minor uint64 `protobuf:"varint,11,opt,name=minor" json:"minor,omitempty"` + // Xattr provides storage for extended attributes for the target resource. + Xattr []*XAttr `protobuf:"bytes,12,rep,name=xattr" json:"xattr,omitempty"` + // Ads stores one or more alternate data streams for the target resource. + Ads []*ADSEntry `protobuf:"bytes,13,rep,name=ads" json:"ads,omitempty"` +} + +func (m *Resource) Reset() { *m = Resource{} } +func (m *Resource) String() string { return proto1.CompactTextString(m) } +func (*Resource) ProtoMessage() {} +func (*Resource) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } + +func (m *Resource) GetXattr() []*XAttr { + if m != nil { + return m.Xattr + } + return nil +} + +func (m *Resource) GetAds() []*ADSEntry { + if m != nil { + return m.Ads + } + return nil +} + +// XAttr encodes extended attributes for a resource. +type XAttr struct { + // Name specifies the attribute name. + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + // Data specifies the associated data for the attribute. + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` +} + +func (m *XAttr) Reset() { *m = XAttr{} } +func (m *XAttr) String() string { return proto1.CompactTextString(m) } +func (*XAttr) ProtoMessage() {} +func (*XAttr) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } + +// ADSEntry encodes information for a Windows Alternate Data Stream. +type ADSEntry struct { + // Name specifices the stream name. + Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` + // Data specifies the stream data. + // See also the description about the digest below. + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + // Digest is a CAS representation of the stream data. + // + // At least one of data or digest MUST be specified, and either one of them + // SHOULD be specified. + // + // How to access the actual data using the digest is implementation-specific, + // and implementations can choose not to implement digest. + // So, digest SHOULD be used only when the stream data is large. + Digest string `protobuf:"bytes,3,opt,name=digest" json:"digest,omitempty"` +} + +func (m *ADSEntry) Reset() { *m = ADSEntry{} } +func (m *ADSEntry) String() string { return proto1.CompactTextString(m) } +func (*ADSEntry) ProtoMessage() {} +func (*ADSEntry) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } + +func init() { + proto1.RegisterType((*Manifest)(nil), "proto.Manifest") + proto1.RegisterType((*Resource)(nil), "proto.Resource") + proto1.RegisterType((*XAttr)(nil), "proto.XAttr") + proto1.RegisterType((*ADSEntry)(nil), "proto.ADSEntry") +} + +func init() { proto1.RegisterFile("manifest.proto", fileDescriptor0) } + +var fileDescriptor0 = []byte{ + // 317 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x8c, 0x90, 0x4f, 0x4b, 0xf3, 0x40, + 0x10, 0xc6, 0x49, 0x93, 0xf4, 0x4d, 0xa7, 0xed, 0xab, 0x2c, 0x52, 0xe6, 0x18, 0x73, 0x0a, 0x08, + 0x15, 0xf4, 0xe0, 0xb9, 0xa2, 0x17, 0xc1, 0xcb, 0x7a, 0xf1, 0xba, 0xba, 0x6b, 0x5c, 0x21, 0xd9, + 0xb0, 0xd9, 0x80, 0xfa, 0xe5, 0xfc, 0x6a, 0x32, 0xb3, 0x69, 0xd1, 0x9b, 0xa7, 0x3c, 0xcf, 0x6f, + 0xfe, 0x64, 0xf6, 0x81, 0xff, 0xad, 0xea, 0xec, 0x8b, 0x19, 0xc2, 0xb6, 0xf7, 0x2e, 0x38, 0x91, + 0xf3, 0xa7, 0xba, 0x82, 0xe2, 0x7e, 0x2a, 0x88, 0x33, 0x28, 0xbc, 0x19, 0xdc, 0xe8, 0x9f, 0x0d, + 0x26, 0x65, 0x5a, 0x2f, 0x2f, 0x8e, 0x62, 0xf3, 0x56, 0x4e, 0x58, 0x1e, 0x1a, 0xaa, 0xaf, 0x19, + 0x14, 0x7b, 0x2c, 0x04, 0x64, 0xbd, 0x0a, 0xaf, 0x3c, 0xb5, 0x90, 0xac, 0xc5, 0x31, 0xa4, 0xa3, + 0xd5, 0x38, 0x2b, 0x93, 0x3a, 0x95, 0x24, 0x89, 0x34, 0x56, 0x63, 0x1a, 0x49, 0x63, 0xb5, 0xd8, + 0x40, 0x36, 0x0e, 0xc6, 0x63, 0x56, 0x26, 0xf5, 0xe2, 0x7a, 0x86, 0x89, 0x64, 0x2f, 0x10, 0xf2, + 0xc6, 0xbb, 0xb1, 0xc7, 0xfc, 0x50, 0x88, 0x80, 0xfe, 0xd4, 0x3a, 0x6d, 0x70, 0x5e, 0x26, 0xf5, + 0x5a, 0xb2, 0x26, 0x36, 0xd8, 0x4f, 0x83, 0xff, 0xca, 0xa4, 0xce, 0x24, 0x6b, 0xb1, 0x81, 0xb9, + 0xb6, 0x8d, 0x19, 0x02, 0x16, 0x7c, 0xd3, 0xe4, 0x88, 0x07, 0xe5, 0x1b, 0x13, 0x70, 0x41, 0xab, + 0xe5, 0xe4, 0xc4, 0x09, 0xe4, 0xad, 0x7a, 0x73, 0x1e, 0x81, 0x97, 0x44, 0xc3, 0xd4, 0x76, 0xce, + 0xe3, 0x72, 0xa2, 0x64, 0x44, 0x05, 0xf9, 0xbb, 0x0a, 0xc1, 0xe3, 0x8a, 0x43, 0x5a, 0x4d, 0x21, + 0x3d, 0xee, 0x42, 0xf0, 0x32, 0x96, 0xc4, 0x29, 0xa4, 0x4a, 0x0f, 0xb8, 0xfe, 0x15, 0xe3, 0xee, + 0xe6, 0xe1, 0xb6, 0x0b, 0xfe, 0x43, 0x52, 0xad, 0x3a, 0x87, 0x9c, 0x47, 0xe8, 0xfe, 0x4e, 0xb5, + 0x94, 0x39, 0x5d, 0xc4, 0x9a, 0x98, 0x56, 0x41, 0x71, 0x7c, 0x2b, 0xc9, 0xba, 0xba, 0x83, 0x62, + 0xbf, 0xe1, 0xaf, 0x33, 0x3f, 0x72, 0x48, 0xe3, 0x7b, 0xa3, 0x7b, 0x9a, 0xf3, 0x45, 0x97, 0xdf, + 0x01, 0x00, 0x00, 0xff, 0xff, 0xef, 0x27, 0x99, 0xf7, 0x17, 0x02, 0x00, 0x00, +} diff --git a/vendor/github.com/containerd/continuity/proto/manifest.proto b/vendor/github.com/containerd/continuity/proto/manifest.proto new file mode 100644 index 000000000..66ef80f05 --- /dev/null +++ b/vendor/github.com/containerd/continuity/proto/manifest.proto @@ -0,0 +1,97 @@ +syntax = "proto3"; + +package proto; + +// Manifest specifies the entries in a container bundle, keyed and sorted by +// path. +message Manifest { + repeated Resource resource = 1; +} + +message Resource { + // Path specifies the path from the bundle root. If more than one + // path is present, the entry may represent a hardlink, rather than using + // a link target. The path format is operating system specific. + repeated string path = 1; + + // NOTE(stevvooe): Need to define clear precedence for user/group/uid/gid precedence. + + // Uid specifies the user id for the resource. + int64 uid = 2; + + // Gid specifies the group id for the resource. + int64 gid = 3; + + // user and group are not currently used but their field numbers have been + // reserved for future use. As such, they are marked as deprecated. + string user = 4 [deprecated=true]; // "deprecated" stands for "reserved" here + string group = 5 [deprecated=true]; // "deprecated" stands for "reserved" here + + // Mode defines the file mode and permissions. We've used the same + // bit-packing from Go's os package, + // http://golang.org/pkg/os/#FileMode, since they've done the work of + // creating a cross-platform layout. + uint32 mode = 6; + + // NOTE(stevvooe): Beyond here, we start defining type specific fields. + + // Size specifies the size in bytes of the resource. This is only valid + // for regular files. + uint64 size = 7; + + // Digest specifies the content digest of the target file. Only valid for + // regular files. The strings are formatted in OCI style, i.e. :. + // For detailed information about the format, please refer to OCI Image Spec: + // https://github.com/opencontainers/image-spec/blob/master/descriptor.md#digests-and-verification + // The digests are sorted in lexical order and implementations may choose + // which algorithms they prefer. + repeated string digest = 8; + + // Target defines the target of a hard or soft link. Absolute links start + // with a slash and specify the resource relative to the bundle root. + // Relative links do not start with a slash and are relative to the + // resource path. + string target = 9; + + // Major specifies the major device number for character and block devices. + uint64 major = 10; + + // Minor specifies the minor device number for character and block devices. + uint64 minor = 11; + + // Xattr provides storage for extended attributes for the target resource. + repeated XAttr xattr = 12; + + // Ads stores one or more alternate data streams for the target resource. + repeated ADSEntry ads = 13; + +} + +// XAttr encodes extended attributes for a resource. +message XAttr { + // Name specifies the attribute name. + string name = 1; + + // Data specifies the associated data for the attribute. + bytes data = 2; +} + +// ADSEntry encodes information for a Windows Alternate Data Stream. +message ADSEntry { + // Name specifices the stream name. + string name = 1; + + // Data specifies the stream data. + // See also the description about the digest below. + bytes data = 2; + + // Digest is a CAS representation of the stream data. + // + // At least one of data or digest MUST be specified, and either one of them + // SHOULD be specified. + // + // How to access the actual data using the digest is implementation-specific, + // and implementations can choose not to implement digest. + // So, digest SHOULD be used only when the stream data is large. + string digest = 3; +} diff --git a/vendor/github.com/containerd/continuity/resource.go b/vendor/github.com/containerd/continuity/resource.go new file mode 100644 index 000000000..3643effb3 --- /dev/null +++ b/vendor/github.com/containerd/continuity/resource.go @@ -0,0 +1,574 @@ +package continuity + +import ( + "errors" + "fmt" + "os" + "reflect" + "sort" + + pb "github.com/containerd/continuity/proto" + "github.com/opencontainers/go-digest" +) + +// TODO(stevvooe): A record based model, somewhat sketched out at the bottom +// of this file, will be more flexible. Another possibly is to tie the package +// interface directly to the protobuf type. This will have efficiency +// advantages at the cost coupling the nasty codegen types to the exported +// interface. + +type Resource interface { + // Path provides the primary resource path relative to the bundle root. In + // cases where resources have more than one path, such as with hard links, + // this will return the primary path, which is often just the first entry. + Path() string + + // Mode returns the + Mode() os.FileMode + + UID() int64 + GID() int64 +} + +// ByPath provides the canonical sort order for a set of resources. Use with +// sort.Stable for deterministic sorting. +type ByPath []Resource + +func (bp ByPath) Len() int { return len(bp) } +func (bp ByPath) Swap(i, j int) { bp[i], bp[j] = bp[j], bp[i] } +func (bp ByPath) Less(i, j int) bool { return bp[i].Path() < bp[j].Path() } + +type XAttrer interface { + XAttrs() map[string][]byte +} + +// Hardlinkable is an interface that a resource type satisfies if it can be a +// hardlink target. +type Hardlinkable interface { + // Paths returns all paths of the resource, including the primary path + // returned by Resource.Path. If len(Paths()) > 1, the resource is a hard + // link. + Paths() []string +} + +type RegularFile interface { + Resource + XAttrer + Hardlinkable + + Size() int64 + Digests() []digest.Digest +} + +// Merge two or more Resources into new file. Typically, this should be +// used to merge regular files as hardlinks. If the files are not identical, +// other than Paths and Digests, the merge will fail and an error will be +// returned. +func Merge(fs ...Resource) (Resource, error) { + if len(fs) < 1 { + return nil, fmt.Errorf("please provide a resource to merge") + } + + if len(fs) == 1 { + return fs[0], nil + } + + var paths []string + var digests []digest.Digest + bypath := map[string][]Resource{} + + // The attributes are all compared against the first to make sure they + // agree before adding to the above collections. If any of these don't + // correctly validate, the merge fails. + prototype := fs[0] + xattrs := make(map[string][]byte) + + // initialize xattrs for use below. All files must have same xattrs. + if prototypeXAttrer, ok := prototype.(XAttrer); ok { + for attr, value := range prototypeXAttrer.XAttrs() { + xattrs[attr] = value + } + } + + for _, f := range fs { + h, isHardlinkable := f.(Hardlinkable) + if !isHardlinkable { + return nil, errNotAHardLink + } + + if f.Mode() != prototype.Mode() { + return nil, fmt.Errorf("modes do not match: %v != %v", f.Mode(), prototype.Mode()) + } + + if f.UID() != prototype.UID() { + return nil, fmt.Errorf("uid does not match: %v != %v", f.UID(), prototype.UID()) + } + + if f.GID() != prototype.GID() { + return nil, fmt.Errorf("gid does not match: %v != %v", f.GID(), prototype.GID()) + } + + if xattrer, ok := f.(XAttrer); ok { + fxattrs := xattrer.XAttrs() + if !reflect.DeepEqual(fxattrs, xattrs) { + return nil, fmt.Errorf("resource %q xattrs do not match: %v != %v", f, fxattrs, xattrs) + } + } + + for _, p := range h.Paths() { + pfs, ok := bypath[p] + if !ok { + // ensure paths are unique by only appending on a new path. + paths = append(paths, p) + } + + bypath[p] = append(pfs, f) + } + + if regFile, isRegFile := f.(RegularFile); isRegFile { + prototypeRegFile, prototypeIsRegFile := prototype.(RegularFile) + if !prototypeIsRegFile { + return nil, errors.New("prototype is not a regular file") + } + + if regFile.Size() != prototypeRegFile.Size() { + return nil, fmt.Errorf("size does not match: %v != %v", regFile.Size(), prototypeRegFile.Size()) + } + + digests = append(digests, regFile.Digests()...) + } else if device, isDevice := f.(Device); isDevice { + prototypeDevice, prototypeIsDevice := prototype.(Device) + if !prototypeIsDevice { + return nil, errors.New("prototype is not a device") + } + + if device.Major() != prototypeDevice.Major() { + return nil, fmt.Errorf("major number does not match: %v != %v", device.Major(), prototypeDevice.Major()) + } + if device.Minor() != prototypeDevice.Minor() { + return nil, fmt.Errorf("minor number does not match: %v != %v", device.Minor(), prototypeDevice.Minor()) + } + } else if _, isNamedPipe := f.(NamedPipe); isNamedPipe { + _, prototypeIsNamedPipe := prototype.(NamedPipe) + if !prototypeIsNamedPipe { + return nil, errors.New("prototype is not a named pipe") + } + } else { + return nil, errNotAHardLink + } + } + + sort.Stable(sort.StringSlice(paths)) + + // Choose a "canonical" file. Really, it is just the first file to sort + // against. We also effectively select the very first digest as the + // "canonical" one for this file. + first := bypath[paths[0]][0] + + resource := resource{ + paths: paths, + mode: first.Mode(), + uid: first.UID(), + gid: first.GID(), + xattrs: xattrs, + } + + switch typedF := first.(type) { + case RegularFile: + var err error + digests, err = uniqifyDigests(digests...) + if err != nil { + return nil, err + } + + return ®ularFile{ + resource: resource, + size: typedF.Size(), + digests: digests, + }, nil + case Device: + return &device{ + resource: resource, + major: typedF.Major(), + minor: typedF.Minor(), + }, nil + + case NamedPipe: + return &namedPipe{ + resource: resource, + }, nil + + default: + return nil, errNotAHardLink + } +} + +type Directory interface { + Resource + XAttrer + + // Directory is a no-op method to identify directory objects by interface. + Directory() +} + +type SymLink interface { + Resource + + // Target returns the target of the symlink contained in the . + Target() string +} + +type NamedPipe interface { + Resource + Hardlinkable + XAttrer + + // Pipe is a no-op method to allow consistent resolution of NamedPipe + // interface. + Pipe() +} + +type Device interface { + Resource + Hardlinkable + XAttrer + + Major() uint64 + Minor() uint64 +} + +type resource struct { + paths []string + mode os.FileMode + uid, gid int64 + xattrs map[string][]byte +} + +var _ Resource = &resource{} + +func (r *resource) Path() string { + if len(r.paths) < 1 { + return "" + } + + return r.paths[0] +} + +func (r *resource) Mode() os.FileMode { + return r.mode +} + +func (r *resource) UID() int64 { + return r.uid +} + +func (r *resource) GID() int64 { + return r.gid +} + +type regularFile struct { + resource + size int64 + digests []digest.Digest +} + +var _ RegularFile = ®ularFile{} + +// newRegularFile returns the RegularFile, using the populated base resource +// and one or more digests of the content. +func newRegularFile(base resource, paths []string, size int64, dgsts ...digest.Digest) (RegularFile, error) { + if !base.Mode().IsRegular() { + return nil, fmt.Errorf("not a regular file") + } + + base.paths = make([]string, len(paths)) + copy(base.paths, paths) + + // make our own copy of digests + ds := make([]digest.Digest, len(dgsts)) + copy(ds, dgsts) + + return ®ularFile{ + resource: base, + size: size, + digests: ds, + }, nil +} + +func (rf *regularFile) Paths() []string { + paths := make([]string, len(rf.paths)) + copy(paths, rf.paths) + return paths +} + +func (rf *regularFile) Size() int64 { + return rf.size +} + +func (rf *regularFile) Digests() []digest.Digest { + digests := make([]digest.Digest, len(rf.digests)) + copy(digests, rf.digests) + return digests +} + +func (rf *regularFile) XAttrs() map[string][]byte { + xattrs := make(map[string][]byte, len(rf.xattrs)) + + for attr, value := range rf.xattrs { + xattrs[attr] = append(xattrs[attr], value...) + } + + return xattrs +} + +type directory struct { + resource +} + +var _ Directory = &directory{} + +func newDirectory(base resource) (Directory, error) { + if !base.Mode().IsDir() { + return nil, fmt.Errorf("not a directory") + } + + return &directory{ + resource: base, + }, nil +} + +func (d *directory) Directory() {} + +func (d *directory) XAttrs() map[string][]byte { + xattrs := make(map[string][]byte, len(d.xattrs)) + + for attr, value := range d.xattrs { + xattrs[attr] = append(xattrs[attr], value...) + } + + return xattrs +} + +type symLink struct { + resource + target string +} + +var _ SymLink = &symLink{} + +func newSymLink(base resource, target string) (SymLink, error) { + if base.Mode()&os.ModeSymlink == 0 { + return nil, fmt.Errorf("not a symlink") + } + + return &symLink{ + resource: base, + target: target, + }, nil +} + +func (l *symLink) Target() string { + return l.target +} + +type namedPipe struct { + resource +} + +var _ NamedPipe = &namedPipe{} + +func newNamedPipe(base resource, paths []string) (NamedPipe, error) { + if base.Mode()&os.ModeNamedPipe == 0 { + return nil, fmt.Errorf("not a namedpipe") + } + + base.paths = make([]string, len(paths)) + copy(base.paths, paths) + + return &namedPipe{ + resource: base, + }, nil +} + +func (np *namedPipe) Pipe() {} + +func (np *namedPipe) Paths() []string { + paths := make([]string, len(np.paths)) + copy(paths, np.paths) + return paths +} + +func (np *namedPipe) XAttrs() map[string][]byte { + xattrs := make(map[string][]byte, len(np.xattrs)) + + for attr, value := range np.xattrs { + xattrs[attr] = append(xattrs[attr], value...) + } + + return xattrs +} + +type device struct { + resource + major, minor uint64 +} + +var _ Device = &device{} + +func newDevice(base resource, paths []string, major, minor uint64) (Device, error) { + if base.Mode()&os.ModeDevice == 0 { + return nil, fmt.Errorf("not a device") + } + + base.paths = make([]string, len(paths)) + copy(base.paths, paths) + + return &device{ + resource: base, + major: major, + minor: minor, + }, nil +} + +func (d *device) Paths() []string { + paths := make([]string, len(d.paths)) + copy(paths, d.paths) + return paths +} + +func (d *device) XAttrs() map[string][]byte { + xattrs := make(map[string][]byte, len(d.xattrs)) + + for attr, value := range d.xattrs { + xattrs[attr] = append(xattrs[attr], value...) + } + + return xattrs +} + +func (d device) Major() uint64 { + return d.major +} + +func (d device) Minor() uint64 { + return d.minor +} + +// toProto converts a resource to a protobuf record. We'd like to push this +// the individual types but we want to keep this all together during +// prototyping. +func toProto(resource Resource) *pb.Resource { + b := &pb.Resource{ + Path: []string{resource.Path()}, + Mode: uint32(resource.Mode()), + Uid: resource.UID(), + Gid: resource.GID(), + } + + if xattrer, ok := resource.(XAttrer); ok { + // Sorts the XAttrs by name for consistent ordering. + keys := []string{} + xattrs := xattrer.XAttrs() + for k := range xattrs { + keys = append(keys, k) + } + sort.Strings(keys) + + for _, k := range keys { + b.Xattr = append(b.Xattr, &pb.XAttr{Name: k, Data: xattrs[k]}) + } + } + + switch r := resource.(type) { + case RegularFile: + b.Path = r.Paths() + b.Size = uint64(r.Size()) + + for _, dgst := range r.Digests() { + b.Digest = append(b.Digest, dgst.String()) + } + case SymLink: + b.Target = r.Target() + case Device: + b.Major, b.Minor = r.Major(), r.Minor() + b.Path = r.Paths() + case NamedPipe: + b.Path = r.Paths() + } + + // enforce a few stability guarantees that may not be provided by the + // resource implementation. + sort.Strings(b.Path) + + return b +} + +// fromProto converts from a protobuf Resource to a Resource interface. +func fromProto(b *pb.Resource) (Resource, error) { + base := &resource{ + paths: b.Path, + mode: os.FileMode(b.Mode), + uid: b.Uid, + gid: b.Gid, + } + + base.xattrs = make(map[string][]byte, len(b.Xattr)) + + for _, attr := range b.Xattr { + base.xattrs[attr.Name] = attr.Data + } + + switch { + case base.Mode().IsRegular(): + dgsts := make([]digest.Digest, len(b.Digest)) + for i, dgst := range b.Digest { + // TODO(stevvooe): Should we be validating at this point? + dgsts[i] = digest.Digest(dgst) + } + + return newRegularFile(*base, b.Path, int64(b.Size), dgsts...) + case base.Mode().IsDir(): + return newDirectory(*base) + case base.Mode()&os.ModeSymlink != 0: + return newSymLink(*base, b.Target) + case base.Mode()&os.ModeNamedPipe != 0: + return newNamedPipe(*base, b.Path) + case base.Mode()&os.ModeDevice != 0: + return newDevice(*base, b.Path, b.Major, b.Minor) + } + + return nil, fmt.Errorf("unknown resource record (%#v): %s", b, base.Mode()) +} + +// NOTE(stevvooe): An alternative model that supports inline declaration. +// Convenient for unit testing where inline declarations may be desirable but +// creates an awkward API for the standard use case. + +// type ResourceKind int + +// const ( +// ResourceRegularFile = iota + 1 +// ResourceDirectory +// ResourceSymLink +// Resource +// ) + +// type Resource struct { +// Kind ResourceKind +// Paths []string +// Mode os.FileMode +// UID string +// GID string +// Size int64 +// Digests []digest.Digest +// Target string +// Major, Minor int +// XAttrs map[string][]byte +// } + +// type RegularFile struct { +// Paths []string +// Size int64 +// Digests []digest.Digest +// Perm os.FileMode // os.ModePerm + sticky, setuid, setgid +// } diff --git a/vendor/github.com/containerd/continuity/resource_unix.go b/vendor/github.com/containerd/continuity/resource_unix.go new file mode 100644 index 000000000..4144643e0 --- /dev/null +++ b/vendor/github.com/containerd/continuity/resource_unix.go @@ -0,0 +1,37 @@ +// +build linux darwin freebsd solaris + +package continuity + +import ( + "fmt" + "os" + "syscall" +) + +// newBaseResource returns a *resource, populated with data from p and fi, +// where p will be populated directly. +func newBaseResource(p string, fi os.FileInfo) (*resource, error) { + // TODO(stevvooe): This need to be resolved for the container's root, + // where here we are really getting the host OS's value. We need to allow + // this be passed in and fixed up to make these uid/gid mappings portable. + // Either this can be part of the driver or we can achieve it through some + // other mechanism. + sys, ok := fi.Sys().(*syscall.Stat_t) + if !ok { + // TODO(stevvooe): This may not be a hard error for all platforms. We + // may want to move this to the driver. + return nil, fmt.Errorf("unable to resolve syscall.Stat_t from (os.FileInfo).Sys(): %#v", fi) + } + + return &resource{ + paths: []string{p}, + mode: fi.Mode(), + + uid: int64(sys.Uid), + gid: int64(sys.Gid), + + // NOTE(stevvooe): Population of shared xattrs field is deferred to + // the resource types that populate it. Since they are a property of + // the context, they must set there. + }, nil +} diff --git a/vendor/github.com/containerd/continuity/resource_windows.go b/vendor/github.com/containerd/continuity/resource_windows.go new file mode 100644 index 000000000..7b44414ac --- /dev/null +++ b/vendor/github.com/containerd/continuity/resource_windows.go @@ -0,0 +1,12 @@ +package continuity + +import "os" + +// newBaseResource returns a *resource, populated with data from p and fi, +// where p will be populated directly. +func newBaseResource(p string, fi os.FileInfo) (*resource, error) { + return &resource{ + paths: []string{p}, + mode: fi.Mode(), + }, nil +} diff --git a/vendor/github.com/containerd/continuity/sysx/README.md b/vendor/github.com/containerd/continuity/sysx/README.md new file mode 100644 index 000000000..ad7aee533 --- /dev/null +++ b/vendor/github.com/containerd/continuity/sysx/README.md @@ -0,0 +1,3 @@ +This package is for internal use only. It is intended to only have +temporary changes before they are upstreamed to golang.org/x/sys/ +(a.k.a. https://github.com/golang/sys). diff --git a/vendor/github.com/containerd/continuity/sysx/asm.s b/vendor/github.com/containerd/continuity/sysx/asm.s deleted file mode 100644 index 8ed2fdb94..000000000 --- a/vendor/github.com/containerd/continuity/sysx/asm.s +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2014 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !gccgo - -#include "textflag.h" - -TEXT ·use(SB),NOSPLIT,$0 - RET diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_darwin.go b/vendor/github.com/containerd/continuity/sysx/chmod_darwin.go deleted file mode 100644 index e3ae2b7bb..000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_darwin.go +++ /dev/null @@ -1,18 +0,0 @@ -package sysx - -const ( - // AtSymlinkNoFollow defined from AT_SYMLINK_NOFOLLOW in - AtSymlinkNofollow = 0x20 -) - -const ( - - // SYS_FCHMODAT defined from golang.org/sys/unix - SYS_FCHMODAT = 467 -) - -// These functions will be generated by generate.sh -// $ GOOS=darwin GOARCH=386 ./generate.sh chmod -// $ GOOS=darwin GOARCH=amd64 ./generate.sh chmod - -//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_darwin_386.go b/vendor/github.com/containerd/continuity/sysx/chmod_darwin_386.go deleted file mode 100644 index 5a8cf5b57..000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_darwin_386.go +++ /dev/null @@ -1,25 +0,0 @@ -// mksyscall.pl -l32 chmod_darwin.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package sysx - -import ( - "syscall" - "unsafe" -) - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := syscall.Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) - use(unsafe.Pointer(_p0)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_darwin_amd64.go b/vendor/github.com/containerd/continuity/sysx/chmod_darwin_amd64.go deleted file mode 100644 index 3287d1d57..000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_darwin_amd64.go +++ /dev/null @@ -1,25 +0,0 @@ -// mksyscall.pl chmod_darwin.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package sysx - -import ( - "syscall" - "unsafe" -) - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := syscall.Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) - use(unsafe.Pointer(_p0)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_freebsd.go b/vendor/github.com/containerd/continuity/sysx/chmod_freebsd.go deleted file mode 100644 index b64a708be..000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_freebsd.go +++ /dev/null @@ -1,17 +0,0 @@ -package sysx - -const ( - // AtSymlinkNoFollow defined from AT_SYMLINK_NOFOLLOW in - AtSymlinkNofollow = 0x200 -) - -const ( - - // SYS_FCHMODAT defined from golang.org/sys/unix - SYS_FCHMODAT = 490 -) - -// These functions will be generated by generate.sh -// $ GOOS=freebsd GOARCH=amd64 ./generate.sh chmod - -//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_freebsd_amd64.go b/vendor/github.com/containerd/continuity/sysx/chmod_freebsd_amd64.go deleted file mode 100644 index 5a271abb1..000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_freebsd_amd64.go +++ /dev/null @@ -1,25 +0,0 @@ -// mksyscall.pl chmod_freebsd.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package sysx - -import ( - "syscall" - "unsafe" -) - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - _, _, e1 := syscall.Syscall6(SYS_FCHMODAT, uintptr(dirfd), uintptr(unsafe.Pointer(_p0)), uintptr(mode), uintptr(flags), 0, 0) - use(unsafe.Pointer(_p0)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_linux.go b/vendor/github.com/containerd/continuity/sysx/chmod_linux.go deleted file mode 100644 index 89df6d38e..000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_linux.go +++ /dev/null @@ -1,12 +0,0 @@ -package sysx - -import "syscall" - -const ( - // AtSymlinkNoFollow defined from AT_SYMLINK_NOFOLLOW in /usr/include/linux/fcntl.h - AtSymlinkNofollow = 0x100 -) - -func Fchmodat(dirfd int, path string, mode uint32, flags int) error { - return syscall.Fchmodat(dirfd, path, mode, flags) -} diff --git a/vendor/github.com/containerd/continuity/sysx/chmod_solaris.go b/vendor/github.com/containerd/continuity/sysx/chmod_solaris.go deleted file mode 100644 index 3ba6e5edc..000000000 --- a/vendor/github.com/containerd/continuity/sysx/chmod_solaris.go +++ /dev/null @@ -1,11 +0,0 @@ -package sysx - -import "golang.org/x/sys/unix" - -const ( - AtSymlinkNofollow = unix.AT_SYMLINK_NOFOLLOW -) - -func Fchmodat(dirfd int, path string, mode uint32, flags int) error { - return unix.Fchmodat(dirfd, path, mode, flags) -} diff --git a/vendor/github.com/containerd/continuity/sysx/sys.go b/vendor/github.com/containerd/continuity/sysx/sys.go deleted file mode 100644 index 0bb167628..000000000 --- a/vendor/github.com/containerd/continuity/sysx/sys.go +++ /dev/null @@ -1,37 +0,0 @@ -package sysx - -import ( - "syscall" - "unsafe" -) - -var _zero uintptr - -// use is a no-op, but the compiler cannot see that it is. -// Calling use(p) ensures that p is kept live until that point. -//go:noescape -func use(p unsafe.Pointer) - -// Do the interface allocations only once for common -// Errno values. -var ( - errEAGAIN error = syscall.EAGAIN - errEINVAL error = syscall.EINVAL - errENOENT error = syscall.ENOENT -) - -// errnoErr returns common boxed Errno values, to prevent -// allocations at runtime. -func errnoErr(e syscall.Errno) error { - switch e { - case 0: - return nil - case syscall.EAGAIN: - return errEAGAIN - case syscall.EINVAL: - return errEINVAL - case syscall.ENOENT: - return errENOENT - } - return e -} diff --git a/vendor/github.com/containerd/continuity/sysx/xattr.go b/vendor/github.com/containerd/continuity/sysx/xattr.go index 20937c2d4..a59efee9a 100644 --- a/vendor/github.com/containerd/continuity/sysx/xattr.go +++ b/vendor/github.com/containerd/continuity/sysx/xattr.go @@ -1,14 +1,56 @@ +// +build linux darwin + package sysx import ( "bytes" - "fmt" "syscall" + + "golang.org/x/sys/unix" ) -const defaultXattrBufferSize = 5 +// Listxattr calls syscall listxattr and reads all content +// and returns a string array +func Listxattr(path string) ([]string, error) { + return listxattrAll(path, unix.Listxattr) +} -var ErrNotSupported = fmt.Errorf("not supported") +// Removexattr calls syscall removexattr +func Removexattr(path string, attr string) (err error) { + return unix.Removexattr(path, attr) +} + +// Setxattr calls syscall setxattr +func Setxattr(path string, attr string, data []byte, flags int) (err error) { + return unix.Setxattr(path, attr, data, flags) +} + +// Getxattr calls syscall getxattr +func Getxattr(path, attr string) ([]byte, error) { + return getxattrAll(path, attr, unix.Getxattr) +} + +// LListxattr lists xattrs, not following symlinks +func LListxattr(path string) ([]string, error) { + return listxattrAll(path, unix.Llistxattr) +} + +// LRemovexattr removes an xattr, not following symlinks +func LRemovexattr(path string, attr string) (err error) { + return unix.Lremovexattr(path, attr) +} + +// LSetxattr sets an xattr, not following symlinks +func LSetxattr(path string, attr string, data []byte, flags int) (err error) { + return unix.Lsetxattr(path, attr, data, flags) +} + +// LGetxattr gets an xattr, not following symlinks +func LGetxattr(path, attr string) ([]byte, error) { + return getxattrAll(path, attr, unix.Lgetxattr) +} + +const defaultXattrBufferSize = 5 type listxattrFunc func(path string, dest []byte) (int, error) diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_darwin.go b/vendor/github.com/containerd/continuity/sysx/xattr_darwin.go deleted file mode 100644 index 1164a7d11..000000000 --- a/vendor/github.com/containerd/continuity/sysx/xattr_darwin.go +++ /dev/null @@ -1,71 +0,0 @@ -package sysx - -// These functions will be generated by generate.sh -// $ GOOS=darwin GOARCH=386 ./generate.sh xattr -// $ GOOS=darwin GOARCH=amd64 ./generate.sh xattr - -//sys getxattr(path string, attr string, dest []byte, pos int, options int) (sz int, err error) -//sys setxattr(path string, attr string, data []byte, flags int) (err error) -//sys removexattr(path string, attr string, options int) (err error) -//sys listxattr(path string, dest []byte, options int) (sz int, err error) -//sys Fchmodat(dirfd int, path string, mode uint32, flags int) (err error) - -const ( - xattrNoFollow = 0x01 -) - -func listxattrFollow(path string, dest []byte) (sz int, err error) { - return listxattr(path, dest, 0) -} - -// Listxattr calls syscall getxattr -func Listxattr(path string) ([]string, error) { - return listxattrAll(path, listxattrFollow) -} - -// Removexattr calls syscall getxattr -func Removexattr(path string, attr string) (err error) { - return removexattr(path, attr, 0) -} - -// Setxattr calls syscall setxattr -func Setxattr(path string, attr string, data []byte, flags int) (err error) { - return setxattr(path, attr, data, flags) -} - -func getxattrFollow(path, attr string, dest []byte) (sz int, err error) { - return getxattr(path, attr, dest, 0, 0) -} - -// Getxattr calls syscall getxattr -func Getxattr(path, attr string) ([]byte, error) { - return getxattrAll(path, attr, getxattrFollow) -} - -func listxattrNoFollow(path string, dest []byte) (sz int, err error) { - return listxattr(path, dest, xattrNoFollow) -} - -// LListxattr calls syscall listxattr with XATTR_NOFOLLOW -func LListxattr(path string) ([]string, error) { - return listxattrAll(path, listxattrNoFollow) -} - -// LRemovexattr calls syscall removexattr with XATTR_NOFOLLOW -func LRemovexattr(path string, attr string) (err error) { - return removexattr(path, attr, xattrNoFollow) -} - -// Setxattr calls syscall setxattr with XATTR_NOFOLLOW -func LSetxattr(path string, attr string, data []byte, flags int) (err error) { - return setxattr(path, attr, data, flags|xattrNoFollow) -} - -func getxattrNoFollow(path, attr string, dest []byte) (sz int, err error) { - return getxattr(path, attr, dest, 0, xattrNoFollow) -} - -// LGetxattr calls syscall getxattr with XATTR_NOFOLLOW -func LGetxattr(path, attr string) ([]byte, error) { - return getxattrAll(path, attr, getxattrNoFollow) -} diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_darwin_386.go b/vendor/github.com/containerd/continuity/sysx/xattr_darwin_386.go deleted file mode 100644 index aa896b57f..000000000 --- a/vendor/github.com/containerd/continuity/sysx/xattr_darwin_386.go +++ /dev/null @@ -1,111 +0,0 @@ -// mksyscall.pl -l32 xattr_darwin.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package sysx - -import ( - "syscall" - "unsafe" -) - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func getxattr(path string, attr string, dest []byte, pos int, options int) (sz int, err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - var _p1 *byte - _p1, err = syscall.BytePtrFromString(attr) - if err != nil { - return - } - var _p2 unsafe.Pointer - if len(dest) > 0 { - _p2 = unsafe.Pointer(&dest[0]) - } else { - _p2 = unsafe.Pointer(&_zero) - } - r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), uintptr(pos), uintptr(options)) - use(unsafe.Pointer(_p0)) - use(unsafe.Pointer(_p1)) - sz = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func setxattr(path string, attr string, data []byte, flags int) (err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - var _p1 *byte - _p1, err = syscall.BytePtrFromString(attr) - if err != nil { - return - } - var _p2 unsafe.Pointer - if len(data) > 0 { - _p2 = unsafe.Pointer(&data[0]) - } else { - _p2 = unsafe.Pointer(&_zero) - } - _, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0) - use(unsafe.Pointer(_p0)) - use(unsafe.Pointer(_p1)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func removexattr(path string, attr string, options int) (err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - var _p1 *byte - _p1, err = syscall.BytePtrFromString(attr) - if err != nil { - return - } - _, _, e1 := syscall.Syscall(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options)) - use(unsafe.Pointer(_p0)) - use(unsafe.Pointer(_p1)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func listxattr(path string, dest []byte, options int) (sz int, err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - var _p1 unsafe.Pointer - if len(dest) > 0 { - _p1 = unsafe.Pointer(&dest[0]) - } else { - _p1 = unsafe.Pointer(&_zero) - } - r0, _, e1 := syscall.Syscall6(syscall.SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)), uintptr(options), 0, 0) - use(unsafe.Pointer(_p0)) - sz = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_darwin_amd64.go b/vendor/github.com/containerd/continuity/sysx/xattr_darwin_amd64.go deleted file mode 100644 index 6ff27e270..000000000 --- a/vendor/github.com/containerd/continuity/sysx/xattr_darwin_amd64.go +++ /dev/null @@ -1,111 +0,0 @@ -// mksyscall.pl xattr_darwin.go -// MACHINE GENERATED BY THE COMMAND ABOVE; DO NOT EDIT - -package sysx - -import ( - "syscall" - "unsafe" -) - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func getxattr(path string, attr string, dest []byte, pos int, options int) (sz int, err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - var _p1 *byte - _p1, err = syscall.BytePtrFromString(attr) - if err != nil { - return - } - var _p2 unsafe.Pointer - if len(dest) > 0 { - _p2 = unsafe.Pointer(&dest[0]) - } else { - _p2 = unsafe.Pointer(&_zero) - } - r0, _, e1 := syscall.Syscall6(syscall.SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), uintptr(pos), uintptr(options)) - use(unsafe.Pointer(_p0)) - use(unsafe.Pointer(_p1)) - sz = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func setxattr(path string, attr string, data []byte, flags int) (err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - var _p1 *byte - _p1, err = syscall.BytePtrFromString(attr) - if err != nil { - return - } - var _p2 unsafe.Pointer - if len(data) > 0 { - _p2 = unsafe.Pointer(&data[0]) - } else { - _p2 = unsafe.Pointer(&_zero) - } - _, _, e1 := syscall.Syscall6(syscall.SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0) - use(unsafe.Pointer(_p0)) - use(unsafe.Pointer(_p1)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func removexattr(path string, attr string, options int) (err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - var _p1 *byte - _p1, err = syscall.BytePtrFromString(attr) - if err != nil { - return - } - _, _, e1 := syscall.Syscall(syscall.SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(options)) - use(unsafe.Pointer(_p0)) - use(unsafe.Pointer(_p1)) - if e1 != 0 { - err = errnoErr(e1) - } - return -} - -// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT - -func listxattr(path string, dest []byte, options int) (sz int, err error) { - var _p0 *byte - _p0, err = syscall.BytePtrFromString(path) - if err != nil { - return - } - var _p1 unsafe.Pointer - if len(dest) > 0 { - _p1 = unsafe.Pointer(&dest[0]) - } else { - _p1 = unsafe.Pointer(&_zero) - } - r0, _, e1 := syscall.Syscall6(syscall.SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)), uintptr(options), 0, 0) - use(unsafe.Pointer(_p0)) - sz = int(r0) - if e1 != 0 { - err = errnoErr(e1) - } - return -} diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_freebsd.go b/vendor/github.com/containerd/continuity/sysx/xattr_freebsd.go deleted file mode 100644 index e8017d317..000000000 --- a/vendor/github.com/containerd/continuity/sysx/xattr_freebsd.go +++ /dev/null @@ -1,12 +0,0 @@ -package sysx - -import ( - "errors" -) - -// Initial stub version for FreeBSD. FreeBSD has a different -// syscall API from Darwin and Linux for extended attributes; -// it is also not widely used. It is not exposed at all by the -// Go syscall package, so we need to implement directly eventually. - -var unsupported = errors.New("extended attributes unsupported on FreeBSD") diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_linux.go b/vendor/github.com/containerd/continuity/sysx/xattr_linux.go deleted file mode 100644 index 311b896d9..000000000 --- a/vendor/github.com/containerd/continuity/sysx/xattr_linux.go +++ /dev/null @@ -1,44 +0,0 @@ -package sysx - -import "golang.org/x/sys/unix" - -// Listxattr calls syscall listxattr and reads all content -// and returns a string array -func Listxattr(path string) ([]string, error) { - return listxattrAll(path, unix.Listxattr) -} - -// Removexattr calls syscall removexattr -func Removexattr(path string, attr string) (err error) { - return unix.Removexattr(path, attr) -} - -// Setxattr calls syscall setxattr -func Setxattr(path string, attr string, data []byte, flags int) (err error) { - return unix.Setxattr(path, attr, data, flags) -} - -// Getxattr calls syscall getxattr -func Getxattr(path, attr string) ([]byte, error) { - return getxattrAll(path, attr, unix.Getxattr) -} - -// LListxattr lists xattrs, not following symlinks -func LListxattr(path string) ([]string, error) { - return listxattrAll(path, unix.Llistxattr) -} - -// LRemovexattr removes an xattr, not following symlinks -func LRemovexattr(path string, attr string) (err error) { - return unix.Lremovexattr(path, attr) -} - -// LSetxattr sets an xattr, not following symlinks -func LSetxattr(path string, attr string, data []byte, flags int) (err error) { - return unix.Lsetxattr(path, attr, data, flags) -} - -// LGetxattr gets an xattr, not following symlinks -func LGetxattr(path, attr string) ([]byte, error) { - return getxattrAll(path, attr, unix.Lgetxattr) -} diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_openbsd.go b/vendor/github.com/containerd/continuity/sysx/xattr_openbsd.go deleted file mode 100644 index 723619977..000000000 --- a/vendor/github.com/containerd/continuity/sysx/xattr_openbsd.go +++ /dev/null @@ -1,7 +0,0 @@ -package sysx - -import ( - "errors" -) - -var unsupported = errors.New("extended attributes unsupported on OpenBSD") diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_solaris.go b/vendor/github.com/containerd/continuity/sysx/xattr_solaris.go deleted file mode 100644 index fc523fcbb..000000000 --- a/vendor/github.com/containerd/continuity/sysx/xattr_solaris.go +++ /dev/null @@ -1,12 +0,0 @@ -package sysx - -import ( - "errors" -) - -// Initial stub version for Solaris. Solaris has a different -// syscall API from Darwin and Linux for extended attributes; -// it is also not widely used. It is not exposed at all by the -// Go syscall package, so we need to implement directly eventually. - -var unsupported = errors.New("extended attributes unsupported on Solaris") diff --git a/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go b/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go index c8389bc13..4f6a12e35 100644 --- a/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go +++ b/vendor/github.com/containerd/continuity/sysx/xattr_unsupported.go @@ -1,7 +1,14 @@ -// +build freebsd openbsd solaris +// +build !linux,!darwin package sysx +import ( + "errors" + "runtime" +) + +var unsupported = errors.New("extended attributes unsupported on " + runtime.GOOS) + // Listxattr calls syscall listxattr and reads all content // and returns a string array func Listxattr(path string) ([]string, error) { diff --git a/vendor/github.com/containerd/continuity/vendor.conf b/vendor/github.com/containerd/continuity/vendor.conf index 7c80deec5..5bd88d5fd 100644 --- a/vendor/github.com/containerd/continuity/vendor.conf +++ b/vendor/github.com/containerd/continuity/vendor.conf @@ -10,4 +10,4 @@ github.com/spf13/pflag 4c012f6dcd9546820e378d0bdda4d8fc772cdfea golang.org/x/crypto 9f005a07e0d31d45e6656d241bb5c0f2efd4bc94 golang.org/x/net a337091b0525af65de94df2eb7e98bd9962dcbe2 golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c -golang.org/x/sys 665f6529cca930e27b831a0d1dafffbe1c172924 +golang.org/x/sys 77b0e4315053a57ed2962443614bdb28db152054 diff --git a/vendor/github.com/containerd/go-runc/console.go b/vendor/github.com/containerd/go-runc/console.go index 09973e9d6..ff223e427 100644 --- a/vendor/github.com/containerd/go-runc/console.go +++ b/vendor/github.com/containerd/go-runc/console.go @@ -1,3 +1,5 @@ +// +build !windows + /* Copyright The containerd Authors. diff --git a/vendor/github.com/containerd/go-runc/io.go b/vendor/github.com/containerd/go-runc/io.go index 1b59a7ef9..6cf0410c9 100644 --- a/vendor/github.com/containerd/go-runc/io.go +++ b/vendor/github.com/containerd/go-runc/io.go @@ -20,9 +20,6 @@ import ( "io" "os" "os/exec" - - "github.com/pkg/errors" - "golang.org/x/sys/unix" ) type IO interface { @@ -37,49 +34,22 @@ type StartCloser interface { CloseAfterStart() error } -// NewPipeIO creates pipe pairs to be used with runc -func NewPipeIO(uid, gid int) (i IO, err error) { - var pipes []*pipe - // cleanup in case of an error - defer func() { - if err != nil { - for _, p := range pipes { - p.Close() - } - } - }() - stdin, err := newPipe() - if err != nil { - return nil, err - } - pipes = append(pipes, stdin) - if err = unix.Fchown(int(stdin.r.Fd()), uid, gid); err != nil { - return nil, errors.Wrap(err, "failed to chown stdin") - } +// IOOpt sets I/O creation options +type IOOpt func(*IOOption) - stdout, err := newPipe() - if err != nil { - return nil, err - } - pipes = append(pipes, stdout) - if err = unix.Fchown(int(stdout.w.Fd()), uid, gid); err != nil { - return nil, errors.Wrap(err, "failed to chown stdout") - } +// IOOption holds I/O creation options +type IOOption struct { + OpenStdin bool + OpenStdout bool + OpenStderr bool +} - stderr, err := newPipe() - if err != nil { - return nil, err +func defaultIOOption() *IOOption { + return &IOOption{ + OpenStdin: true, + OpenStdout: true, + OpenStderr: true, } - pipes = append(pipes, stderr) - if err = unix.Fchown(int(stderr.w.Fd()), uid, gid); err != nil { - return nil, errors.Wrap(err, "failed to chown stderr") - } - - return &pipeIO{ - in: stdin, - out: stdout, - err: stderr, - }, nil } func newPipe() (*pipe, error) { @@ -99,9 +69,9 @@ type pipe struct { } func (p *pipe) Close() error { - err := p.r.Close() - if werr := p.w.Close(); err == nil { - err = werr + err := p.w.Close() + if rerr := p.r.Close(); err == nil { + err = rerr } return err } @@ -113,14 +83,23 @@ type pipeIO struct { } func (i *pipeIO) Stdin() io.WriteCloser { + if i.in == nil { + return nil + } return i.in.w } func (i *pipeIO) Stdout() io.ReadCloser { + if i.out == nil { + return nil + } return i.out.r } func (i *pipeIO) Stderr() io.ReadCloser { + if i.err == nil { + return nil + } return i.err.r } @@ -131,28 +110,38 @@ func (i *pipeIO) Close() error { i.out, i.err, } { - if cerr := v.Close(); err == nil { - err = cerr + if v != nil { + if cerr := v.Close(); err == nil { + err = cerr + } } } return err } func (i *pipeIO) CloseAfterStart() error { - for _, f := range []*os.File{ - i.out.w, - i.err.w, + for _, f := range []*pipe{ + i.out, + i.err, } { - f.Close() + if f != nil { + f.w.Close() + } } return nil } // Set sets the io to the exec.Cmd func (i *pipeIO) Set(cmd *exec.Cmd) { - cmd.Stdin = i.in.r - cmd.Stdout = i.out.w - cmd.Stderr = i.err.w + if i.in != nil { + cmd.Stdin = i.in.r + } + if i.out != nil { + cmd.Stdout = i.out.w + } + if i.err != nil { + cmd.Stderr = i.err.w + } } func NewSTDIO() (IO, error) { diff --git a/vendor/github.com/containerd/go-runc/io_unix.go b/vendor/github.com/containerd/go-runc/io_unix.go new file mode 100644 index 000000000..567cd072e --- /dev/null +++ b/vendor/github.com/containerd/go-runc/io_unix.go @@ -0,0 +1,76 @@ +// +build !windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package runc + +import ( + "github.com/pkg/errors" + "golang.org/x/sys/unix" +) + +// NewPipeIO creates pipe pairs to be used with runc +func NewPipeIO(uid, gid int, opts ...IOOpt) (i IO, err error) { + option := defaultIOOption() + for _, o := range opts { + o(option) + } + var ( + pipes []*pipe + stdin, stdout, stderr *pipe + ) + // cleanup in case of an error + defer func() { + if err != nil { + for _, p := range pipes { + p.Close() + } + } + }() + if option.OpenStdin { + if stdin, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stdin) + if err = unix.Fchown(int(stdin.r.Fd()), uid, gid); err != nil { + return nil, errors.Wrap(err, "failed to chown stdin") + } + } + if option.OpenStdout { + if stdout, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stdout) + if err = unix.Fchown(int(stdout.w.Fd()), uid, gid); err != nil { + return nil, errors.Wrap(err, "failed to chown stdout") + } + } + if option.OpenStderr { + if stderr, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stderr) + if err = unix.Fchown(int(stderr.w.Fd()), uid, gid); err != nil { + return nil, errors.Wrap(err, "failed to chown stderr") + } + } + return &pipeIO{ + in: stdin, + out: stdout, + err: stderr, + }, nil +} diff --git a/vendor/github.com/containerd/go-runc/io_windows.go b/vendor/github.com/containerd/go-runc/io_windows.go new file mode 100644 index 000000000..fc56ac4f3 --- /dev/null +++ b/vendor/github.com/containerd/go-runc/io_windows.go @@ -0,0 +1,62 @@ +// +build windows + +/* + Copyright The containerd Authors. + + 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. +*/ + +package runc + +// NewPipeIO creates pipe pairs to be used with runc +func NewPipeIO(opts ...IOOpt) (i IO, err error) { + option := defaultIOOption() + for _, o := range opts { + o(option) + } + var ( + pipes []*pipe + stdin, stdout, stderr *pipe + ) + // cleanup in case of an error + defer func() { + if err != nil { + for _, p := range pipes { + p.Close() + } + } + }() + if option.OpenStdin { + if stdin, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stdin) + } + if option.OpenStdout { + if stdout, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stdout) + } + if option.OpenStderr { + if stderr, err = newPipe(); err != nil { + return nil, err + } + pipes = append(pipes, stderr) + } + return &pipeIO{ + in: stdin, + out: stdout, + err: stderr, + }, nil +} diff --git a/vendor/github.com/containerd/go-runc/runc.go b/vendor/github.com/containerd/go-runc/runc.go index ac9c89d80..96262afab 100644 --- a/vendor/github.com/containerd/go-runc/runc.go +++ b/vendor/github.com/containerd/go-runc/runc.go @@ -608,9 +608,8 @@ func parseVersion(data []byte) (Version, error) { var v Version parts := strings.Split(strings.TrimSpace(string(data)), "\n") if len(parts) != 3 { - return v, ErrParseRuncVersion + return v, nil } - for i, p := range []struct { dest *string split string diff --git a/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go b/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go index 3cae4fd8d..b1c4762fe 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/configs/config.go @@ -141,9 +141,10 @@ type Config struct { // OomScoreAdj specifies the adjustment to be made by the kernel when calculating oom scores // for a process. Valid values are between the range [-1000, '1000'], where processes with - // higher scores are preferred for being killed. + // higher scores are preferred for being killed. If it is unset then we don't touch the current + // value. // More information about kernel oom score calculation here: https://lwn.net/Articles/317814/ - OomScoreAdj int `json:"oom_score_adj"` + OomScoreAdj *int `json:"oom_score_adj,omitempty"` // UidMappings is an array of User ID mappings for User Namespaces UidMappings []IDMap `json:"uid_mappings"` diff --git a/vendor/github.com/opencontainers/runc/libcontainer/devices/devices.go b/vendor/github.com/opencontainers/runc/libcontainer/devices/devices.go index 361925890..5e2ab0581 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/devices/devices.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/devices/devices.go @@ -30,8 +30,9 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) { } var ( - devNumber = stat.Rdev + devNumber = uint64(stat.Rdev) major = unix.Major(devNumber) + minor = unix.Minor(devNumber) ) if major == 0 { return nil, ErrNotADevice @@ -51,7 +52,7 @@ func DeviceFromPath(path, permissions string) (*configs.Device, error) { Type: devType, Path: path, Major: int64(major), - Minor: int64(unix.Minor(devNumber)), + Minor: int64(minor), Permissions: permissions, FileMode: os.FileMode(mode), Uid: stat.Uid, diff --git a/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c b/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c index 2c69cee5d..a4cd1399d 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c +++ b/vendor/github.com/opencontainers/runc/libcontainer/nsenter/nsexec.c @@ -505,7 +505,8 @@ void join_namespaces(char *nslist) ns->fd = fd; ns->ns = nsflag(namespace); - strncpy(ns->path, path, PATH_MAX); + strncpy(ns->path, path, PATH_MAX - 1); + ns->path[PATH_MAX - 1] = '\0'; } while ((namespace = strtok_r(NULL, ",", &saveptr)) != NULL); /* @@ -678,17 +679,15 @@ void nsexec(void) /* * Enable setgroups(2) if we've been asked to. But we also * have to explicitly disable setgroups(2) if we're - * creating a rootless container (this is required since - * Linux 3.19). + * creating a rootless container for single-entry mapping. + * i.e. config.is_setgroup == false. + * (this is required since Linux 3.19). + * + * For rootless multi-entry mapping, config.is_setgroup shall be true and + * newuidmap/newgidmap shall be used. */ - if (config.is_rootless && config.is_setgroup) { - kill(child, SIGKILL); - bail("cannot allow setgroup in an unprivileged user namespace setup"); - } - if (config.is_setgroup) - update_setgroups(child, SETGROUPS_ALLOW); - if (config.is_rootless) + if (config.is_rootless && !config.is_setgroup) update_setgroups(child, SETGROUPS_DENY); /* Set up mappings. */ @@ -809,25 +808,30 @@ void nsexec(void) if (config.namespaces) join_namespaces(config.namespaces); - /* - * Unshare all of the namespaces. Now, it should be noted that this - * ordering might break in the future (especially with rootless - * containers). But for now, it's not possible to split this into - * CLONE_NEWUSER + [the rest] because of some RHEL SELinux issues. - * - * Note that we don't merge this with clone() because there were - * some old kernel versions where clone(CLONE_PARENT | CLONE_NEWPID) - * was broken, so we'll just do it the long way anyway. - */ - if (unshare(config.cloneflags) < 0) - bail("failed to unshare namespaces"); - /* * Deal with user namespaces first. They are quite special, as they * affect our ability to unshare other namespaces and are used as * context for privilege checks. + * + * We don't unshare all namespaces in one go. The reason for this + * is that, while the kernel documentation may claim otherwise, + * there are certain cases where unsharing all namespaces at once + * will result in namespace objects being owned incorrectly. + * Ideally we should just fix these kernel bugs, but it's better to + * be safe than sorry, and fix them separately. + * + * A specific case of this is that the SELinux label of the + * internal kern-mount that mqueue uses will be incorrect if the + * UTS namespace is cloned before the USER namespace is mapped. + * I've also heard of similar problems with the network namespace + * in some scenarios. This also mirrors how LXC deals with this + * problem. */ if (config.cloneflags & CLONE_NEWUSER) { + if (unshare(CLONE_NEWUSER) < 0) + bail("failed to unshare user namespace"); + config.cloneflags &= ~CLONE_NEWUSER; + /* * We don't have the privileges to do any mapping here (see the * clone_parent rant). So signal our parent to hook us up. @@ -853,8 +857,21 @@ void nsexec(void) if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) < 0) bail("failed to set process as dumpable"); } + + /* Become root in the namespace proper. */ + if (setresuid(0, 0, 0) < 0) + bail("failed to become root in user namespace"); } + /* + * Unshare all of the namespaces. Note that we don't merge this + * with clone() because there were some old kernel versions where + * clone(CLONE_PARENT | CLONE_NEWPID) was broken, so we'll just do + * it the long way. + */ + if (unshare(config.cloneflags) < 0) + bail("failed to unshare namespaces"); + /* * TODO: What about non-namespace clone flags that we're dropping here? * diff --git a/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go b/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go index 5f124cd8b..a4ae8901a 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/system/linux.go @@ -3,13 +3,12 @@ package system import ( - "bufio" - "fmt" "os" "os/exec" "syscall" // only for exec "unsafe" + "github.com/opencontainers/runc/libcontainer/user" "golang.org/x/sys/unix" ) @@ -102,34 +101,43 @@ func Setctty() error { } // RunningInUserNS detects whether we are currently running in a user namespace. -// Copied from github.com/lxc/lxd/shared/util.go +// Originally copied from github.com/lxc/lxd/shared/util.go func RunningInUserNS() bool { - file, err := os.Open("/proc/self/uid_map") + uidmap, err := user.CurrentProcessUIDMap() if err != nil { // This kernel-provided file only exists if user namespaces are supported return false } - defer file.Close() + return UIDMapInUserNS(uidmap) +} - buf := bufio.NewReader(file) - l, _, err := buf.ReadLine() - if err != nil { - return false - } - - line := string(l) - var a, b, c int64 - fmt.Sscanf(line, "%d %d %d", &a, &b, &c) +func UIDMapInUserNS(uidmap []user.IDMap) bool { /* * We assume we are in the initial user namespace if we have a full * range - 4294967295 uids starting at uid 0. */ - if a == 0 && b == 0 && c == 4294967295 { + if len(uidmap) == 1 && uidmap[0].ID == 0 && uidmap[0].ParentID == 0 && uidmap[0].Count == 4294967295 { return false } return true } +// GetParentNSeuid returns the euid within the parent user namespace +func GetParentNSeuid() int64 { + euid := int64(os.Geteuid()) + uidmap, err := user.CurrentProcessUIDMap() + if err != nil { + // This kernel-provided file only exists if user namespaces are supported + return euid + } + for _, um := range uidmap { + if um.ID <= euid && euid <= um.ID+um.Count-1 { + return um.ParentID + euid - um.ID + } + } + return euid +} + // SetSubreaper sets the value i as the subreaper setting for the calling process func SetSubreaper(i int) error { return unix.Prctl(PR_SET_CHILD_SUBREAPER, uintptr(i), 0, 0, 0) diff --git a/vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go b/vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go index e7cfd62b2..b94be74a6 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/system/unsupported.go @@ -2,8 +2,26 @@ package system +import ( + "os" + + "github.com/opencontainers/runc/libcontainer/user" +) + // RunningInUserNS is a stub for non-Linux systems // Always returns false func RunningInUserNS() bool { return false } + +// UIDMapInUserNS is a stub for non-Linux systems +// Always returns false +func UIDMapInUserNS(uidmap []user.IDMap) bool { + return false +} + +// GetParentNSeuid returns the euid within the parent user namespace +// Always returns os.Geteuid on non-linux +func GetParentNSeuid() int { + return os.Geteuid() +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go index c45e30041..c1e634c94 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go @@ -114,3 +114,29 @@ func CurrentUser() (User, error) { func CurrentGroup() (Group, error) { return LookupGid(unix.Getgid()) } + +func CurrentUserSubUIDs() ([]SubID, error) { + u, err := CurrentUser() + if err != nil { + return nil, err + } + return ParseSubIDFileFilter("/etc/subuid", + func(entry SubID) bool { return entry.Name == u.Name }) +} + +func CurrentGroupSubGIDs() ([]SubID, error) { + g, err := CurrentGroup() + if err != nil { + return nil, err + } + return ParseSubIDFileFilter("/etc/subgid", + func(entry SubID) bool { return entry.Name == g.Name }) +} + +func CurrentProcessUIDMap() ([]IDMap, error) { + return ParseIDMapFile("/proc/self/uid_map") +} + +func CurrentProcessGIDMap() ([]IDMap, error) { + return ParseIDMapFile("/proc/self/gid_map") +} diff --git a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go index 93414516c..7b912bbf8 100644 --- a/vendor/github.com/opencontainers/runc/libcontainer/user/user.go +++ b/vendor/github.com/opencontainers/runc/libcontainer/user/user.go @@ -75,12 +75,29 @@ func groupFromOS(g *user.Group) (Group, error) { return newGroup, nil } +// SubID represents an entry in /etc/sub{u,g}id +type SubID struct { + Name string + SubID int64 + Count int64 +} + +// IDMap represents an entry in /proc/PID/{u,g}id_map +type IDMap struct { + ID int64 + ParentID int64 + Count int64 +} + func parseLine(line string, v ...interface{}) { - if line == "" { + parseParts(strings.Split(line, ":"), v...) +} + +func parseParts(parts []string, v ...interface{}) { + if len(parts) == 0 { return } - parts := strings.Split(line, ":") for i, p := range parts { // Ignore cases where we don't have enough fields to populate the arguments. // Some configuration files like to misbehave. @@ -96,6 +113,8 @@ func parseLine(line string, v ...interface{}) { case *int: // "numbers", with conversion errors ignored because of some misbehaving configuration files. *e, _ = strconv.Atoi(p) + case *int64: + *e, _ = strconv.ParseInt(p, 10, 64) case *[]string: // Comma-separated lists. if p != "" { @@ -105,7 +124,7 @@ func parseLine(line string, v ...interface{}) { } default: // Someone goof'd when writing code using this function. Scream so they can hear us. - panic(fmt.Sprintf("parseLine only accepts {*string, *int, *[]string} as arguments! %#v is not a pointer!", e)) + panic(fmt.Sprintf("parseLine only accepts {*string, *int, *int64, *[]string} as arguments! %#v is not a pointer!", e)) } } } @@ -479,3 +498,111 @@ func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int } return GetAdditionalGroups(additionalGroups, group) } + +func ParseSubIDFile(path string) ([]SubID, error) { + subid, err := os.Open(path) + if err != nil { + return nil, err + } + defer subid.Close() + return ParseSubID(subid) +} + +func ParseSubID(subid io.Reader) ([]SubID, error) { + return ParseSubIDFilter(subid, nil) +} + +func ParseSubIDFileFilter(path string, filter func(SubID) bool) ([]SubID, error) { + subid, err := os.Open(path) + if err != nil { + return nil, err + } + defer subid.Close() + return ParseSubIDFilter(subid, filter) +} + +func ParseSubIDFilter(r io.Reader, filter func(SubID) bool) ([]SubID, error) { + if r == nil { + return nil, fmt.Errorf("nil source for subid-formatted data") + } + + var ( + s = bufio.NewScanner(r) + out = []SubID{} + ) + + for s.Scan() { + if err := s.Err(); err != nil { + return nil, err + } + + line := strings.TrimSpace(s.Text()) + if line == "" { + continue + } + + // see: man 5 subuid + p := SubID{} + parseLine(line, &p.Name, &p.SubID, &p.Count) + + if filter == nil || filter(p) { + out = append(out, p) + } + } + + return out, nil +} + +func ParseIDMapFile(path string) ([]IDMap, error) { + r, err := os.Open(path) + if err != nil { + return nil, err + } + defer r.Close() + return ParseIDMap(r) +} + +func ParseIDMap(r io.Reader) ([]IDMap, error) { + return ParseIDMapFilter(r, nil) +} + +func ParseIDMapFileFilter(path string, filter func(IDMap) bool) ([]IDMap, error) { + r, err := os.Open(path) + if err != nil { + return nil, err + } + defer r.Close() + return ParseIDMapFilter(r, filter) +} + +func ParseIDMapFilter(r io.Reader, filter func(IDMap) bool) ([]IDMap, error) { + if r == nil { + return nil, fmt.Errorf("nil source for idmap-formatted data") + } + + var ( + s = bufio.NewScanner(r) + out = []IDMap{} + ) + + for s.Scan() { + if err := s.Err(); err != nil { + return nil, err + } + + line := strings.TrimSpace(s.Text()) + if line == "" { + continue + } + + // see: man 7 user_namespaces + p := IDMap{} + parseParts(strings.Fields(line), &p.ID, &p.ParentID, &p.Count) + + if filter == nil || filter(p) { + out = append(out, p) + } + } + + return out, nil +}