Merge pull request #2183 from euanh/update-vendoring

Update vendoring to match moby/moby
This commit is contained in:
Flavio Crisciani 2018-06-22 08:17:39 -07:00 committed by GitHub
commit 28ec64d2e5
531 changed files with 21806 additions and 8130 deletions

View file

@ -69,6 +69,7 @@ import (
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/osl"
"github.com/docker/libnetwork/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@ -1252,7 +1253,7 @@ func (c *controller) loadDriver(networkType string) error {
}
if err != nil {
if err == plugins.ErrNotFound {
if errors.Cause(err) == plugins.ErrNotFound {
return types.NotFoundErrorf(err.Error())
}
return err

View file

@ -1,27 +1,28 @@
github.com/Azure/go-ansiterm d6e3b3328b783f23731bc4d058875b0371ff8109
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/Microsoft/go-winio v0.4.5
github.com/Microsoft/hcsshim v0.6.5
github.com/Microsoft/go-winio v0.4.7
github.com/Microsoft/hcsshim v0.6.11
github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
github.com/boltdb/bolt fff57c100f4dea1905678da7e90d92429dff2904
github.com/codegangsta/cli a65b733b303f0055f8d324d805f393cd3e7a7904
github.com/containerd/console 84eeaae905fa414d03e07bcd6c8d3f19e7cf180e
github.com/containerd/continuity 22694c680ee48fb8f50015b44618517e2bde77e8
github.com/containerd/console cb7008ab3d8359b78c5f464cb7cf160107ad5925
github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
github.com/coreos/etcd v3.2.1
github.com/coreos/go-semver v0.2.0
github.com/coreos/go-systemd v4
github.com/coreos/go-systemd v17
github.com/coreos/pkg fa29b1d70f0beaddd4c7021607cc3c3be8ce94b8
github.com/deckarep/golang-set ef32fa3046d9f249d399f98ebaf9be944430fd1d
github.com/docker/docker a3efe9722f34af5cf4443fe3a5c4e4e3e0457b54
github.com/docker/go-connections 3ede32e2033de7505e6500d6c868c2b9ed9f169d
github.com/docker/docker 162ba6016def672690ee4a1f3978368853a1e149
github.com/docker/go-connections 7beb39f0b969b075d1325fecb092faf27fd357b6
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
github.com/docker/go-units 9e638d38cf6977a37a8ea0078f3ee75a7cdb2dd1
github.com/docker/libkv 1d8431073ae03cdaedb198a89722f3aab6d418ef
github.com/godbus/dbus v4.0.0
github.com/gogo/protobuf v0.4
github.com/golang/protobuf 7a211bcf3bce0e3f1d74f9894916e6f116ae83b4
github.com/gogo/protobuf v1.0.0
github.com/golang/protobuf v1.1.0
github.com/gorilla/context v1.1
github.com/gorilla/mux v1.1
github.com/hashicorp/consul v0.5.2
@ -34,22 +35,32 @@ github.com/hashicorp/serf 598c54895cc5a7b1a24a398d635e8c0ea0959870
github.com/mattn/go-shellwords v1.0.3
github.com/miekg/dns v1.0.7
github.com/mrunalp/fileutils ed869b029674c0e9ce4c0dfa781405c2d9946d08
github.com/opencontainers/go-digest a6d0ee40d4207ea02364bd3b9e8e77b9159ba1eb
github.com/opencontainers/image-spec 372ad780f63454fbbbbcc7cf80e5b90245c13e13
github.com/opencontainers/runc 0351df1c5a66838d0c392b4ac4cf9450de844e2d
github.com/opencontainers/runtime-spec v1.0.0
github.com/opencontainers/selinux v1.0.0-rc1
github.com/opencontainers/go-digest v1.0.0-rc1
github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340
github.com/opencontainers/runtime-spec v1.0.1
github.com/opencontainers/selinux b29023b86e4a69d1b46b7e7b4e2b6fda03f0b9cd
github.com/samuel/go-zookeeper d0e0d8e11f318e000a8cc434616d69e329edc374
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
github.com/sirupsen/logrus v1.0.3
github.com/stretchr/testify dab07ac62d4905d3e48d17dc549c684ac3b7c15a
github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
github.com/syndtr/gocapability 33e07d32887e1e06b7c025f27ce52f62c7990bc0
github.com/ugorji/go f1f1a805ed361a0e078bb537e4ea78cd37dcf065
github.com/vishvananda/netlink b2de5d10e38ecce8607e6b438b6d174f389a004e
github.com/vishvananda/netns 604eaf189ee867d8c147fafc28def2394e878d25
golang.org/x/crypto 1a580b3eff7814fc9b40602fd35256c63b50f491
golang.org/x/net 0ed95abb35c445290478a5348a7b38bb154135fd
golang.org/x/sys 07c182904dbd53199946ba614a412c61d3c548f5
golang.org/x/sys 37707fdb30a5b38865cfb95e5aab41707daec7fd
golang.org/x/sync fd80eb99c8f653c847d294a001bdf2a3a6f768f5
github.com/pkg/errors 839d9e913e063e28dfd0e6c7b7512793e0a48be9
github.com/ishidawataru/sctp 07191f837fedd2f13d1ec7b5f885f0f3ec54b1cb
github.com/davecgh/go-spew 8991bc29aa16c548c550c7ff78260e27b9ab7c73
github.com/pmezard/go-difflib 792786c7400a136282c1664665ae0a8db921c6c2
github.com/cyphar/filepath-securejoin v0.2.1
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
github.com/hashicorp/go-immutable-radix 7f3cd4390caab3250a57f30efdb2a65dd7649ecf
github.com/hashicorp/golang-lru 0fb14efe8c47ae851c0034ed7a448854d3d34cf3
github.com/hashicorp/go-cleanhttp d5fe4b57a186c716b0e00b8c301cbd9b4182694d
github.com/hashicorp/go-rootcerts 6bb64b370b90e7ef1fa532be9e591a81c3493e00
github.com/mitchellh/go-homedir 3864e76763d94a6df2f9960b16a20a33da9f9a66

View file

@ -16,7 +16,6 @@ import (
//sys createIoCompletionPort(file syscall.Handle, port syscall.Handle, key uintptr, threadCount uint32) (newport syscall.Handle, err error) = CreateIoCompletionPort
//sys getQueuedCompletionStatus(port syscall.Handle, bytes *uint32, key *uintptr, o **ioOperation, timeout uint32) (err error) = GetQueuedCompletionStatus
//sys setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err error) = SetFileCompletionNotificationModes
//sys timeBeginPeriod(period uint32) (n int32) = winmm.timeBeginPeriod
type atomicBool int32
@ -153,8 +152,6 @@ func (f *win32File) prepareIo() (*ioOperation, error) {
// ioCompletionProcessor processes completed async IOs forever
func ioCompletionProcessor(h syscall.Handle) {
// Set the timer resolution to 1. This fixes a performance regression in golang 1.6.
timeBeginPeriod(1)
for {
var bytes uint32
var key uintptr

View file

@ -22,6 +22,7 @@ import (
const (
cERROR_PIPE_BUSY = syscall.Errno(231)
cERROR_NO_DATA = syscall.Errno(232)
cERROR_PIPE_CONNECTED = syscall.Errno(535)
cERROR_SEM_TIMEOUT = syscall.Errno(121)
@ -254,6 +255,36 @@ func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
return f, nil
}
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
p, err := l.makeServerPipe()
if err != nil {
return nil, err
}
// Wait for the client to connect.
ch := make(chan error)
go func(p *win32File) {
ch <- connectPipe(p)
}(p)
select {
case err = <-ch:
if err != nil {
p.Close()
p = nil
}
case <-l.closeCh:
// Abort the connect request by closing the handle.
p.Close()
p = nil
err = <-ch
if err == nil || err == ErrFileClosed {
err = ErrPipeListenerClosed
}
}
return p, err
}
func (l *win32PipeListener) listenerRoutine() {
closed := false
for !closed {
@ -261,31 +292,20 @@ func (l *win32PipeListener) listenerRoutine() {
case <-l.closeCh:
closed = true
case responseCh := <-l.acceptCh:
p, err := l.makeServerPipe()
if err == nil {
// Wait for the client to connect.
ch := make(chan error)
go func(p *win32File) {
ch <- connectPipe(p)
}(p)
select {
case err = <-ch:
if err != nil {
p.Close()
p = nil
}
case <-l.closeCh:
// Abort the connect request by closing the handle.
p.Close()
p = nil
err = <-ch
if err == nil || err == ErrFileClosed {
err = ErrPipeListenerClosed
}
closed = true
var (
p *win32File
err error
)
for {
p, err = l.makeConnectedServerPipe()
// If the connection was immediately closed by the client, try
// again.
if err != cERROR_NO_DATA {
break
}
}
responseCh <- acceptResponse{p, err}
closed = err == ErrPipeListenerClosed
}
}
syscall.Close(l.firstHandle)

View file

@ -38,14 +38,12 @@ func errnoErr(e syscall.Errno) error {
var (
modkernel32 = windows.NewLazySystemDLL("kernel32.dll")
modwinmm = windows.NewLazySystemDLL("winmm.dll")
modadvapi32 = windows.NewLazySystemDLL("advapi32.dll")
procCancelIoEx = modkernel32.NewProc("CancelIoEx")
procCreateIoCompletionPort = modkernel32.NewProc("CreateIoCompletionPort")
procGetQueuedCompletionStatus = modkernel32.NewProc("GetQueuedCompletionStatus")
procSetFileCompletionNotificationModes = modkernel32.NewProc("SetFileCompletionNotificationModes")
proctimeBeginPeriod = modwinmm.NewProc("timeBeginPeriod")
procConnectNamedPipe = modkernel32.NewProc("ConnectNamedPipe")
procCreateNamedPipeW = modkernel32.NewProc("CreateNamedPipeW")
procCreateFileW = modkernel32.NewProc("CreateFileW")
@ -122,12 +120,6 @@ func setFileCompletionNotificationModes(h syscall.Handle, flags uint8) (err erro
return
}
func timeBeginPeriod(period uint32) (n int32) {
r0, _, _ := syscall.Syscall(proctimeBeginPeriod.Addr(), 1, uintptr(period), 0, 0)
n = int32(r0)
return
}
func connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) {
r1, _, e1 := syscall.Syscall(procConnectNamedPipe.Addr(), 2, uintptr(pipe), uintptr(unsafe.Pointer(o)), 0)
if r1 == 0 {

View file

@ -4,9 +4,30 @@ 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.
This project has adopted the [Microsoft Open Source Code of
Conduct](https://opensource.microsoft.com/codeofconduct/). 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.
## 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.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
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.
## Reporting Security Issues
Security issues and bugs should be reported privately, via email, to the Microsoft Security
Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should
receive a response within 24 hours. If for some reason you do not, please follow up via
email to ensure we received your original message. Further information, including the
[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).
-------------------------------------------
Copyright (c) 2018 Microsoft Corp. All rights reserved.

View file

@ -10,7 +10,7 @@ import (
)
type baseLayerWriter struct {
root string
root *os.File
f *os.File
bw *winio.BackupFileWriter
err error
@ -26,10 +26,10 @@ type dirInfo struct {
// reapplyDirectoryTimes reapplies directory modification, creation, etc. times
// after processing of the directory tree has completed. The times are expected
// to be ordered such that parent directories come before child directories.
func reapplyDirectoryTimes(dis []dirInfo) error {
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 := winio.OpenForBackup(di.path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, syscall.OPEN_EXISTING)
f, err := openRelative(di.path, root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_OPEN, _FILE_DIRECTORY_FILE)
if err != nil {
return err
}
@ -75,12 +75,6 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e
w.hasUtilityVM = true
}
path := filepath.Join(w.root, name)
path, err = makeLongAbsPath(path)
if err != nil {
return err
}
var f *os.File
defer func() {
if f != nil {
@ -88,27 +82,23 @@ func (w *baseLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) (err e
}
}()
createmode := uint32(syscall.CREATE_NEW)
extraFlags := uint32(0)
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0 {
err := os.Mkdir(path, 0)
if err != nil && !os.IsExist(err) {
return err
}
createmode = syscall.OPEN_EXISTING
extraFlags |= _FILE_DIRECTORY_FILE
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
w.dirInfo = append(w.dirInfo, dirInfo{path, *fileInfo})
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 = winio.OpenForBackup(path, mode, syscall.FILE_SHARE_READ, createmode)
f, err = openRelative(name, w.root, mode, syscall.FILE_SHARE_READ, _FILE_CREATE, extraFlags)
if err != nil {
return makeError(err, "Failed to OpenForBackup", path)
return makeError(err, "Failed to openRelative", name)
}
err = winio.SetFileBasicInfo(f, fileInfo)
if err != nil {
return makeError(err, "Failed to SetFileBasicInfo", path)
return makeError(err, "Failed to SetFileBasicInfo", name)
}
w.f = f
@ -129,17 +119,7 @@ func (w *baseLayerWriter) AddLink(name string, target string) (err error) {
return err
}
linkpath, err := makeLongAbsPath(filepath.Join(w.root, name))
if err != nil {
return err
}
linktarget, err := makeLongAbsPath(filepath.Join(w.root, target))
if err != nil {
return err
}
return os.Link(linktarget, linkpath)
return linkRelative(target, w.root, name, w.root)
}
func (w *baseLayerWriter) Remove(name string) error {
@ -155,6 +135,10 @@ func (w *baseLayerWriter) Write(b []byte) (int, error) {
}
func (w *baseLayerWriter) Close() error {
defer func() {
w.root.Close()
w.root = nil
}()
err := w.closeCurrentFile()
if err != nil {
return err
@ -162,18 +146,22 @@ func (w *baseLayerWriter) Close() error {
if w.err == nil {
// Restore the file times of all the directories, since they may have
// been modified by creating child directories.
err = reapplyDirectoryTimes(w.dirInfo)
err = reapplyDirectoryTimes(w.root, w.dirInfo)
if err != nil {
return err
}
err = ProcessBaseLayer(w.root)
err = ProcessBaseLayer(w.root.Name())
if err != nil {
return err
}
if w.hasUtilityVM {
err = ProcessUtilityVMImage(filepath.Join(w.root, "UtilityVM"))
err := ensureNotReparsePointRelative("UtilityVM", w.root)
if err != nil {
return err
}
err = ProcessUtilityVMImage(filepath.Join(w.root.Name(), "UtilityVM"))
if err != nil {
return err
}

View file

@ -72,6 +72,22 @@ var (
ErrPlatformNotSupported = errors.New("unsupported platform request")
)
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)
}
// ProcessError is an error encountered in HCS during an operation on a Process object
type ProcessError struct {
Process *process
@ -174,6 +190,12 @@ func makeProcessError(process *process, operation string, extraInfo string, err
// 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

View file

@ -11,7 +11,7 @@ import (
"github.com/sirupsen/logrus"
)
//go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go
//go:generate go run mksyscall_windows.go -output zhcsshim.go hcsshim.go safeopen.go
//sys coTaskMemFree(buffer unsafe.Pointer) = ole32.CoTaskMemFree
//sys SetCurrentThreadCompartmentId(compartmentId uint32) (hr error) = iphlpapi.SetCurrentThreadCompartmentId

View file

@ -1,318 +1,323 @@
package hcsshim
import (
"encoding/json"
"fmt"
"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"`
}
//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
}
// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container
func HotAttachEndpoint(containerID string, endpointID string) error {
return modifyNetworkEndpoint(containerID, endpointID, Add)
}
// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container
func HotDetachEndpoint(containerID string, endpointID string) error {
return modifyNetworkEndpoint(containerID, endpointID, Remove)
}
// ModifyContainer corresponding to the container id, by sending a request
func modifyContainer(id string, request *ResourceModificationRequestResponse) error {
container, err := OpenContainer(id)
if err != nil {
if IsNotExist(err) {
return ErrComputeSystemDoesNotExist
}
return getInnerError(err)
}
defer container.Close()
err = container.Modify(request)
if err != nil {
if IsNotSupported(err) {
return ErrPlatformNotSupported
}
return getInnerError(err)
}
return nil
}
func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error {
requestMessage := &ResourceModificationRequestResponse{
Resource: Network,
Request: request,
Data: endpointID,
}
err := modifyContainer(containerID, requestMessage)
if err != nil {
return err
}
return 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, fmt.Errorf("Endpoint %v not found", 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 Acl Policy on the Endpoint
func (endpoint *HNSEndpoint) ApplyACLPolicy(policy *ACLPolicy) error {
operation := "ApplyACLPolicy"
title := "HCSShim::HNSEndpoint::" + operation
logrus.Debugf(title+" id=%s", endpoint.Id)
jsonString, err := json.Marshal(policy)
if err != nil {
return err
}
endpoint.Policies[0] = 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)
}
package hcsshim
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"`
}
//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
}
// HotAttachEndpoint makes a HCS Call to attach the endpoint to the container
func HotAttachEndpoint(containerID string, endpointID string) error {
return modifyNetworkEndpoint(containerID, endpointID, Add)
}
// HotDetachEndpoint makes a HCS Call to detach the endpoint from the container
func HotDetachEndpoint(containerID string, endpointID string) error {
return modifyNetworkEndpoint(containerID, endpointID, Remove)
}
// ModifyContainer corresponding to the container id, by sending a request
func modifyContainer(id string, request *ResourceModificationRequestResponse) error {
container, err := OpenContainer(id)
if err != nil {
if IsNotExist(err) {
return ErrComputeSystemDoesNotExist
}
return getInnerError(err)
}
defer container.Close()
err = container.Modify(request)
if err != nil {
if IsNotSupported(err) {
return ErrPlatformNotSupported
}
return getInnerError(err)
}
return nil
}
func modifyNetworkEndpoint(containerID string, endpointID string, request RequestType) error {
requestMessage := &ResourceModificationRequestResponse{
Resource: Network,
Request: request,
Data: endpointID,
}
err := modifyContainer(containerID, requestMessage)
if err != nil {
return err
}
return 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
}
// 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)
}

View file

@ -1,142 +1,141 @@
package hcsshim
import (
"encoding/json"
"fmt"
"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, fmt.Errorf("Network %v not found", 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)
}
package hcsshim
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)
}

View file

@ -1,95 +1,94 @@
package hcsshim
// 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"`
Protocol uint16
InternalPort uint16
Action ActionType
Direction DirectionType
LocalAddress string
RemoteAddress string
LocalPort uint16
RemotePort uint16
RuleType RuleType `json:"RuleType,omitempty"`
Priority uint16
ServiceName string
}
type Policy struct {
Type PolicyType `json:"Type"`
}
package hcsshim
// 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"`
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 Policy struct {
Type PolicyType `json:"Type"`
}

View file

@ -1,200 +1,200 @@
package hcsshim
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()
}
package hcsshim
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()
}

View file

@ -129,37 +129,39 @@ type legacyLayerWriterWrapper struct {
}
func (r *legacyLayerWriterWrapper) Close() error {
defer os.RemoveAll(r.root)
defer os.RemoveAll(r.root.Name())
defer r.legacyLayerWriter.CloseRoots()
err := r.legacyLayerWriter.Close()
if err != nil {
return err
}
// Use the original path here because ImportLayer does not support long paths for the source in TP5.
// But do use a long path for the destination to work around another bug with directories
// with MAX_PATH - 12 < length < MAX_PATH.
info := r.info
fullPath, err := makeLongAbsPath(filepath.Join(info.HomeDir, r.layerID))
if err != nil {
info.HomeDir = ""
if err = ImportLayer(info, r.destRoot.Name(), r.path, r.parentLayerPaths); err != nil {
return err
}
info.HomeDir = ""
if err = ImportLayer(info, fullPath, r.path, r.parentLayerPaths); err != nil {
return err
for _, name := range r.Tombstones {
if err = 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 = os.Remove(lnk.Path); err != nil && !os.IsNotExist(err) {
if err = removeRelative(lnk.Path, r.destRoot); err != nil && !os.IsNotExist(err) {
return err
}
if err = os.Link(lnk.Target, lnk.Path); err != nil {
if err = 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 = ProcessUtilityVMImage(filepath.Join(fullPath, "UtilityVM"))
err := ensureNotReparsePointRelative("UtilityVM", r.destRoot)
if err != nil {
return err
}
err = ProcessUtilityVMImage(filepath.Join(r.destRoot.Name(), "UtilityVM"))
if err != nil {
return err
}
@ -173,8 +175,12 @@ func (r *legacyLayerWriterWrapper) Close() error {
func NewLayerWriter(info DriverInfo, layerID 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))
if err != nil {
return nil, err
}
return &baseLayerWriter{
root: filepath.Join(info.HomeDir, layerID),
root: f,
}, nil
}
@ -185,8 +191,12 @@ func NewLayerWriter(info DriverInfo, layerID string, parentLayerPaths []string)
if err != nil {
return nil, err
}
w, err := newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID))
if err != nil {
return nil, err
}
return &legacyLayerWriterWrapper{
legacyLayerWriter: newLegacyLayerWriter(path, parentLayerPaths, filepath.Join(info.HomeDir, layerID)),
legacyLayerWriter: w,
info: info,
layerID: layerID,
path: path,

View file

@ -121,6 +121,16 @@ func (r *legacyLayerReader) walkUntilCancelled() error {
if err != nil {
return err
}
// Indirect fix for https://github.com/moby/moby/issues/32838#issuecomment-343610048.
// Handle failure from what may be a golang bug in the conversion of
// UTF16 to UTF8 in files which are left in the recycle bin. Os.Lstat
// which is called by filepath.Walk will fail when a filename contains
// unicode characters. Skip the recycle bin regardless which is goodness.
if strings.EqualFold(path, filepath.Join(r.root, `Files\$Recycle.Bin`)) && info.IsDir() {
return filepath.SkipDir
}
if path == r.root || path == filepath.Join(r.root, "tombstones.txt") || strings.HasSuffix(path, ".$wcidirs$") {
return nil
}
@ -326,59 +336,79 @@ func (r *legacyLayerReader) Close() error {
type pendingLink struct {
Path, Target string
TargetRoot *os.File
}
type pendingDir struct {
Path string
Root *os.File
}
type legacyLayerWriter struct {
root string
parentRoots []string
destRoot string
currentFile *os.File
backupWriter *winio.BackupFileWriter
tombstones []string
pathFixed bool
HasUtilityVM bool
uvmDi []dirInfo
addedFiles map[string]bool
PendingLinks []pendingLink
root *os.File
destRoot *os.File
parentRoots []*os.File
currentFile *os.File
currentFileName string
currentFileRoot *os.File
backupWriter *winio.BackupFileWriter
Tombstones []string
HasUtilityVM bool
uvmDi []dirInfo
addedFiles map[string]bool
PendingLinks []pendingLink
pendingDirs []pendingDir
currentIsDir bool
}
// newLegacyLayerWriter returns a LayerWriter that can write the contaler layer
// transport format to disk.
func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) *legacyLayerWriter {
return &legacyLayerWriter{
root: root,
parentRoots: parentRoots,
destRoot: destRoot,
addedFiles: make(map[string]bool),
func newLegacyLayerWriter(root string, parentRoots []string, destRoot string) (w *legacyLayerWriter, err error) {
w = &legacyLayerWriter{
addedFiles: make(map[string]bool),
}
defer func() {
if err != nil {
w.CloseRoots()
w = nil
}
}()
w.root, err = openRoot(root)
if err != nil {
return
}
w.destRoot, err = openRoot(destRoot)
if err != nil {
return
}
for _, r := range parentRoots {
f, err := openRoot(r)
if err != nil {
return w, err
}
w.parentRoots = append(w.parentRoots, f)
}
return
}
func (w *legacyLayerWriter) init() error {
if !w.pathFixed {
path, err := makeLongAbsPath(w.root)
if err != nil {
return err
}
for i, p := range w.parentRoots {
w.parentRoots[i], err = makeLongAbsPath(p)
if err != nil {
return err
}
}
destPath, err := makeLongAbsPath(w.destRoot)
if err != nil {
return err
}
w.root = path
w.destRoot = destPath
w.pathFixed = true
func (w *legacyLayerWriter) CloseRoots() {
if w.root != nil {
w.root.Close()
w.root = nil
}
return nil
if w.destRoot != nil {
w.destRoot.Close()
w.destRoot = nil
}
for i := range w.parentRoots {
w.parentRoots[i].Close()
}
w.parentRoots = nil
}
func (w *legacyLayerWriter) initUtilityVM() error {
if !w.HasUtilityVM {
err := os.Mkdir(filepath.Join(w.destRoot, utilityVMPath), 0)
err := mkdirRelative(utilityVMPath, w.destRoot)
if err != nil {
return err
}
@ -386,7 +416,7 @@ func (w *legacyLayerWriter) initUtilityVM() error {
// clone the utility VM from the parent layer into this layer. Use hard
// links to avoid unnecessary copying, since most of the files are
// immutable.
err = cloneTree(filepath.Join(w.parentRoots[0], utilityVMFilesPath), filepath.Join(w.destRoot, utilityVMFilesPath), mutatedUtilityVMFiles)
err = cloneTree(w.parentRoots[0], w.destRoot, utilityVMFilesPath, mutatedUtilityVMFiles)
if err != nil {
return fmt.Errorf("cloning the parent utility VM image failed: %s", err)
}
@ -395,7 +425,40 @@ func (w *legacyLayerWriter) initUtilityVM() error {
return nil
}
func (w *legacyLayerWriter) reset() {
func (w *legacyLayerWriter) reset() error {
if w.currentIsDir {
r := w.currentFile
br := winio.NewBackupStreamReader(r)
// Seek to the beginning of the backup stream, skipping the fileattrs
if _, err := r.Seek(4, io.SeekStart); err != nil {
return err
}
for {
bhdr, err := br.Next()
if err == io.EOF {
// end of backupstream data
break
}
if err != nil {
return err
}
switch bhdr.Id {
case winio.BackupReparseData:
// The current file is a `.$wcidirs$` metadata file that
// 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 {
return err
}
w.pendingDirs = append(w.pendingDirs, pendingDir{Path: w.currentFileName, Root: w.currentFileRoot})
default:
// ignore all other stream types, as we only care about directory reparse points
}
}
w.currentIsDir = false
}
if w.backupWriter != nil {
w.backupWriter.Close()
w.backupWriter = nil
@ -403,21 +466,21 @@ func (w *legacyLayerWriter) reset() {
if w.currentFile != nil {
w.currentFile.Close()
w.currentFile = nil
w.currentFileName = ""
w.currentFileRoot = nil
}
return nil
}
// copyFileWithMetadata copies a file using the backup/restore APIs in order to preserve metadata
func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) {
createDisposition := uint32(syscall.CREATE_NEW)
if isDir {
err = os.Mkdir(destPath, 0)
if err != nil {
return nil, err
}
createDisposition = syscall.OPEN_EXISTING
}
src, err := openFileOrDir(srcPath, syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY, syscall.OPEN_EXISTING)
func copyFileWithMetadata(srcRoot, destRoot *os.File, subPath string, isDir bool) (fileInfo *winio.FileBasicInfo, err error) {
src, err := openRelative(
subPath,
srcRoot,
syscall.GENERIC_READ|winio.ACCESS_SYSTEM_SECURITY,
syscall.FILE_SHARE_READ,
_FILE_OPEN,
_FILE_OPEN_REPARSE_POINT)
if err != nil {
return nil, err
}
@ -430,7 +493,17 @@ func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio
return nil, err
}
dest, err := openFileOrDir(destPath, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, createDisposition)
extraFlags := uint32(0)
if isDir {
extraFlags |= _FILE_DIRECTORY_FILE
}
dest, err := 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,
extraFlags)
if err != nil {
return nil, err
}
@ -459,39 +532,49 @@ func copyFileWithMetadata(srcPath, destPath string, isDir bool) (fileInfo *winio
// cloneTree clones a directory tree using hard links. It skips hard links for
// the file names in the provided map and just copies those files.
func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error {
func cloneTree(srcRoot *os.File, destRoot *os.File, subPath string, mutatedFiles map[string]bool) error {
var di []dirInfo
err := filepath.Walk(srcPath, func(srcFilePath string, info os.FileInfo, err error) error {
err := ensureNotReparsePointRelative(subPath, srcRoot)
if err != nil {
return err
}
err = filepath.Walk(filepath.Join(srcRoot.Name(), subPath), func(srcFilePath string, info os.FileInfo, err error) error {
if err != nil {
return err
}
relPath, err := filepath.Rel(srcPath, srcFilePath)
relPath, err := filepath.Rel(srcRoot.Name(), srcFilePath)
if err != nil {
return err
}
destFilePath := filepath.Join(destPath, relPath)
fileAttributes := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes
// Directories, reparse points, and files that will be mutated during
// utility VM import must be copied. All other files can be hard linked.
isReparsePoint := info.Sys().(*syscall.Win32FileAttributeData).FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0
if info.IsDir() || isReparsePoint || mutatedFiles[relPath] {
fi, err := copyFileWithMetadata(srcFilePath, destFilePath, info.IsDir())
isReparsePoint := fileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT != 0
// In go1.9, FileInfo.IsDir() returns false if the directory is also a symlink.
// See: https://github.com/golang/go/commit/1989921aef60c83e6f9127a8448fb5ede10e9acc
// Fixes the problem by checking syscall.FILE_ATTRIBUTE_DIRECTORY directly
isDir := fileAttributes&syscall.FILE_ATTRIBUTE_DIRECTORY != 0
if isDir || isReparsePoint || mutatedFiles[relPath] {
fi, err := copyFileWithMetadata(srcRoot, destRoot, relPath, isDir)
if err != nil {
return err
}
if info.IsDir() && !isReparsePoint {
di = append(di, dirInfo{path: destFilePath, fileInfo: *fi})
if isDir && !isReparsePoint {
di = append(di, dirInfo{path: relPath, fileInfo: *fi})
}
} else {
err = os.Link(srcFilePath, destFilePath)
err = linkRelative(relPath, srcRoot, relPath, destRoot)
if err != nil {
return err
}
}
// Don't recurse on reparse points.
if info.IsDir() && isReparsePoint {
// 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
}
@ -501,13 +584,11 @@ func cloneTree(srcPath, destPath string, mutatedFiles map[string]bool) error {
return err
}
return reapplyDirectoryTimes(di)
return reapplyDirectoryTimes(destRoot, di)
}
func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) error {
w.reset()
err := w.init()
if err != nil {
if err := w.reset(); err != nil {
return err
}
@ -515,6 +596,7 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
return w.initUtilityVM()
}
name = filepath.Clean(name)
if hasPathPrefix(name, utilityVMPath) {
if !w.HasUtilityVM {
return errors.New("missing UtilityVM directory")
@ -522,10 +604,9 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
if !hasPathPrefix(name, utilityVMFilesPath) && name != utilityVMFilesPath {
return errors.New("invalid UtilityVM layer")
}
path := filepath.Join(w.destRoot, name)
createDisposition := uint32(syscall.OPEN_EXISTING)
createDisposition := uint32(_FILE_OPEN)
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
st, err := os.Lstat(path)
st, err := lstatRelative(name, w.destRoot)
if err != nil && !os.IsNotExist(err) {
return err
}
@ -533,37 +614,44 @@ 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 = os.RemoveAll(path); err != nil {
if err = removeAllRelative(name, w.destRoot); err != nil {
return err
}
st = nil
}
}
if st == nil {
if err = os.Mkdir(path, 0); err != nil {
if err = mkdirRelative(name, w.destRoot); err != nil {
return err
}
}
if fileInfo.FileAttributes&syscall.FILE_ATTRIBUTE_REPARSE_POINT == 0 {
w.uvmDi = append(w.uvmDi, dirInfo{path: path, fileInfo: *fileInfo})
w.uvmDi = append(w.uvmDi, dirInfo{path: name, fileInfo: *fileInfo})
}
} else {
// Overwrite any existing hard link.
err = os.Remove(path)
err := removeRelative(name, w.destRoot)
if err != nil && !os.IsNotExist(err) {
return err
}
createDisposition = syscall.CREATE_NEW
createDisposition = _FILE_CREATE
}
f, err := openFileOrDir(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE|winio.WRITE_DAC|winio.WRITE_OWNER|winio.ACCESS_SYSTEM_SECURITY, createDisposition)
f, err := 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,
)
if err != nil {
return err
}
defer func() {
if f != nil {
f.Close()
os.Remove(path)
removeRelative(name, w.destRoot)
}
}()
@ -574,28 +662,31 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
w.backupWriter = winio.NewBackupFileWriter(f, true)
w.currentFile = f
w.currentFileName = name
w.currentFileRoot = w.destRoot
w.addedFiles[name] = true
f = nil
return nil
}
path := filepath.Join(w.root, name)
fname := name
if (fileInfo.FileAttributes & syscall.FILE_ATTRIBUTE_DIRECTORY) != 0 {
err := os.Mkdir(path, 0)
err := mkdirRelative(name, w.root)
if err != nil {
return err
}
path += ".$wcidirs$"
fname += ".$wcidirs$"
w.currentIsDir = true
}
f, err := openFileOrDir(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.CREATE_NEW)
f, err := openRelative(fname, w.root, syscall.GENERIC_READ|syscall.GENERIC_WRITE, syscall.FILE_SHARE_READ, _FILE_CREATE, 0)
if err != nil {
return err
}
defer func() {
if f != nil {
f.Close()
os.Remove(path)
removeRelative(fname, w.root)
}
}()
@ -617,19 +708,20 @@ func (w *legacyLayerWriter) Add(name string, fileInfo *winio.FileBasicInfo) erro
}
w.currentFile = f
w.currentFileName = name
w.currentFileRoot = w.root
w.addedFiles[name] = true
f = nil
return nil
}
func (w *legacyLayerWriter) AddLink(name string, target string) error {
w.reset()
err := w.init()
if err != nil {
if err := w.reset(); err != nil {
return err
}
var roots []string
target = filepath.Clean(target)
var roots []*os.File
if hasPathPrefix(target, filesPath) {
// Look for cross-layer hard link targets in the parent layers, since
// nothing is in the destination path yet.
@ -638,7 +730,7 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
// Since the utility VM is fully cloned into the destination path
// already, look for cross-layer hard link targets directly in the
// destination path.
roots = []string{w.destRoot}
roots = []*os.File{w.destRoot}
}
if roots == nil || (!hasPathPrefix(name, filesPath) && !hasPathPrefix(name, utilityVMFilesPath)) {
@ -647,12 +739,12 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
// Find to try the target of the link in a previously added file. If that
// fails, search in parent layers.
var selectedRoot string
var selectedRoot *os.File
if _, ok := w.addedFiles[target]; ok {
selectedRoot = w.destRoot
} else {
for _, r := range roots {
if _, err = os.Lstat(filepath.Join(r, target)); err != nil {
if _, err := lstatRelative(target, r); err != nil {
if !os.IsNotExist(err) {
return err
}
@ -661,22 +753,25 @@ func (w *legacyLayerWriter) AddLink(name string, target string) error {
break
}
}
if selectedRoot == "" {
if selectedRoot == nil {
return fmt.Errorf("failed to find link target for '%s' -> '%s'", name, target)
}
}
// The link can't be written until after the ImportLayer call.
w.PendingLinks = append(w.PendingLinks, pendingLink{
Path: filepath.Join(w.destRoot, name),
Target: filepath.Join(selectedRoot, target),
Path: name,
Target: target,
TargetRoot: selectedRoot,
})
w.addedFiles[name] = true
return nil
}
func (w *legacyLayerWriter) Remove(name string) error {
name = filepath.Clean(name)
if hasPathPrefix(name, filesPath) {
w.tombstones = append(w.tombstones, name[len(filesPath)+1:])
w.Tombstones = append(w.Tombstones, name)
} else if hasPathPrefix(name, utilityVMFilesPath) {
err := w.initUtilityVM()
if err != nil {
@ -685,11 +780,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.
path := filepath.Join(w.destRoot, name)
if _, err := os.Lstat(path); err != nil {
if _, err := lstatRelative(name, w.destRoot); err != nil {
return err
}
err = os.RemoveAll(path)
err = removeAllRelative(name, w.destRoot)
if err != nil {
return err
}
@ -711,28 +805,20 @@ func (w *legacyLayerWriter) Write(b []byte) (int, error) {
}
func (w *legacyLayerWriter) Close() error {
w.reset()
err := w.init()
if err != nil {
if err := w.reset(); err != nil {
return err
}
tf, err := os.Create(filepath.Join(w.root, "tombstones.txt"))
if err != nil {
if err := removeRelative("tombstones.txt", w.root); err != nil && !os.IsNotExist(err) {
return err
}
defer tf.Close()
_, err = tf.Write([]byte("\xef\xbb\xbfVersion 1.0\n"))
if err != nil {
return err
}
for _, t := range w.tombstones {
_, err = tf.Write([]byte(filepath.Join(`\`, t) + "\n"))
for _, pd := range w.pendingDirs {
err := mkdirRelative(pd.Path, pd.Root)
if err != nil {
return err
}
}
if w.HasUtilityVM {
err = reapplyDirectoryTimes(w.uvmDi)
err := reapplyDirectoryTimes(w.destRoot, w.uvmDi)
if err != nil {
return err
}

View file

@ -0,0 +1,7 @@
// +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

View file

@ -0,0 +1,7 @@
// +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

View file

@ -0,0 +1,427 @@
package hcsshim
import (
"errors"
"io"
"os"
"path/filepath"
"strings"
"syscall"
"unicode/utf16"
"unsafe"
winio "github.com/Microsoft/go-winio"
)
//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
//sys localAlloc(flags uint32, size int) (ptr uintptr) = kernel32.LocalAlloc
//sys localFree(ptr uintptr) = kernel32.LocalFree
type ioStatusBlock struct {
Status, Information uintptr
}
type objectAttributes struct {
Length uintptr
RootDirectory uintptr
ObjectName uintptr
Attributes uintptr
SecurityDescriptor uintptr
SecurityQoS uintptr
}
type unicodeString struct {
Length uint16
MaximumLength uint16
Buffer uintptr
}
type fileLinkInformation struct {
ReplaceIfExists bool
RootDirectory uintptr
FileNameLength uint32
FileName [1]uint16
}
type fileDispositionInformationEx struct {
Flags uintptr
}
const (
_FileLinkInformation = 11
_FileDispositionInformationEx = 64
_FILE_READ_ATTRIBUTES = 0x0080
_FILE_WRITE_ATTRIBUTES = 0x0100
_DELETE = 0x10000
_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_DISPOSITION_DELETE = 0x00000001
_OBJ_DONT_REPARSE = 0x1000
_STATUS_REPARSE_POINT_ENCOUNTERED = 0xC000050B
)
func openRoot(path string) (*os.File, error) {
longpath, err := makeLongAbsPath(path)
if err != nil {
return nil, err
}
return winio.OpenForBackup(longpath, syscall.GENERIC_READ, syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE, syscall.OPEN_EXISTING)
}
func ntRelativePath(path string) ([]uint16, error) {
path = filepath.Clean(path)
if strings.Contains(":", path) {
// Since alternate data streams must follow the file they
// are attached to, finding one here (out of order) is invalid.
return nil, errors.New("path contains invalid character `:`")
}
fspath := filepath.FromSlash(path)
if len(fspath) > 0 && fspath[0] == '\\' {
return nil, errors.New("expected relative path")
}
path16 := utf16.Encode(([]rune)(fspath))
if len(path16) > 32767 {
return nil, syscall.ENAMETOOLONG
}
return path16, nil
}
// openRelativeInternal opens a relative path from the given root, failing if
// any of the intermediate path components are reparse points.
func openRelativeInternal(path string, root *os.File, accessMask uint32, shareFlags uint32, createDisposition uint32, flags uint32) (*os.File, error) {
var (
h uintptr
iosb ioStatusBlock
oa objectAttributes
)
path16, err := ntRelativePath(path)
if err != nil {
return nil, err
}
if root == nil || root.Fd() == 0 {
return nil, errors.New("missing root directory")
}
upathBuffer := localAlloc(0, int(unsafe.Sizeof(unicodeString{}))+len(path16)*2)
defer localFree(upathBuffer)
upath := (*unicodeString)(unsafe.Pointer(upathBuffer))
upath.Length = uint16(len(path16) * 2)
upath.MaximumLength = upath.Length
upath.Buffer = upathBuffer + unsafe.Sizeof(*upath)
copy((*[32768]uint16)(unsafe.Pointer(upath.Buffer))[:], path16)
oa.Length = unsafe.Sizeof(oa)
oa.ObjectName = upathBuffer
oa.RootDirectory = uintptr(root.Fd())
oa.Attributes = _OBJ_DONT_REPARSE
status := ntCreateFile(
&h,
accessMask|syscall.SYNCHRONIZE,
&oa,
&iosb,
nil,
0,
shareFlags,
createDisposition,
_FILE_OPEN_FOR_BACKUP_INTENT|_FILE_SYNCHRONOUS_IO_NONALERT|flags,
nil,
0,
)
if status != 0 {
return nil, rtlNtStatusToDosError(status)
}
fullPath, err := makeLongAbsPath(filepath.Join(root.Name(), path))
if err != nil {
syscall.Close(syscall.Handle(h))
return nil, err
}
return os.NewFile(h, fullPath), nil
}
// 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) {
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}
}
return f, err
}
// 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 {
// 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,
0,
)
if err != nil {
return &os.LinkError{Op: "link", Old: filepath.Join(oldroot.Name(), oldname), New: filepath.Join(newroot.Name(), newname), Err: err}
}
defer oldf.Close()
// Open the parent of the new file.
var parent *os.File
parentPath := filepath.Dir(newname)
if parentPath != "." {
parent, err = openRelativeInternal(
parentPath,
newroot,
syscall.GENERIC_READ,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
_FILE_OPEN,
_FILE_DIRECTORY_FILE)
if err != nil {
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: err}
}
defer parent.Close()
fi, err := winio.GetFileBasicInfo(parent)
if err != nil {
return err
}
if (fi.FileAttributes & syscall.FILE_ATTRIBUTE_REPARSE_POINT) != 0 {
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(newroot.Name(), newname), Err: rtlNtStatusToDosError(_STATUS_REPARSE_POINT_ENCOUNTERED)}
}
} else {
parent = newroot
}
// Issue an NT call to create the link. This will be safe because NT will
// not open any more directories to create the link, so it cannot walk any
// more reparse points.
newbase := filepath.Base(newname)
newbase16, err := ntRelativePath(newbase)
if err != nil {
return err
}
size := int(unsafe.Offsetof(fileLinkInformation{}.FileName)) + len(newbase16)*2
linkinfoBuffer := localAlloc(0, size)
defer localFree(linkinfoBuffer)
linkinfo := (*fileLinkInformation)(unsafe.Pointer(linkinfoBuffer))
linkinfo.RootDirectory = parent.Fd()
linkinfo.FileNameLength = uint32(len(newbase16) * 2)
copy((*[32768]uint16)(unsafe.Pointer(&linkinfo.FileName[0]))[:], newbase16)
var iosb ioStatusBlock
status := ntSetInformationFile(
oldf.Fd(),
&iosb,
linkinfoBuffer,
uint32(size),
_FileLinkInformation,
)
if status != 0 {
return &os.LinkError{Op: "link", Old: oldf.Name(), New: filepath.Join(parent.Name(), newbase), Err: rtlNtStatusToDosError(status)}
}
return nil
}
// deleteOnClose marks a file to be deleted when the handle is closed.
func deleteOnClose(f *os.File) error {
disposition := fileDispositionInformationEx{Flags: _FILE_DISPOSITION_DELETE}
var iosb ioStatusBlock
status := ntSetInformationFile(
f.Fd(),
&iosb,
uintptr(unsafe.Pointer(&disposition)),
uint32(unsafe.Sizeof(disposition)),
_FileDispositionInformationEx,
)
if status != 0 {
return rtlNtStatusToDosError(status)
}
return nil
}
// clearReadOnly clears the readonly attribute on a file.
func clearReadOnly(f *os.File) error {
bi, err := winio.GetFileBasicInfo(f)
if err != nil {
return err
}
if bi.FileAttributes&syscall.FILE_ATTRIBUTE_READONLY == 0 {
return nil
}
sbi := winio.FileBasicInfo{
FileAttributes: bi.FileAttributes &^ syscall.FILE_ATTRIBUTE_READONLY,
}
if sbi.FileAttributes == 0 {
sbi.FileAttributes = syscall.FILE_ATTRIBUTE_NORMAL
}
return winio.SetFileBasicInfo(f, &sbi)
}
// 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 {
f, err := openRelativeInternal(
path,
root,
_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)
if err == nil {
defer f.Close()
err = deleteOnClose(f)
if err == syscall.ERROR_ACCESS_DENIED {
// Maybe the file is marked readonly. Clear the bit and retry.
clearReadOnly(f)
err = deleteOnClose(f)
}
}
if err != nil {
return &os.PathError{Op: "remove", Path: filepath.Join(root.Name(), path), Err: err}
}
return nil
}
// 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)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
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)
if err == nil || os.IsNotExist(err) {
return nil
}
return err
}
// It is necessary to use os.Open as Readdirnames does not work with
// 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))
if err != nil {
if os.IsNotExist(err) {
// Race. It was deleted between the Lstat and Open.
// Return nil per RemoveAll's docs.
return nil
}
return err
}
// Remove contents & return first error.
for {
names, err1 := fd.Readdirnames(100)
for _, name := range names {
err1 := removeAllRelative(path+string(os.PathSeparator)+name, root)
if err == nil {
err = err1
}
}
if err1 == io.EOF {
break
}
// If Readdirnames returned an error, use it.
if err == nil {
err = err1
}
if len(names) == 0 {
break
}
}
fd.Close()
// Remove directory.
err1 := removeRelative(path, root)
if err1 == nil || os.IsNotExist(err1) {
return nil
}
if err == nil {
err = err1
}
return err
}
// 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 {
f, err := openRelativeInternal(
path,
root,
0,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
_FILE_CREATE,
_FILE_DIRECTORY_FILE)
if err == nil {
f.Close()
} else {
err = &os.PathError{Op: "mkdir", Path: filepath.Join(root.Name(), path), Err: err}
}
return err
}
// 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) {
f, err := openRelativeInternal(
path,
root,
_FILE_READ_ATTRIBUTES,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
_FILE_OPEN,
_FILE_OPEN_REPARSE_POINT)
if err != nil {
return nil, &os.PathError{Op: "stat", Path: filepath.Join(root.Name(), path), Err: err}
}
defer f.Close()
return f.Stat()
}
// 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 {
// Perform an open with OBJ_DONT_REPARSE but without specifying FILE_OPEN_REPARSE_POINT.
f, err := openRelative(
path,
root,
0,
syscall.FILE_SHARE_READ|syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_DELETE,
_FILE_OPEN,
0)
if err != nil {
return err
}
f.Close()
return nil
}

View file

@ -41,6 +41,8 @@ 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")
@ -94,6 +96,11 @@ var (
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) {
@ -1040,3 +1047,34 @@ func __hnsCall(method *uint16, path *uint16, object *uint16, response **uint16)
}
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
}

View file

@ -1,3 +1,19 @@
/*
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 console
import (

View file

@ -1,5 +1,21 @@
// +build linux
/*
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 console
import (

View file

@ -1,4 +1,20 @@
// +build darwin freebsd linux solaris
// +build darwin freebsd linux openbsd solaris
/*
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 console

View file

@ -1,3 +1,19 @@
/*
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 console
import (

View file

@ -1,3 +1,19 @@
/*
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 console
import (

View file

@ -1,3 +1,19 @@
/*
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 console
import (

View file

@ -1,3 +1,19 @@
/*
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 console
import (
@ -13,25 +29,21 @@ const (
cmdTcSet = unix.TCSETS
)
func ioctl(fd, flag, data uintptr) error {
if _, _, err := unix.Syscall(unix.SYS_IOCTL, fd, flag, data); err != 0 {
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
// unlockpt should be called before opening the slave side of a pty.
func unlockpt(f *os.File) error {
var u int32
if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))); err != 0 {
return err
}
return nil
}
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
// unlockpt should be called before opening the slave side of a pty.
func unlockpt(f *os.File) error {
var u int32
return ioctl(f.Fd(), unix.TIOCSPTLCK, uintptr(unsafe.Pointer(&u)))
}
// ptsname retrieves the name of the first available pts for the given master.
func ptsname(f *os.File) (string, error) {
n, err := unix.IoctlGetInt(int(f.Fd()), unix.TIOCGPTN)
if err != nil {
var u uint32
if _, _, err := unix.Syscall(unix.SYS_IOCTL, f.Fd(), unix.TIOCGPTN, uintptr(unsafe.Pointer(&u))); err != 0 {
return "", err
}
return fmt.Sprintf("/dev/pts/%d", n), nil
return fmt.Sprintf("/dev/pts/%d", u), nil
}

View file

@ -0,0 +1,51 @@
// +build openbsd,cgo
/*
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 console
import (
"os"
"golang.org/x/sys/unix"
)
//#include <stdlib.h>
import "C"
const (
cmdTcGet = unix.TIOCGETA
cmdTcSet = unix.TIOCSETA
)
// ptsname retrieves the name of the first available pts for the given master.
func ptsname(f *os.File) (string, error) {
ptspath, err := C.ptsname(C.int(f.Fd()))
if err != nil {
return "", err
}
return C.GoString(ptspath), nil
}
// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f.
// unlockpt should be called before opening the slave side of a pty.
func unlockpt(f *os.File) error {
if _, err := C.grantpt(C.int(f.Fd())); err != nil {
return err
}
return nil
}

View file

@ -0,0 +1,47 @@
// +build openbsd,!cgo
/*
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.
*/
//
// Implementing the functions below requires cgo support. Non-cgo stubs
// versions are defined below to enable cross-compilation of source code
// that depends on these functions, but the resultant cross-compiled
// binaries cannot actually be used. If the stub function(s) below are
// actually invoked they will display an error message and cause the
// calling process to exit.
//
package console
import (
"os"
"golang.org/x/sys/unix"
)
const (
cmdTcGet = unix.TIOCGETA
cmdTcSet = unix.TIOCSETA
)
func ptsname(f *os.File) (string, error) {
panic("ptsname() support requires cgo.")
}
func unlockpt(f *os.File) error {
panic("unlockpt() support requires cgo.")
}

View file

@ -1,5 +1,21 @@
// +build solaris,cgo
/*
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 console
import (

View file

@ -1,5 +1,21 @@
// +build solaris,!cgo
/*
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.
*/
//
// Implementing the functions below requires cgo support. Non-cgo stubs
// versions are defined below to enable cross-compilation of source code

View file

@ -1,4 +1,20 @@
// +build darwin freebsd linux solaris
// +build darwin freebsd linux openbsd solaris
/*
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 console

View file

@ -0,0 +1,13 @@
bazil.org/fuse 371fbbdaa8987b715bdd21d6adc4c9b20155f748
github.com/dustin/go-humanize bb3d318650d48840a39aa21a027c6630e198e626
github.com/golang/protobuf 1e59b77b52bf8e4b449a57e6f79f21226d571845
github.com/inconshreveable/mousetrap 76626ae9c91c4f2a10f34cad8ce83ea42c93bb75
github.com/opencontainers/go-digest 279bed98673dd5bef374d3b6e4b09e2af76183bf
github.com/pkg/errors f15c970de5b76fac0b59abb32d62c17cc7bed265
github.com/sirupsen/logrus 89742aefa4b206dcf400792f3bd35b542998eb3b
github.com/spf13/cobra 2da4a54c5ceefcee7ca5dd0eea1e18a3b6366489
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

View file

@ -0,0 +1,5 @@
CoreOS Project
Copyright 2018 CoreOS, Inc
This product includes software developed at CoreOS, Inc.
(http://www.coreos.com/).

View file

@ -6,9 +6,11 @@
Go bindings to systemd. The project has several packages:
- `activation` - for writing and using socket activation from Go
- `daemon` - for notifying systemd of service status changes
- `dbus` - for starting/stopping/inspecting running services and units
- `journal` - for writing to systemd's logging service, journald
- `sdjournal` - for reading from journald by wrapping its C API
- `login1` - for integration with the systemd logind API
- `machine1` - for registering machines/containers with systemd
- `unit` - for (de)serialization and comparison of unit files
@ -18,10 +20,9 @@ An example HTTP server using socket activation can be quickly set up by followin
https://github.com/coreos/go-systemd/tree/master/examples/activation/httpserver
## Journal
## systemd Service Notification
Using the pure-Go `journal` package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry.
The `sdjournal` package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available.
The `daemon` package is an implementation of the [sd_notify protocol](https://www.freedesktop.org/software/systemd/man/sd_notify.html#Description). It can be used to inform systemd of service start-up completion, watchdog events, and other status changes.
## D-Bus
@ -45,6 +46,20 @@ Create `/etc/dbus-1/system-local.conf` that looks like this:
</busconfig>
```
## Journal
### Writing to the Journal
Using the pure-Go `journal` package you can submit journal entries directly to systemd's journal, taking advantage of features like indexed key/value pairs for each log entry.
### Reading from the Journal
The `sdjournal` package provides read access to the journal by wrapping around journald's native C API; consequently it requires cgo and the journal headers to be available.
## logind
The `login1` package provides functions to integrate with the [systemd logind API](http://www.freedesktop.org/wiki/Software/systemd/logind/).
## machined
The `machine1` package allows interaction with the [systemd machined D-Bus API](http://www.freedesktop.org/wiki/Software/systemd/machined/).

View file

@ -16,6 +16,7 @@
package dbus
import (
"encoding/hex"
"fmt"
"os"
"strconv"
@ -60,6 +61,27 @@ func PathBusEscape(path string) string {
return string(n)
}
// pathBusUnescape is the inverse of PathBusEscape.
func pathBusUnescape(path string) string {
if path == "_" {
return ""
}
n := []byte{}
for i := 0; i < len(path); i++ {
c := path[i]
if c == '_' && i+2 < len(path) {
res, err := hex.DecodeString(path[i+1 : i+3])
if err == nil {
n = append(n, res...)
}
i += 2
} else {
n = append(n, c)
}
}
return string(n)
}
// Conn is a connection to systemd's dbus endpoint.
type Conn struct {
// sysconn/sysobj are only used to call dbus methods
@ -74,19 +96,34 @@ type Conn struct {
jobs map[dbus.ObjectPath]chan<- string
sync.Mutex
}
subscriber struct {
subStateSubscriber struct {
updateCh chan<- *SubStateUpdate
errCh chan<- error
sync.Mutex
ignore map[dbus.ObjectPath]int64
cleanIgnore int64
}
propertiesSubscriber struct {
updateCh chan<- *PropertiesUpdate
errCh chan<- error
sync.Mutex
}
}
// New establishes a connection to the system bus and authenticates.
// New establishes a connection to any available bus and authenticates.
// Callers should call Close() when done with the connection.
func New() (*Conn, error) {
return newConnection(func() (*dbus.Conn, error) {
conn, err := NewSystemConnection()
if err != nil && os.Geteuid() == 0 {
return NewSystemdConnection()
}
return conn, err
}
// NewSystemConnection establishes a connection to the system bus and authenticates.
// Callers should call Close() when done with the connection
func NewSystemConnection() (*Conn, error) {
return NewConnection(func() (*dbus.Conn, error) {
return dbusAuthHelloConnection(dbus.SystemBusPrivate)
})
}
@ -95,7 +132,7 @@ func New() (*Conn, error) {
// authenticates. This can be used to connect to systemd user instances.
// Callers should call Close() when done with the connection.
func NewUserConnection() (*Conn, error) {
return newConnection(func() (*dbus.Conn, error) {
return NewConnection(func() (*dbus.Conn, error) {
return dbusAuthHelloConnection(dbus.SessionBusPrivate)
})
}
@ -104,7 +141,7 @@ func NewUserConnection() (*Conn, error) {
// This can be used for communicating with systemd without a dbus daemon.
// Callers should call Close() when done with the connection.
func NewSystemdConnection() (*Conn, error) {
return newConnection(func() (*dbus.Conn, error) {
return NewConnection(func() (*dbus.Conn, error) {
// We skip Hello when talking directly to systemd.
return dbusAuthConnection(func() (*dbus.Conn, error) {
return dbus.Dial("unix:path=/run/systemd/private")
@ -118,13 +155,18 @@ func (c *Conn) Close() {
c.sigconn.Close()
}
func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
sysconn, err := createBus()
// NewConnection establishes a connection to a bus using a caller-supplied function.
// This allows connecting to remote buses through a user-supplied mechanism.
// The supplied function may be called multiple times, and should return independent connections.
// The returned connection must be fully initialised: the org.freedesktop.DBus.Hello call must have succeeded,
// and any authentication should be handled by the function.
func NewConnection(dialBus func() (*dbus.Conn, error)) (*Conn, error) {
sysconn, err := dialBus()
if err != nil {
return nil, err
}
sigconn, err := createBus()
sigconn, err := dialBus()
if err != nil {
sysconn.Close()
return nil, err
@ -137,7 +179,7 @@ func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
sigobj: systemdObject(sigconn),
}
c.subscriber.ignore = make(map[dbus.ObjectPath]int64)
c.subStateSubscriber.ignore = make(map[dbus.ObjectPath]int64)
c.jobListener.jobs = make(map[dbus.ObjectPath]chan<- string)
// Setup the listeners on jobs so that we can get completions
@ -148,6 +190,17 @@ func newConnection(createBus func() (*dbus.Conn, error)) (*Conn, error) {
return c, nil
}
// GetManagerProperty returns the value of a property on the org.freedesktop.systemd1.Manager
// interface. The value is returned in its string representation, as defined at
// https://developer.gnome.org/glib/unstable/gvariant-text.html
func (c *Conn) GetManagerProperty(prop string) (string, error) {
variant, err := c.sysobj.GetProperty("org.freedesktop.systemd1.Manager." + prop)
if err != nil {
return "", err
}
return variant.String(), nil
}
func dbusAuthConnection(createBus func() (*dbus.Conn, error)) (*dbus.Conn, error) {
conn, err := createBus()
if err != nil {

View file

@ -1,4 +1,4 @@
// Copyright 2015 CoreOS, Inc.
// Copyright 2015, 2018 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@ -16,6 +16,7 @@ package dbus
import (
"errors"
"fmt"
"path"
"strconv"
@ -148,14 +149,27 @@ func (c *Conn) ResetFailedUnit(name string) error {
return c.sysobj.Call("org.freedesktop.systemd1.Manager.ResetFailedUnit", 0, name).Store()
}
// getProperties takes the unit name and returns all of its dbus object properties, for the given dbus interface
func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]interface{}, error) {
// SystemState returns the systemd state. Equivalent to `systemctl is-system-running`.
func (c *Conn) SystemState() (*Property, error) {
var err error
var prop dbus.Variant
obj := c.sysconn.Object("org.freedesktop.systemd1", "/org/freedesktop/systemd1")
err = obj.Call("org.freedesktop.DBus.Properties.Get", 0, "org.freedesktop.systemd1.Manager", "SystemState").Store(&prop)
if err != nil {
return nil, err
}
return &Property{Name: "SystemState", Value: prop}, nil
}
// getProperties takes the unit path and returns all of its dbus object properties, for the given dbus interface
func (c *Conn) getProperties(path dbus.ObjectPath, dbusInterface string) (map[string]interface{}, error) {
var err error
var props map[string]dbus.Variant
path := unitPath(unit)
if !path.IsValid() {
return nil, errors.New("invalid unit name: " + unit)
return nil, fmt.Errorf("invalid unit name: %v", path)
}
obj := c.sysconn.Object("org.freedesktop.systemd1", path)
@ -172,9 +186,15 @@ func (c *Conn) getProperties(unit string, dbusInterface string) (map[string]inte
return out, nil
}
// GetUnitProperties takes the unit name and returns all of its dbus object properties.
// GetUnitProperties takes the (unescaped) unit name and returns all of its dbus object properties.
func (c *Conn) GetUnitProperties(unit string) (map[string]interface{}, error) {
return c.getProperties(unit, "org.freedesktop.systemd1.Unit")
path := unitPath(unit)
return c.getProperties(path, "org.freedesktop.systemd1.Unit")
}
// GetUnitProperties takes the (escaped) unit path and returns all of its dbus object properties.
func (c *Conn) GetUnitPathProperties(path dbus.ObjectPath) (map[string]interface{}, error) {
return c.getProperties(path, "org.freedesktop.systemd1.Unit")
}
func (c *Conn) getProperty(unit string, dbusInterface string, propertyName string) (*Property, error) {
@ -199,11 +219,17 @@ func (c *Conn) GetUnitProperty(unit string, propertyName string) (*Property, err
return c.getProperty(unit, "org.freedesktop.systemd1.Unit", propertyName)
}
// GetServiceProperty returns property for given service name and property name
func (c *Conn) GetServiceProperty(service string, propertyName string) (*Property, error) {
return c.getProperty(service, "org.freedesktop.systemd1.Service", propertyName)
}
// GetUnitTypeProperties returns the extra properties for a unit, specific to the unit type.
// Valid values for unitType: Service, Socket, Target, Device, Mount, Automount, Snapshot, Timer, Swap, Path, Slice, Scope
// return "dbus.Error: Unknown interface" if the unitType is not the correct type of the unit
func (c *Conn) GetUnitTypeProperties(unit string, unitType string) (map[string]interface{}, error) {
return c.getProperties(unit, "org.freedesktop.systemd1."+unitType)
path := unitPath(unit)
return c.getProperties(path, "org.freedesktop.systemd1."+unitType)
}
// SetUnitProperties() may be used to modify certain unit properties at runtime.
@ -221,12 +247,24 @@ func (c *Conn) GetUnitTypeProperty(unit string, unitType string, propertyName st
return c.getProperty(unit, "org.freedesktop.systemd1."+unitType, propertyName)
}
// ListUnits returns an array with all currently loaded units. Note that
// units may be known by multiple names at the same time, and hence there might
// be more unit names loaded than actual units behind them.
func (c *Conn) ListUnits() ([]UnitStatus, error) {
type UnitStatus struct {
Name string // The primary unit name as string
Description string // The human readable description string
LoadState string // The load state (i.e. whether the unit file has been loaded successfully)
ActiveState string // The active state (i.e. whether the unit is currently started or not)
SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not)
Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
Path dbus.ObjectPath // The unit object path
JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise
JobType string // The job type as string
JobPath dbus.ObjectPath // The job object path
}
type storeFunc func(retvalues ...interface{}) error
func (c *Conn) listUnitsInternal(f storeFunc) ([]UnitStatus, error) {
result := make([][]interface{}, 0)
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store(&result)
err := f(&result)
if err != nil {
return nil, err
}
@ -250,17 +288,75 @@ func (c *Conn) ListUnits() ([]UnitStatus, error) {
return status, nil
}
type UnitStatus struct {
Name string // The primary unit name as string
Description string // The human readable description string
LoadState string // The load state (i.e. whether the unit file has been loaded successfully)
ActiveState string // The active state (i.e. whether the unit is currently started or not)
SubState string // The sub state (a more fine-grained version of the active state that is specific to the unit type, which the active state is not)
Followed string // A unit that is being followed in its state by this unit, if there is any, otherwise the empty string.
Path dbus.ObjectPath // The unit object path
JobId uint32 // If there is a job queued for the job unit the numeric job id, 0 otherwise
JobType string // The job type as string
JobPath dbus.ObjectPath // The job object path
// ListUnits returns an array with all currently loaded units. Note that
// units may be known by multiple names at the same time, and hence there might
// be more unit names loaded than actual units behind them.
func (c *Conn) ListUnits() ([]UnitStatus, error) {
return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnits", 0).Store)
}
// ListUnitsFiltered returns an array with units filtered by state.
// It takes a list of units' statuses to filter.
func (c *Conn) ListUnitsFiltered(states []string) ([]UnitStatus, error) {
return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsFiltered", 0, states).Store)
}
// ListUnitsByPatterns returns an array with units.
// It takes a list of units' statuses and names to filter.
// Note that units may be known by multiple names at the same time,
// and hence there might be more unit names loaded than actual units behind them.
func (c *Conn) ListUnitsByPatterns(states []string, patterns []string) ([]UnitStatus, error) {
return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByPatterns", 0, states, patterns).Store)
}
// ListUnitsByNames returns an array with units. It takes a list of units'
// names and returns an UnitStatus array. Comparing to ListUnitsByPatterns
// method, this method returns statuses even for inactive or non-existing
// units. Input array should contain exact unit names, but not patterns.
// Note: Requires systemd v230 or higher
func (c *Conn) ListUnitsByNames(units []string) ([]UnitStatus, error) {
return c.listUnitsInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitsByNames", 0, units).Store)
}
type UnitFile struct {
Path string
Type string
}
func (c *Conn) listUnitFilesInternal(f storeFunc) ([]UnitFile, error) {
result := make([][]interface{}, 0)
err := f(&result)
if err != nil {
return nil, err
}
resultInterface := make([]interface{}, len(result))
for i := range result {
resultInterface[i] = result[i]
}
files := make([]UnitFile, len(result))
fileInterface := make([]interface{}, len(files))
for i := range files {
fileInterface[i] = &files[i]
}
err = dbus.Store(resultInterface, fileInterface...)
if err != nil {
return nil, err
}
return files, nil
}
// ListUnitFiles returns an array of all available units on disk.
func (c *Conn) ListUnitFiles() ([]UnitFile, error) {
return c.listUnitFilesInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFiles", 0).Store)
}
// ListUnitFilesByPatterns returns an array of all available units on disk matched the patterns.
func (c *Conn) ListUnitFilesByPatterns(states []string, patterns []string) ([]UnitFile, error) {
return c.listUnitFilesInternal(c.sysobj.Call("org.freedesktop.systemd1.Manager.ListUnitFilesByPatterns", 0, states, patterns).Store)
}
type LinkUnitFileChange EnableUnitFileChange
@ -399,6 +495,87 @@ type DisableUnitFileChange struct {
Destination string // Destination of the symlink
}
// MaskUnitFiles masks one or more units in the system
//
// It takes three arguments:
// * list of units to mask (either just file names or full
// absolute paths if the unit files are residing outside
// the usual unit search paths)
// * runtime to specify whether the unit was enabled for runtime
// only (true, /run/systemd/..), or persistently (false, /etc/systemd/..)
// * force flag
func (c *Conn) MaskUnitFiles(files []string, runtime bool, force bool) ([]MaskUnitFileChange, error) {
result := make([][]interface{}, 0)
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.MaskUnitFiles", 0, files, runtime, force).Store(&result)
if err != nil {
return nil, err
}
resultInterface := make([]interface{}, len(result))
for i := range result {
resultInterface[i] = result[i]
}
changes := make([]MaskUnitFileChange, len(result))
changesInterface := make([]interface{}, len(changes))
for i := range changes {
changesInterface[i] = &changes[i]
}
err = dbus.Store(resultInterface, changesInterface...)
if err != nil {
return nil, err
}
return changes, nil
}
type MaskUnitFileChange struct {
Type string // Type of the change (one of symlink or unlink)
Filename string // File name of the symlink
Destination string // Destination of the symlink
}
// UnmaskUnitFiles unmasks one or more units in the system
//
// It takes two arguments:
// * list of unit files to mask (either just file names or full
// absolute paths if the unit files are residing outside
// the usual unit search paths)
// * runtime to specify whether the unit was enabled for runtime
// only (true, /run/systemd/..), or persistently (false, /etc/systemd/..)
func (c *Conn) UnmaskUnitFiles(files []string, runtime bool) ([]UnmaskUnitFileChange, error) {
result := make([][]interface{}, 0)
err := c.sysobj.Call("org.freedesktop.systemd1.Manager.UnmaskUnitFiles", 0, files, runtime).Store(&result)
if err != nil {
return nil, err
}
resultInterface := make([]interface{}, len(result))
for i := range result {
resultInterface[i] = result[i]
}
changes := make([]UnmaskUnitFileChange, len(result))
changesInterface := make([]interface{}, len(changes))
for i := range changes {
changesInterface[i] = &changes[i]
}
err = dbus.Store(resultInterface, changesInterface...)
if err != nil {
return nil, err
}
return changes, nil
}
type UnmaskUnitFileChange struct {
Type string // Type of the change (one of symlink or unlink)
Filename string // File name of the symlink
Destination string // Destination of the symlink
}
// Reload instructs systemd to scan for and reload unit files. This is
// equivalent to a 'systemctl daemon-reload'.
func (c *Conn) Reload() error {
@ -408,3 +585,8 @@ func (c *Conn) Reload() error {
func unitPath(name string) dbus.ObjectPath {
return dbus.ObjectPath("/org/freedesktop/systemd1/unit/" + PathBusEscape(name))
}
// unitName returns the unescaped base element of the supplied escaped path
func unitName(dpath dbus.ObjectPath) string {
return pathBusUnescape(path.Base(string(dpath)))
}

View file

@ -78,6 +78,15 @@ func PropRemainAfterExit(b bool) Property {
}
}
// PropType sets the Type service property. See
// http://www.freedesktop.org/software/systemd/man/systemd.service.html#Type=
func PropType(t string) Property {
return Property{
Name: "Type",
Value: dbus.MakeVariant(t),
}
}
// PropDescription sets the Description unit property. See
// http://www.freedesktop.org/software/systemd/man/systemd.unit#Description=
func PropDescription(desc string) Property {
@ -216,3 +225,13 @@ func PropSlice(slice string) Property {
Value: dbus.MakeVariant(slice),
}
}
// PropPids sets the PIDs field of scope units used in the initial construction
// of the scope only and specifies the initial PIDs to add to the scope object.
// See https://www.freedesktop.org/wiki/Software/systemd/ControlGroupInterface/#properties
func PropPids(pids ...uint32) Property {
return Property{
Name: "PIDs",
Value: dbus.MakeVariant(pids),
}
}

View file

@ -36,7 +36,7 @@ func (s *set) Length() int {
}
func (s *set) Values() (values []string) {
for val, _ := range s.data {
for val := range s.data {
values = append(values, val)
}
return

View file

@ -16,6 +16,7 @@ package dbus
import (
"errors"
"log"
"time"
"github.com/godbus/dbus"
@ -36,22 +37,12 @@ func (c *Conn) Subscribe() error {
c.sigconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0,
"type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'")
err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
if err != nil {
return err
}
return nil
return c.sigobj.Call("org.freedesktop.systemd1.Manager.Subscribe", 0).Store()
}
// Unsubscribe this connection from systemd dbus events.
func (c *Conn) Unsubscribe() error {
err := c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
if err != nil {
return err
}
return nil
return c.sigobj.Call("org.freedesktop.systemd1.Manager.Unsubscribe", 0).Store()
}
func (c *Conn) dispatch() {
@ -70,7 +61,8 @@ func (c *Conn) dispatch() {
c.jobComplete(signal)
}
if c.subscriber.updateCh == nil {
if c.subStateSubscriber.updateCh == nil &&
c.propertiesSubscriber.updateCh == nil {
continue
}
@ -84,6 +76,12 @@ func (c *Conn) dispatch() {
case "org.freedesktop.DBus.Properties.PropertiesChanged":
if signal.Body[0].(string) == "org.freedesktop.systemd1.Unit" {
unitPath = signal.Path
if len(signal.Body) >= 2 {
if changed, ok := signal.Body[1].(map[string]dbus.Variant); ok {
c.sendPropertiesUpdate(unitPath, changed)
}
}
}
}
@ -169,42 +167,80 @@ type SubStateUpdate struct {
// is full, it attempts to write an error to errCh; if errCh is full, the error
// passes silently.
func (c *Conn) SetSubStateSubscriber(updateCh chan<- *SubStateUpdate, errCh chan<- error) {
c.subscriber.Lock()
defer c.subscriber.Unlock()
c.subscriber.updateCh = updateCh
c.subscriber.errCh = errCh
}
func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
c.subscriber.Lock()
defer c.subscriber.Unlock()
if c.shouldIgnore(path) {
if c == nil {
msg := "nil receiver"
select {
case errCh <- errors.New(msg):
default:
log.Printf("full error channel while reporting: %s\n", msg)
}
return
}
info, err := c.GetUnitProperties(string(path))
if err != nil {
select {
case c.subscriber.errCh <- err:
default:
}
c.subStateSubscriber.Lock()
defer c.subStateSubscriber.Unlock()
c.subStateSubscriber.updateCh = updateCh
c.subStateSubscriber.errCh = errCh
}
func (c *Conn) sendSubStateUpdate(unitPath dbus.ObjectPath) {
c.subStateSubscriber.Lock()
defer c.subStateSubscriber.Unlock()
if c.subStateSubscriber.updateCh == nil {
return
}
name := info["Id"].(string)
substate := info["SubState"].(string)
isIgnored := c.shouldIgnore(unitPath)
defer c.cleanIgnore()
if isIgnored {
return
}
info, err := c.GetUnitPathProperties(unitPath)
if err != nil {
select {
case c.subStateSubscriber.errCh <- err:
default:
log.Printf("full error channel while reporting: %s\n", err)
}
return
}
defer c.updateIgnore(unitPath, info)
name, ok := info["Id"].(string)
if !ok {
msg := "failed to cast info.Id"
select {
case c.subStateSubscriber.errCh <- errors.New(msg):
default:
log.Printf("full error channel while reporting: %s\n", err)
}
return
}
substate, ok := info["SubState"].(string)
if !ok {
msg := "failed to cast info.SubState"
select {
case c.subStateSubscriber.errCh <- errors.New(msg):
default:
log.Printf("full error channel while reporting: %s\n", msg)
}
return
}
update := &SubStateUpdate{name, substate}
select {
case c.subscriber.updateCh <- update:
case c.subStateSubscriber.updateCh <- update:
default:
msg := "update channel is full"
select {
case c.subscriber.errCh <- errors.New("update channel full!"):
case c.subStateSubscriber.errCh <- errors.New(msg):
default:
log.Printf("full error channel while reporting: %s\n", msg)
}
return
}
c.updateIgnore(path, info)
}
// The ignore functions work around a wart in the systemd dbus interface.
@ -222,29 +258,76 @@ func (c *Conn) sendSubStateUpdate(path dbus.ObjectPath) {
// the properties).
func (c *Conn) shouldIgnore(path dbus.ObjectPath) bool {
t, ok := c.subscriber.ignore[path]
t, ok := c.subStateSubscriber.ignore[path]
return ok && t >= time.Now().UnixNano()
}
func (c *Conn) updateIgnore(path dbus.ObjectPath, info map[string]interface{}) {
c.cleanIgnore()
loadState, ok := info["LoadState"].(string)
if !ok {
return
}
// unit is unloaded - it will trigger bad systemd dbus behavior
if info["LoadState"].(string) == "not-found" {
c.subscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
if loadState == "not-found" {
c.subStateSubscriber.ignore[path] = time.Now().UnixNano() + ignoreInterval
}
}
// without this, ignore would grow unboundedly over time
func (c *Conn) cleanIgnore() {
now := time.Now().UnixNano()
if c.subscriber.cleanIgnore < now {
c.subscriber.cleanIgnore = now + cleanIgnoreInterval
if c.subStateSubscriber.cleanIgnore < now {
c.subStateSubscriber.cleanIgnore = now + cleanIgnoreInterval
for p, t := range c.subscriber.ignore {
for p, t := range c.subStateSubscriber.ignore {
if t < now {
delete(c.subscriber.ignore, p)
delete(c.subStateSubscriber.ignore, p)
}
}
}
}
// PropertiesUpdate holds a map of a unit's changed properties
type PropertiesUpdate struct {
UnitName string
Changed map[string]dbus.Variant
}
// SetPropertiesSubscriber writes to updateCh when any unit's properties
// change. Every property change reported by systemd will be sent; that is, no
// transitions will be "missed" (as they might be with SetSubStateSubscriber).
// However, state changes will only be written to the channel with non-blocking
// writes. If updateCh is full, it attempts to write an error to errCh; if
// errCh is full, the error passes silently.
func (c *Conn) SetPropertiesSubscriber(updateCh chan<- *PropertiesUpdate, errCh chan<- error) {
c.propertiesSubscriber.Lock()
defer c.propertiesSubscriber.Unlock()
c.propertiesSubscriber.updateCh = updateCh
c.propertiesSubscriber.errCh = errCh
}
// we don't need to worry about shouldIgnore() here because
// sendPropertiesUpdate doesn't call GetProperties()
func (c *Conn) sendPropertiesUpdate(unitPath dbus.ObjectPath, changedProps map[string]dbus.Variant) {
c.propertiesSubscriber.Lock()
defer c.propertiesSubscriber.Unlock()
if c.propertiesSubscriber.updateCh == nil {
return
}
update := &PropertiesUpdate{unitName(unitPath), changedProps}
select {
case c.propertiesSubscriber.updateCh <- update:
default:
msg := "update channel is full"
select {
case c.propertiesSubscriber.errCh <- errors.New(msg):
default:
log.Printf("full error channel while reporting: %s\n", msg)
}
return
}
}

View file

@ -13,15 +13,60 @@
// limitations under the License.
// Package util contains utility functions related to systemd that applications
// can use to check things like whether systemd is running.
// can use to check things like whether systemd is running. Note that some of
// these functions attempt to manually load systemd libraries at runtime rather
// than linking against them.
package util
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
var (
ErrNoCGO = fmt.Errorf("go-systemd built with CGO disabled")
)
// GetRunningSlice attempts to retrieve the name of the systemd slice in which
// the current process is running.
// This function is a wrapper around the libsystemd C library; if it cannot be
// opened, an error is returned.
func GetRunningSlice() (string, error) {
return getRunningSlice()
}
// RunningFromSystemService tries to detect whether the current process has
// been invoked from a system service. The condition for this is whether the
// process is _not_ a user process. User processes are those running in session
// scopes or under per-user `systemd --user` instances.
//
// To avoid false positives on systems without `pam_systemd` (which is
// responsible for creating user sessions), this function also uses a heuristic
// to detect whether it's being invoked from a session leader process. This is
// the case if the current process is executed directly from a service file
// (e.g. with `ExecStart=/this/cmd`). Note that this heuristic will fail if the
// command is instead launched in a subshell or similar so that it is not
// session leader (e.g. `ExecStart=/bin/bash -c "/this/cmd"`)
//
// This function is a wrapper around the libsystemd C library; if this is
// unable to successfully open a handle to the library for any reason (e.g. it
// cannot be found), an error will be returned.
func RunningFromSystemService() (bool, error) {
return runningFromSystemService()
}
// CurrentUnitName attempts to retrieve the name of the systemd system unit
// from which the calling process has been invoked. It wraps the systemd
// `sd_pid_get_unit` call, with the same caveat: for processes not part of a
// systemd system unit, this function will return an error.
func CurrentUnitName() (string, error) {
return currentUnitName()
}
// IsRunningSystemd checks whether the host was booted with systemd as its init
// system. This functions similar to systemd's `sd_booted(3)`: internally, it
// system. This functions similarly to systemd's `sd_booted(3)`: internally, it
// checks whether /run/systemd/system/ exists and is a directory.
// http://www.freedesktop.org/software/systemd/man/sd_booted.html
func IsRunningSystemd() bool {
@ -31,3 +76,15 @@ func IsRunningSystemd() bool {
}
return fi.IsDir()
}
// GetMachineID returns a host's 128-bit machine ID as a string. This functions
// similarly to systemd's `sd_id128_get_machine`: internally, it simply reads
// the contents of /etc/machine-id
// http://www.freedesktop.org/software/systemd/man/sd_id128_get_machine.html
func GetMachineID() (string, error) {
machineID, err := ioutil.ReadFile("/etc/machine-id")
if err != nil {
return "", fmt.Errorf("failed to read /etc/machine-id: %v", err)
}
return strings.TrimSpace(string(machineID)), nil
}

View file

@ -0,0 +1,175 @@
// Copyright 2016 CoreOS, Inc.
//
// 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.
// +build cgo
package util
// #include <stdlib.h>
// #include <sys/types.h>
// #include <unistd.h>
//
// int
// my_sd_pid_get_owner_uid(void *f, pid_t pid, uid_t *uid)
// {
// int (*sd_pid_get_owner_uid)(pid_t, uid_t *);
//
// sd_pid_get_owner_uid = (int (*)(pid_t, uid_t *))f;
// return sd_pid_get_owner_uid(pid, uid);
// }
//
// int
// my_sd_pid_get_unit(void *f, pid_t pid, char **unit)
// {
// int (*sd_pid_get_unit)(pid_t, char **);
//
// sd_pid_get_unit = (int (*)(pid_t, char **))f;
// return sd_pid_get_unit(pid, unit);
// }
//
// int
// my_sd_pid_get_slice(void *f, pid_t pid, char **slice)
// {
// int (*sd_pid_get_slice)(pid_t, char **);
//
// sd_pid_get_slice = (int (*)(pid_t, char **))f;
// return sd_pid_get_slice(pid, slice);
// }
//
// int
// am_session_leader()
// {
// return (getsid(0) == getpid());
// }
import "C"
import (
"fmt"
"syscall"
"unsafe"
"github.com/coreos/pkg/dlopen"
)
var libsystemdNames = []string{
// systemd < 209
"libsystemd-login.so.0",
"libsystemd-login.so",
// systemd >= 209 merged libsystemd-login into libsystemd proper
"libsystemd.so.0",
"libsystemd.so",
}
func getRunningSlice() (slice string, err error) {
var h *dlopen.LibHandle
h, err = dlopen.GetHandle(libsystemdNames)
if err != nil {
return
}
defer func() {
if err1 := h.Close(); err1 != nil {
err = err1
}
}()
sd_pid_get_slice, err := h.GetSymbolPointer("sd_pid_get_slice")
if err != nil {
return
}
var s string
sl := C.CString(s)
defer C.free(unsafe.Pointer(sl))
ret := C.my_sd_pid_get_slice(sd_pid_get_slice, 0, &sl)
if ret < 0 {
err = fmt.Errorf("error calling sd_pid_get_slice: %v", syscall.Errno(-ret))
return
}
return C.GoString(sl), nil
}
func runningFromSystemService() (ret bool, err error) {
var h *dlopen.LibHandle
h, err = dlopen.GetHandle(libsystemdNames)
if err != nil {
return
}
defer func() {
if err1 := h.Close(); err1 != nil {
err = err1
}
}()
sd_pid_get_owner_uid, err := h.GetSymbolPointer("sd_pid_get_owner_uid")
if err != nil {
return
}
var uid C.uid_t
errno := C.my_sd_pid_get_owner_uid(sd_pid_get_owner_uid, 0, &uid)
serrno := syscall.Errno(-errno)
// when we're running from a unit file, sd_pid_get_owner_uid returns
// ENOENT (systemd <220), ENXIO (systemd 220-223), or ENODATA
// (systemd >=234)
switch {
case errno >= 0:
ret = false
case serrno == syscall.ENOENT, serrno == syscall.ENXIO, serrno == syscall.ENODATA:
// Since the implementation of sessions in systemd relies on
// the `pam_systemd` module, using the sd_pid_get_owner_uid
// heuristic alone can result in false positives if that module
// (or PAM itself) is not present or properly configured on the
// system. As such, we also check if we're the session leader,
// which should be the case if we're invoked from a unit file,
// but not if e.g. we're invoked from the command line from a
// user's login session
ret = C.am_session_leader() == 1
default:
err = fmt.Errorf("error calling sd_pid_get_owner_uid: %v", syscall.Errno(-errno))
}
return
}
func currentUnitName() (unit string, err error) {
var h *dlopen.LibHandle
h, err = dlopen.GetHandle(libsystemdNames)
if err != nil {
return
}
defer func() {
if err1 := h.Close(); err1 != nil {
err = err1
}
}()
sd_pid_get_unit, err := h.GetSymbolPointer("sd_pid_get_unit")
if err != nil {
return
}
var s string
u := C.CString(s)
defer C.free(unsafe.Pointer(u))
ret := C.my_sd_pid_get_unit(sd_pid_get_unit, 0, &u)
if ret < 0 {
err = fmt.Errorf("error calling sd_pid_get_unit: %v", syscall.Errno(-ret))
return
}
unit = C.GoString(u)
return
}

View file

@ -0,0 +1,23 @@
// Copyright 2016 CoreOS, Inc.
//
// 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.
// +build !cgo
package util
func getRunningSlice() (string, error) { return "", ErrNoCGO }
func runningFromSystemService() (bool, error) { return false, ErrNoCGO }
func currentUnitName() (string, error) { return "", ErrNoCGO }

View file

@ -1,5 +1,4 @@
Apache License
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
@ -176,7 +175,18 @@
END OF TERMS AND CONDITIONS
Copyright 2014-2017 Docker, Inc.
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -189,3 +199,4 @@
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.

5
libnetwork/vendor/github.com/coreos/pkg/NOTICE generated vendored Normal file
View file

@ -0,0 +1,5 @@
CoreOS Project
Copyright 2014 CoreOS, Inc
This product includes software developed at CoreOS, Inc.
(http://www.coreos.com/).

4
libnetwork/vendor/github.com/coreos/pkg/README.md generated vendored Normal file
View file

@ -0,0 +1,4 @@
a collection of go utility packages
[![Build Status](https://travis-ci.org/coreos/pkg.png?branch=master)](https://travis-ci.org/coreos/pkg)
[![Godoc](http://img.shields.io/badge/godoc-reference-blue.svg?style=flat)](https://godoc.org/github.com/coreos/pkg)

View file

@ -0,0 +1,82 @@
// Copyright 2016 CoreOS, Inc.
//
// 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 dlopen provides some convenience functions to dlopen a library and
// get its symbols.
package dlopen
// #cgo LDFLAGS: -ldl
// #include <stdlib.h>
// #include <dlfcn.h>
import "C"
import (
"errors"
"fmt"
"unsafe"
)
var ErrSoNotFound = errors.New("unable to open a handle to the library")
// LibHandle represents an open handle to a library (.so)
type LibHandle struct {
Handle unsafe.Pointer
Libname string
}
// GetHandle tries to get a handle to a library (.so), attempting to access it
// by the names specified in libs and returning the first that is successfully
// opened. Callers are responsible for closing the handler. If no library can
// be successfully opened, an error is returned.
func GetHandle(libs []string) (*LibHandle, error) {
for _, name := range libs {
libname := C.CString(name)
defer C.free(unsafe.Pointer(libname))
handle := C.dlopen(libname, C.RTLD_LAZY)
if handle != nil {
h := &LibHandle{
Handle: handle,
Libname: name,
}
return h, nil
}
}
return nil, ErrSoNotFound
}
// GetSymbolPointer takes a symbol name and returns a pointer to the symbol.
func (l *LibHandle) GetSymbolPointer(symbol string) (unsafe.Pointer, error) {
sym := C.CString(symbol)
defer C.free(unsafe.Pointer(sym))
C.dlerror()
p := C.dlsym(l.Handle, sym)
e := C.dlerror()
if e != nil {
return nil, fmt.Errorf("error resolving symbol %q: %v", symbol, errors.New(C.GoString(e)))
}
return p, nil
}
// Close closes a LibHandle.
func (l *LibHandle) Close() error {
C.dlerror()
C.dlclose(l.Handle)
e := C.dlerror()
if e != nil {
return fmt.Errorf("error closing %v: %v", l.Libname, errors.New(C.GoString(e)))
}
return nil
}

View file

@ -0,0 +1,56 @@
// Copyright 2015 CoreOS, Inc.
//
// 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.
//
// +build linux
package dlopen
// #include <string.h>
// #include <stdlib.h>
//
// int
// my_strlen(void *f, const char *s)
// {
// size_t (*strlen)(const char *);
//
// strlen = (size_t (*)(const char *))f;
// return strlen(s);
// }
import "C"
import (
"fmt"
"unsafe"
)
func strlen(libs []string, s string) (int, error) {
h, err := GetHandle(libs)
if err != nil {
return -1, fmt.Errorf(`couldn't get a handle to the library: %v`, err)
}
defer h.Close()
f := "strlen"
cs := C.CString(s)
defer C.free(unsafe.Pointer(cs))
strlen, err := h.GetSymbolPointer(f)
if err != nil {
return -1, fmt.Errorf(`couldn't get symbol %q: %v`, f, err)
}
len := C.my_strlen(strlen, cs)
return int(len), nil
}

View file

@ -1,4 +1,5 @@
Copyright (c) 2014-2017 The Docker & Go Authors. All rights reserved.
Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
Copyright (C) 2017 SUSE LLC. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are

View file

@ -0,0 +1,65 @@
## `filepath-securejoin` ##
[![Build Status](https://travis-ci.org/cyphar/filepath-securejoin.svg?branch=master)](https://travis-ci.org/cyphar/filepath-securejoin)
An implementation of `SecureJoin`, a [candidate for inclusion in the Go
standard library][go#20126]. The purpose of this function is to be a "secure"
alternative to `filepath.Join`, and in particular it provides certain
guarantees that are not provided by `filepath.Join`.
This is the function prototype:
```go
func SecureJoin(root, unsafePath string) (string, error)
```
This library **guarantees** the following:
* If no error is set, the resulting string **must** be a child path of
`SecureJoin` and will not contain any symlink path components (they will all
be expanded).
* When expanding symlinks, all symlink path components **must** be resolved
relative to the provided root. In particular, this can be considered a
userspace implementation of how `chroot(2)` operates on file paths. Note that
these symlinks will **not** be expanded lexically (`filepath.Clean` is not
called on the input before processing).
* Non-existant path components are unaffected by `SecureJoin` (similar to
`filepath.EvalSymlinks`'s semantics).
* The returned path will always be `filepath.Clean`ed and thus not contain any
`..` components.
A (trivial) implementation of this function on GNU/Linux systems could be done
with the following (note that this requires root privileges and is far more
opaque than the implementation in this library, and also requires that
`readlink` is inside the `root` path):
```go
package securejoin
import (
"os/exec"
"path/filepath"
)
func SecureJoin(root, unsafePath string) (string, error) {
unsafePath = string(filepath.Separator) + unsafePath
cmd := exec.Command("chroot", root,
"readlink", "--canonicalize-missing", "--no-newline", unsafePath)
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
expanded := string(output)
return filepath.Join(root, expanded), nil
}
```
[go#20126]: https://github.com/golang/go/issues/20126
### License ###
The license of this project is the same as Go, which is a BSD 3-clause license
available in the `LICENSE` file.

View file

@ -0,0 +1,135 @@
// Copyright (C) 2014-2015 Docker Inc & Go Authors. All rights reserved.
// Copyright (C) 2017 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package securejoin is an implementation of the hopefully-soon-to-be-included
// SecureJoin helper that is meant to be part of the "path/filepath" package.
// The purpose of this project is to provide a PoC implementation to make the
// SecureJoin proposal (https://github.com/golang/go/issues/20126) more
// tangible.
package securejoin
import (
"bytes"
"fmt"
"os"
"path/filepath"
"strings"
"syscall"
"github.com/pkg/errors"
)
// ErrSymlinkLoop is returned by SecureJoinVFS when too many symlinks have been
// evaluated in attempting to securely join the two given paths.
var ErrSymlinkLoop = fmt.Errorf("SecureJoin: too many links")
// IsNotExist tells you if err is an error that implies that either the path
// accessed does not exist (or path components don't exist). This is
// effectively a more broad version of os.IsNotExist.
func IsNotExist(err error) bool {
// If it's a bone-fide ENOENT just bail.
if os.IsNotExist(errors.Cause(err)) {
return true
}
// Check that it's not actually an ENOTDIR, which in some cases is a more
// convoluted case of ENOENT (usually involving weird paths).
var errno error
switch err := errors.Cause(err).(type) {
case *os.PathError:
errno = err.Err
case *os.LinkError:
errno = err.Err
case *os.SyscallError:
errno = err.Err
}
return errno == syscall.ENOTDIR || errno == syscall.ENOENT
}
// SecureJoinVFS joins the two given path components (similar to Join) except
// that the returned path is guaranteed to be scoped inside the provided root
// path (when evaluated). Any symbolic links in the path are evaluated with the
// given root treated as the root of the filesystem, similar to a chroot. The
// filesystem state is evaluated through the given VFS interface (if nil, the
// standard os.* family of functions are used).
//
// Note that the guarantees provided by this function only apply if the path
// components in the returned string are not modified (in other words are not
// replaced with symlinks on the filesystem) after this function has returned.
// Such a symlink race is necessarily out-of-scope of SecureJoin.
func SecureJoinVFS(root, unsafePath string, vfs VFS) (string, error) {
// Use the os.* VFS implementation if none was specified.
if vfs == nil {
vfs = osVFS{}
}
var path bytes.Buffer
n := 0
for unsafePath != "" {
if n > 255 {
return "", ErrSymlinkLoop
}
// Next path component, p.
i := strings.IndexRune(unsafePath, filepath.Separator)
var p string
if i == -1 {
p, unsafePath = unsafePath, ""
} else {
p, unsafePath = unsafePath[:i], unsafePath[i+1:]
}
// Create a cleaned path, using the lexical semantics of /../a, to
// create a "scoped" path component which can safely be joined to fullP
// for evaluation. At this point, path.String() doesn't contain any
// symlink components.
cleanP := filepath.Clean(string(filepath.Separator) + path.String() + p)
if cleanP == string(filepath.Separator) {
path.Reset()
continue
}
fullP := filepath.Clean(root + cleanP)
// Figure out whether the path is a symlink.
fi, err := vfs.Lstat(fullP)
if err != nil && !IsNotExist(err) {
return "", err
}
// Treat non-existent path components the same as non-symlinks (we
// can't do any better here).
if IsNotExist(err) || fi.Mode()&os.ModeSymlink == 0 {
path.WriteString(p)
path.WriteRune(filepath.Separator)
continue
}
// Only increment when we actually dereference a link.
n++
// It's a symlink, expand it by prepending it to the yet-unparsed path.
dest, err := vfs.Readlink(fullP)
if err != nil {
return "", err
}
// Absolute symlinks reset any work we've already done.
if filepath.IsAbs(dest) {
path.Reset()
}
unsafePath = dest + string(filepath.Separator) + unsafePath
}
// We have to clean path.String() here because it may contain '..'
// components that are entirely lexical, but would be misleading otherwise.
// And finally do a final clean to ensure that root is also lexically
// clean.
fullP := filepath.Clean(string(filepath.Separator) + path.String())
return filepath.Clean(root + fullP), nil
}
// SecureJoin is a wrapper around SecureJoinVFS that just uses the os.* library
// of functions as the VFS. If in doubt, use this function over SecureJoinVFS.
func SecureJoin(root, unsafePath string) (string, error) {
return SecureJoinVFS(root, unsafePath, nil)
}

View file

@ -0,0 +1 @@
github.com/pkg/errors v0.8.0

View file

@ -0,0 +1,41 @@
// Copyright (C) 2017 SUSE LLC. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package securejoin
import "os"
// In future this should be moved into a separate package, because now there
// are several projects (umoci and go-mtree) that are using this sort of
// interface.
// VFS is the minimal interface necessary to use SecureJoinVFS. A nil VFS is
// equivalent to using the standard os.* family of functions. This is mainly
// used for the purposes of mock testing, but also can be used to otherwise use
// SecureJoin with VFS-like system.
type VFS interface {
// Lstat returns a FileInfo describing the named file. If the file is a
// symbolic link, the returned FileInfo describes the symbolic link. Lstat
// makes no attempt to follow the link. These semantics are identical to
// os.Lstat.
Lstat(name string) (os.FileInfo, error)
// Readlink returns the destination of the named symbolic link. These
// semantics are identical to os.Readlink.
Readlink(name string) (string, error)
}
// osVFS is the "nil" VFS, in that it just passes everything through to the os
// module.
type osVFS struct{}
// Lstat returns a FileInfo describing the named file. If the file is a
// symbolic link, the returned FileInfo describes the symbolic link. Lstat
// makes no attempt to follow the link. These semantics are identical to
// os.Lstat.
func (o osVFS) Lstat(name string) (os.FileInfo, error) { return os.Lstat(name) }
// Readlink returns the destination of the named symbolic link. These
// semantics are identical to os.Readlink.
func (o osVFS) Readlink(name string) (string, error) { return os.Readlink(name) }

View file

@ -1,70 +1,38 @@
### Docker users, see [Moby and Docker](https://mobyproject.org/#moby-and-docker) to clarify the relationship between the projects
### Docker maintainers and contributors, see [Transitioning to Moby](#transitioning-to-moby) for more details
The Moby Project
================
![Moby Project logo](docs/static_files/moby-project-logo.png "The Moby Project")
Moby is an open-source project created by Docker to advance the software containerization movement.
It provides a “Lego set” of dozens of components, the framework for assembling them into custom container-based systems, and a place for all container enthusiasts to experiment and exchange ideas.
Moby is an open-source project created by Docker to enable and accelerate software containerization.
# Moby
## Overview
At the core of Moby is a framework to assemble specialized container systems.
It provides:
- A library of containerized components for all vital aspects of a container system: OS, container runtime, orchestration, infrastructure management, networking, storage, security, build, image distribution, etc.
- Tools to assemble the components into runnable artifacts for a variety of platforms and architectures: bare metal (both x86 and Arm); executables for Linux, Mac and Windows; VM images for popular cloud and virtualization providers.
- A set of reference assemblies which can be used as-is, modified, or used as inspiration to create your own.
All Moby components are containers, so creating new components is as easy as building a new OCI-compatible container.
It provides a "Lego set" of toolkit components, the framework for assembling them into custom container-based systems, and a place for all container enthusiasts and professionals to experiment and exchange ideas.
Components include container build tools, a container registry, orchestration tools, a runtime and more, and these can be used as building blocks in conjunction with other tools and projects.
## Principles
Moby is an open project guided by strong principles, but modular, flexible and without too strong an opinion on user experience, so it is open to the community to help set its direction.
The guiding principles are:
Moby is an open project guided by strong principles, aiming to be modular, flexible and without too strong an opinion on user experience.
It is open to the community to help set its direction.
- Modular: the project includes lots of components that have well-defined functions and APIs that work together.
- Batteries included but swappable: Moby includes enough components to build fully featured container system, but its modular architecture ensures that most of the components can be swapped by different implementations.
- Usable security: Moby will provide secure defaults without compromising usability.
- Container centric: Moby is built with containers, for running containers.
With Moby, you should be able to describe all the components of your distributed application, from the high-level configuration files down to the kernel you would like to use and build and deploy it easily.
Moby uses [containerd](https://github.com/containerd/containerd) as the default container runtime.
- Usable security: Moby provides secure defaults without compromising usability.
- Developer focused: The APIs are intended to be functional and useful to build powerful tools.
They are not necessarily intended as end user tools but as components aimed at developers.
Documentation and UX is aimed at developers not end users.
## Audience
Moby is recommended for anyone who wants to assemble a container-based system. This includes:
The Moby Project is intended for engineers, integrators and enthusiasts looking to modify, hack, fix, experiment, invent and build systems based on containers.
It is not for people looking for a commercially supported system, but for people who want to work and learn with open source code.
- Hackers who want to customize or patch their Docker build
- System engineers or integrators building a container system
- Infrastructure providers looking to adapt existing container systems to their environment
- Container enthusiasts who want to experiment with the latest container tech
- Open-source developers looking to test their project in a variety of different systems
- Anyone curious about Docker internals and how its built
## Relationship with Docker
Moby is NOT recommended for:
The components and tools in the Moby Project are initially the open source components that Docker and the community have built for the Docker Project.
New projects can be added if they fit with the community goals. Docker is committed to using Moby as the upstream for the Docker Product.
However, other projects are also encouraged to use Moby as an upstream, and to reuse the components in diverse ways, and all these uses will be treated in the same way. External maintainers and contributors are welcomed.
- Application developers looking for an easy way to run their applications in containers. We recommend Docker CE instead.
- Enterprise IT and development teams looking for a ready-to-use, commercially supported container platform. We recommend Docker EE instead.
- Anyone curious about containers and looking for an easy way to learn. We recommend the [docker.com](https://www.docker.com/) website instead.
# Transitioning to Moby
Docker is transitioning all of its open source collaborations to the Moby project going forward.
During the transition, all open source activity should continue as usual.
We are proposing the following list of changes:
- splitting up the engine into more open components
- removing the docker UI, SDK etc to keep them in the Docker org
- clarifying that the project is not limited to the engine, but to the assembly of all the individual components of the Docker platform
- open-source new tools & components which we currently use to assemble the Docker product, but could benefit the community
- defining an open, community-centric governance inspired by the Fedora project (a very successful example of balancing the needs of the community with the constraints of the primary corporate sponsor)
The Moby project is not intended as a location for support or feature requests for Docker products, but as a place for contributors to work on open source code, fix bugs, and make the code more useful.
The releases are supported by the maintainers, community and users, on a best efforts basis only, and are not intended for customers who want enterprise or commercial support; Docker EE is the appropriate product for these use cases.
-----
@ -82,7 +50,6 @@ violate applicable laws.
For more information, please see https://www.bis.doc.gov
Licensing
=========
Moby is licensed under the Apache License, Version 2.0. See

View file

@ -10,7 +10,7 @@ It consists of various components in this repository:
- `client/` The Go client used by the command-line client. It can also be used by third-party Go programs.
- `daemon/` The daemon, which serves the API.
## Swagger definition
## Swagger definition
The API is defined by the [Swagger](http://swagger.io/specification/) definition in `api/swagger.yaml`. This definition can be used to:
@ -20,7 +20,7 @@ The API is defined by the [Swagger](http://swagger.io/specification/) definition
## Updating the API documentation
The API documentation is generated entirely from `api/swagger.yaml`. If you make updates to the API, you'll need to edit this file to represent the change in the documentation.
The API documentation is generated entirely from `api/swagger.yaml`. If you make updates to the API, edit this file to represent the change in the documentation.
The file is split into two main sections:
@ -29,9 +29,9 @@ The file is split into two main sections:
To make an edit, first look for the endpoint you want to edit under `paths`, then make the required edits. Endpoints may reference reusable objects with `$ref`, which can be found in the `definitions` section.
There is hopefully enough example material in the file for you to copy a similar pattern from elsewhere in the file (e.g. adding new fields or endpoints), but for the full reference, see the [Swagger specification](https://github.com/docker/docker/issues/27919)
There is hopefully enough example material in the file for you to copy a similar pattern from elsewhere in the file (e.g. adding new fields or endpoints), but for the full reference, see the [Swagger specification](https://github.com/docker/docker/issues/27919).
`swagger.yaml` is validated by `hack/validate/swagger` to ensure it is a valid Swagger definition. This is useful for when you are making edits to ensure you are doing the right thing.
`swagger.yaml` is validated by `hack/validate/swagger` to ensure it is a valid Swagger definition. This is useful when making edits to ensure you are doing the right thing.
## Viewing the API documentation

View file

@ -1,4 +1,4 @@
package types
package types // import "github.com/docker/docker/api/types"
// AuthConfig contains authorization information for connecting to a Registry
type AuthConfig struct {

View file

@ -1,4 +1,4 @@
package blkiodev
package blkiodev // import "github.com/docker/docker/api/types/blkiodev"
import "fmt"

View file

@ -1,4 +1,4 @@
package types
package types // import "github.com/docker/docker/api/types"
import (
"bufio"
@ -7,7 +7,7 @@ import (
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters"
units "github.com/docker/go-units"
"github.com/docker/go-units"
)
// CheckpointCreateOptions holds parameters to create a checkpoint from a container
@ -74,6 +74,7 @@ type ContainerLogsOptions struct {
ShowStdout bool
ShowStderr bool
Since string
Until string
Timestamps bool
Follow bool
Tail string

View file

@ -1,4 +1,4 @@
package types
package types // import "github.com/docker/docker/api/types"
import (
"github.com/docker/docker/api/types/container"
@ -25,19 +25,6 @@ type ContainerRmConfig struct {
ForceRemove, RemoveVolume, RemoveLink bool
}
// ContainerCommitConfig contains build configs for commit operation,
// and is used when making a commit with the current state of the container.
type ContainerCommitConfig struct {
Pause bool
Repo string
Tag string
Author string
Comment string
// merge container config into commit config before commit
MergeConfigs bool
Config *container.Config
}
// ExecConfig is a small subset of the Config struct that holds the configuration
// for the exec feature of docker.
type ExecConfig struct {
@ -50,6 +37,7 @@ type ExecConfig struct {
Detach bool // Execute in detach mode
DetachKeys string // Escape keys for detach
Env []string // Environment variables
WorkingDir string // Working directory
Cmd []string // Execution commands and args
}

View file

@ -1,4 +1,4 @@
package container
package container // import "github.com/docker/docker/api/types/container"
import (
"time"

View file

@ -7,7 +7,7 @@ package container
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerChangeResponseItem container change response item
// ContainerChangeResponseItem change item in response to ContainerChanges operation
// swagger:model ContainerChangeResponseItem
type ContainerChangeResponseItem struct {

View file

@ -7,7 +7,7 @@ package container
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerCreateCreatedBody container create created body
// ContainerCreateCreatedBody OK response to ContainerCreate operation
// swagger:model ContainerCreateCreatedBody
type ContainerCreateCreatedBody struct {

View file

@ -7,7 +7,7 @@ package container
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerTopOKBody container top o k body
// ContainerTopOKBody OK response to ContainerTop operation
// swagger:model ContainerTopOKBody
type ContainerTopOKBody struct {

View file

@ -7,7 +7,7 @@ package container
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerUpdateOKBody container update o k body
// ContainerUpdateOKBody OK response to ContainerUpdate operation
// swagger:model ContainerUpdateOKBody
type ContainerUpdateOKBody struct {

View file

@ -7,10 +7,22 @@ package container
// See hack/generate-swagger-api.sh
// ----------------------------------------------------------------------------
// ContainerWaitOKBody container wait o k body
// ContainerWaitOKBodyError container waiting error, if any
// swagger:model ContainerWaitOKBodyError
type ContainerWaitOKBodyError struct {
// Details of an error
Message string `json:"Message,omitempty"`
}
// ContainerWaitOKBody OK response to ContainerWait operation
// swagger:model ContainerWaitOKBody
type ContainerWaitOKBody struct {
// error
// Required: true
Error *ContainerWaitOKBodyError `json:"Error"`
// Exit code of the container
// Required: true
StatusCode int64 `json:"StatusCode"`

View file

@ -1,4 +1,4 @@
package container
package container // import "github.com/docker/docker/api/types/container"
import (
"strings"
@ -20,6 +20,27 @@ func (i Isolation) IsDefault() bool {
return strings.ToLower(string(i)) == "default" || string(i) == ""
}
// IsHyperV indicates the use of a Hyper-V partition for isolation
func (i Isolation) IsHyperV() bool {
return strings.ToLower(string(i)) == "hyperv"
}
// IsProcess indicates the use of process isolation
func (i Isolation) IsProcess() bool {
return strings.ToLower(string(i)) == "process"
}
const (
// IsolationEmpty is unspecified (same behavior as default)
IsolationEmpty = Isolation("")
// IsolationDefault is the default isolation mode on current daemon
IsolationDefault = Isolation("default")
// IsolationProcess is process isolation mode
IsolationProcess = Isolation("process")
// IsolationHyperV is HyperV isolation mode
IsolationHyperV = Isolation("hyperv")
)
// IpcMode represents the container ipc stack.
type IpcMode string
@ -380,6 +401,12 @@ type HostConfig struct {
// Mounts specs used by the container
Mounts []mount.Mount `json:",omitempty"`
// MaskedPaths is the list of paths to be masked inside the container (this overrides the default set of paths)
MaskedPaths []string
// ReadonlyPaths is the list of paths to be set as read-only inside the container (this overrides the default set of paths)
ReadonlyPaths []string
// Run a custom init inside the container, if null, use the daemon's configured settings
Init *bool `json:",omitempty"`
}

View file

@ -1,6 +1,6 @@
// +build !windows
package container
package container // import "github.com/docker/docker/api/types/container"
// IsValid indicates if an isolation technology is valid
func (i Isolation) IsValid() bool {

View file

@ -1,8 +1,4 @@
package container
import (
"strings"
)
package container // import "github.com/docker/docker/api/types/container"
// IsBridge indicates whether container uses the bridge network stack
// in windows it is given the name NAT
@ -21,16 +17,6 @@ func (n NetworkMode) IsUserDefined() bool {
return !n.IsDefault() && !n.IsNone() && !n.IsBridge() && !n.IsContainer()
}
// IsHyperV indicates the use of a Hyper-V partition for isolation
func (i Isolation) IsHyperV() bool {
return strings.ToLower(string(i)) == "hyperv"
}
// IsProcess indicates the use of process isolation
func (i Isolation) IsProcess() bool {
return strings.ToLower(string(i)) == "process"
}
// IsValid indicates if an isolation technology is valid
func (i Isolation) IsValid() bool {
return i.IsDefault() || i.IsHyperV() || i.IsProcess()

View file

@ -1,4 +1,4 @@
package container
package container // import "github.com/docker/docker/api/types/container"
// WaitCondition is a type used to specify a container state for which
// to wait.

View file

@ -1,7 +1,7 @@
/*Package filters provides tools for encoding a mapping of keys to a set of
multiple values.
*/
package filters
package filters // import "github.com/docker/docker/api/types/filters"
import (
"encoding/json"

View file

@ -1,4 +1,4 @@
package mount
package mount // import "github.com/docker/docker/api/types/mount"
import (
"os"

View file

@ -1,4 +1,4 @@
package network
package network // import "github.com/docker/docker/api/types/network"
// Address represents an IP address
type Address struct {

View file

@ -121,6 +121,9 @@ type PluginConfigArgs struct {
// swagger:model PluginConfigInterface
type PluginConfigInterface struct {
// Protocol to use for clients connecting to the plugin.
ProtocolScheme string `json:"ProtocolScheme,omitempty"`
// socket
// Required: true
Socket string `json:"Socket"`

View file

@ -1,4 +1,4 @@
package types
package types // import "github.com/docker/docker/api/types"
import (
"encoding/json"

View file

@ -7,7 +7,7 @@ package types
// swagger:model Port
type Port struct {
// IP
// Host IP address that the container's port is mapped to
IP string `json:"IP,omitempty"`
// Port on the container

View file

@ -1,4 +1,4 @@
package registry
package registry // import "github.com/docker/docker/api/types/registry"
// ----------------------------------------------------------------------------
// DO NOT EDIT THIS FILE

View file

@ -1,4 +1,4 @@
package registry
package registry // import "github.com/docker/docker/api/types/registry"
import (
"encoding/json"

View file

@ -1,4 +1,4 @@
package types
package types // import "github.com/docker/docker/api/types"
// Seccomp represents the config for a seccomp profile for syscall restriction.
type Seccomp struct {

View file

@ -1,6 +1,6 @@
// Package types is used for API stability in the types and response to the
// consumers of the API stats endpoint.
package types
package types // import "github.com/docker/docker/api/types"
import "time"

View file

@ -1,4 +1,4 @@
package strslice
package strslice // import "github.com/docker/docker/api/types/strslice"
import "encoding/json"

View file

@ -1,4 +1,4 @@
package swarm
package swarm // import "github.com/docker/docker/api/types/swarm"
import "time"

View file

@ -1,4 +1,4 @@
package swarm
package swarm // import "github.com/docker/docker/api/types/swarm"
import "os"
@ -13,6 +13,10 @@ type Config struct {
type ConfigSpec struct {
Annotations
Data []byte `json:",omitempty"`
// Templating controls whether and how to evaluate the config payload as
// a template. If it is not set, no templating is used.
Templating *Driver `json:",omitempty"`
}
// ConfigReferenceFileTarget is a file target in a config reference

View file

@ -1,4 +1,4 @@
package swarm
package swarm // import "github.com/docker/docker/api/types/swarm"
import (
"time"
@ -55,6 +55,7 @@ type ContainerSpec struct {
User string `json:",omitempty"`
Groups []string `json:",omitempty"`
Privileges *Privileges `json:",omitempty"`
Init *bool `json:",omitempty"`
StopSignal string `json:",omitempty"`
TTY bool `json:",omitempty"`
OpenStdin bool `json:",omitempty"`
@ -65,8 +66,9 @@ type ContainerSpec struct {
// The format of extra hosts on swarmkit is specified in:
// http://man7.org/linux/man-pages/man5/hosts.5.html
// IP_address canonical_hostname [aliases...]
Hosts []string `json:",omitempty"`
DNSConfig *DNSConfig `json:",omitempty"`
Secrets []*SecretReference `json:",omitempty"`
Configs []*ConfigReference `json:",omitempty"`
Hosts []string `json:",omitempty"`
DNSConfig *DNSConfig `json:",omitempty"`
Secrets []*SecretReference `json:",omitempty"`
Configs []*ConfigReference `json:",omitempty"`
Isolation container.Isolation `json:",omitempty"`
}

View file

@ -1,4 +1,4 @@
package swarm
package swarm // import "github.com/docker/docker/api/types/swarm"
import (
"github.com/docker/docker/api/types/network"
@ -62,6 +62,8 @@ const (
PortConfigProtocolTCP PortConfigProtocol = "tcp"
// PortConfigProtocolUDP UDP
PortConfigProtocolUDP PortConfigProtocol = "udp"
// PortConfigProtocolSCTP SCTP
PortConfigProtocolSCTP PortConfigProtocol = "sctp"
)
// EndpointVirtualIP represents the virtual ip of a port.

View file

@ -1,4 +1,4 @@
package swarm
package swarm // import "github.com/docker/docker/api/types/swarm"
// Node represents a node.
type Node struct {

View file

@ -1,4 +1,4 @@
package swarm
package swarm // import "github.com/docker/docker/api/types/swarm"
// RuntimeType is the type of runtime used for the TaskSpec
type RuntimeType string
@ -11,9 +11,17 @@ const (
RuntimeContainer RuntimeType = "container"
// RuntimePlugin is the plugin based runtime
RuntimePlugin RuntimeType = "plugin"
// RuntimeNetworkAttachment is the network attachment runtime
RuntimeNetworkAttachment RuntimeType = "attachment"
// RuntimeURLContainer is the proto url for the container type
RuntimeURLContainer RuntimeURL = "types.docker.com/RuntimeContainer"
// RuntimeURLPlugin is the proto url for the plugin type
RuntimeURLPlugin RuntimeURL = "types.docker.com/RuntimePlugin"
)
// NetworkAttachmentSpec represents the runtime spec type for network
// attachment tasks
type NetworkAttachmentSpec struct {
ContainerID string
}

View file

@ -1,3 +1,3 @@
//go:generate protoc -I . --gogofast_out=import_path=github.com/docker/docker/api/types/swarm/runtime:. plugin.proto
package runtime
package runtime // import "github.com/docker/docker/api/types/swarm/runtime"

View file

@ -1,5 +1,7 @@
syntax = "proto3";
option go_package = "github.com/docker/docker/api/types/swarm/runtime;runtime";
// PluginSpec defines the base payload which clients can specify for creating
// a service with the plugin runtime.
message PluginSpec {

View file

@ -1,4 +1,4 @@
package swarm
package swarm // import "github.com/docker/docker/api/types/swarm"
import "os"
@ -14,6 +14,10 @@ type SecretSpec struct {
Annotations
Data []byte `json:",omitempty"`
Driver *Driver `json:",omitempty"` // name of the secrets driver used to fetch the secret's value from an external secret store
// Templating controls whether and how to evaluate the secret payload as
// a template. If it is not set, no templating is used.
Templating *Driver `json:",omitempty"`
}
// SecretReferenceFileTarget is a file target in a secret reference

View file

@ -1,4 +1,4 @@
package swarm
package swarm // import "github.com/docker/docker/api/types/swarm"
import "time"

View file

@ -1,4 +1,4 @@
package swarm
package swarm // import "github.com/docker/docker/api/types/swarm"
import "time"

View file

@ -1,4 +1,4 @@
package swarm
package swarm // import "github.com/docker/docker/api/types/swarm"
import (
"time"
@ -36,6 +36,10 @@ const (
TaskStateFailed TaskState = "failed"
// TaskStateRejected REJECTED
TaskStateRejected TaskState = "rejected"
// TaskStateRemove REMOVE
TaskStateRemove TaskState = "remove"
// TaskStateOrphaned ORPHANED
TaskStateOrphaned TaskState = "orphaned"
)
// Task represents a task.
@ -56,10 +60,13 @@ type Task struct {
// TaskSpec represents the spec of a task.
type TaskSpec struct {
// ContainerSpec and PluginSpec are mutually exclusive.
// PluginSpec will only be used when the `Runtime` field is set to `plugin`
ContainerSpec *ContainerSpec `json:",omitempty"`
PluginSpec *runtime.PluginSpec `json:",omitempty"`
// ContainerSpec, NetworkAttachmentSpec, and PluginSpec are mutually exclusive.
// PluginSpec is only used when the `Runtime` field is set to `plugin`
// NetworkAttachmentSpec is used if the `Runtime` field is set to
// `attachment`.
ContainerSpec *ContainerSpec `json:",omitempty"`
PluginSpec *runtime.PluginSpec `json:",omitempty"`
NetworkAttachmentSpec *NetworkAttachmentSpec `json:",omitempty"`
Resources *ResourceRequirements `json:",omitempty"`
RestartPolicy *RestartPolicy `json:",omitempty"`
@ -162,19 +169,19 @@ const (
// TaskStatus represents the status of a task.
type TaskStatus struct {
Timestamp time.Time `json:",omitempty"`
State TaskState `json:",omitempty"`
Message string `json:",omitempty"`
Err string `json:",omitempty"`
ContainerStatus ContainerStatus `json:",omitempty"`
PortStatus PortStatus `json:",omitempty"`
Timestamp time.Time `json:",omitempty"`
State TaskState `json:",omitempty"`
Message string `json:",omitempty"`
Err string `json:",omitempty"`
ContainerStatus *ContainerStatus `json:",omitempty"`
PortStatus PortStatus `json:",omitempty"`
}
// ContainerStatus represents the status of a container.
type ContainerStatus struct {
ContainerID string `json:",omitempty"`
PID int `json:",omitempty"`
ExitCode int `json:",omitempty"`
ContainerID string
PID int
ExitCode int
}
// PortStatus represents the port status of a task's host ports whose

View file

@ -1,4 +1,4 @@
package types
package types // import "github.com/docker/docker/api/types"
import (
"errors"
@ -15,7 +15,6 @@ import (
"github.com/docker/docker/api/types/registry"
"github.com/docker/docker/api/types/swarm"
"github.com/docker/go-connections/nat"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)
// RootFS returns Image's RootFS description including the layer IDs.
@ -108,9 +107,21 @@ type Ping struct {
Experimental bool
}
// ComponentVersion describes the version information for a specific component.
type ComponentVersion struct {
Name string
Version string
Details map[string]string `json:",omitempty"`
}
// Version contains response of Engine API:
// GET "/version"
type Version struct {
Platform struct{ Name string } `json:",omitempty"`
Components []ComponentVersion `json:",omitempty"`
// The following fields are deprecated, they relate to the Engine component and are kept for backwards compatibility
Version string
APIVersion string `json:"ApiVersion"`
MinAPIVersion string `json:"MinAPIVersion,omitempty"`
@ -328,7 +339,7 @@ type ContainerJSONBase struct {
Name string
RestartCount int
Driver string
Platform specs.Platform
Platform string
MountLabel string
ProcessLabel string
AppArmorProfile string

View file

@ -1,4 +1,4 @@
package versions
package versions // import "github.com/docker/docker/api/types/versions"
import (
"strconv"

View file

@ -0,0 +1,84 @@
package opts
import (
"encoding/csv"
"encoding/json"
"fmt"
"strconv"
"strings"
types "github.com/docker/libnetwork/ipamutils"
)
// PoolsOpt is a Value type for parsing the default address pools definitions
type PoolsOpt struct {
values []*types.NetworkToSplit
}
// UnmarshalJSON fills values structure info from JSON input
func (p *PoolsOpt) UnmarshalJSON(raw []byte) error {
return json.Unmarshal(raw, &(p.values))
}
// Set predefined pools
func (p *PoolsOpt) Set(value string) error {
csvReader := csv.NewReader(strings.NewReader(value))
fields, err := csvReader.Read()
if err != nil {
return err
}
poolsDef := types.NetworkToSplit{}
for _, field := range fields {
parts := strings.SplitN(field, "=", 2)
if len(parts) != 2 {
return fmt.Errorf("invalid field '%s' must be a key=value pair", field)
}
key := strings.ToLower(parts[0])
value := strings.ToLower(parts[1])
switch key {
case "base":
poolsDef.Base = value
case "size":
size, err := strconv.Atoi(value)
if err != nil {
return fmt.Errorf("invalid size value: %q (must be integer): %v", value, err)
}
poolsDef.Size = size
default:
return fmt.Errorf("unexpected key '%s' in '%s'", key, field)
}
}
p.values = append(p.values, &poolsDef)
return nil
}
// Type returns the type of this option
func (p *PoolsOpt) Type() string {
return "pool-options"
}
// String returns a string repr of this option
func (p *PoolsOpt) String() string {
var pools []string
for _, pool := range p.values {
repr := fmt.Sprintf("%s %d", pool.Base, pool.Size)
pools = append(pools, repr)
}
return strings.Join(pools, ", ")
}
// Value returns the mounts
func (p *PoolsOpt) Value() []*types.NetworkToSplit {
return p.values
}
// Name returns the flag name of this option
func (p *PoolsOpt) Name() string {
return "default-address-pools"
}

View file

@ -1,4 +1,4 @@
package opts
package opts // import "github.com/docker/docker/opts"
import (
"fmt"

View file

@ -1,4 +1,4 @@
package opts
package opts // import "github.com/docker/docker/opts"
import (
"fmt"
@ -29,9 +29,9 @@ var (
// ValidateHost validates that the specified string is a valid host and returns it.
func ValidateHost(val string) (string, error) {
host := strings.TrimSpace(val)
// The empty string means default and is not handled by parseDockerDaemonHost
// The empty string means default and is not handled by parseDaemonHost
if host != "" {
_, err := parseDockerDaemonHost(host)
_, err := parseDaemonHost(host)
if err != nil {
return val, err
}
@ -52,7 +52,7 @@ func ParseHost(defaultToTLS bool, val string) (string, error) {
}
} else {
var err error
host, err = parseDockerDaemonHost(host)
host, err = parseDaemonHost(host)
if err != nil {
return val, err
}
@ -60,9 +60,9 @@ func ParseHost(defaultToTLS bool, val string) (string, error) {
return host, nil
}
// parseDockerDaemonHost parses the specified address and returns an address that will be used as the host.
// parseDaemonHost parses the specified address and returns an address that will be used as the host.
// Depending of the address specified, this may return one of the global Default* strings defined in hosts.go.
func parseDockerDaemonHost(addr string) (string, error) {
func parseDaemonHost(addr string) (string, error) {
addrParts := strings.SplitN(addr, "://", 2)
if len(addrParts) == 1 && addrParts[0] != "" {
addrParts = []string{"tcp", addrParts[0]}

View file

@ -1,6 +1,6 @@
// +build !windows
package opts
package opts // import "github.com/docker/docker/opts"
import "fmt"

View file

@ -1,6 +1,4 @@
// +build windows
package opts
package opts // import "github.com/docker/docker/opts"
// DefaultHost constant defines the default host string used by docker on Windows
var DefaultHost = "npipe://" + DefaultNamedPipe

View file

@ -1,4 +1,4 @@
package opts
package opts // import "github.com/docker/docker/opts"
import (
"fmt"

View file

@ -1,4 +1,4 @@
package opts
package opts // import "github.com/docker/docker/opts"
import (
"fmt"
@ -7,7 +7,7 @@ import (
"regexp"
"strings"
units "github.com/docker/go-units"
"github.com/docker/go-units"
)
var (
@ -52,7 +52,7 @@ func (opts *ListOpts) Set(value string) error {
}
value = v
}
(*opts.values) = append((*opts.values), value)
*opts.values = append(*opts.values, value)
return nil
}
@ -60,7 +60,7 @@ func (opts *ListOpts) Set(value string) error {
func (opts *ListOpts) Delete(key string) {
for i, k := range *opts.values {
if k == key {
(*opts.values) = append((*opts.values)[:i], (*opts.values)[i+1:]...)
*opts.values = append((*opts.values)[:i], (*opts.values)[i+1:]...)
return
}
}
@ -78,7 +78,7 @@ func (opts *ListOpts) GetMap() map[string]struct{} {
// GetAll returns the values of slice.
func (opts *ListOpts) GetAll() []string {
return (*opts.values)
return *opts.values
}
// GetAllOrEmpty returns the values of the slice
@ -103,7 +103,7 @@ func (opts *ListOpts) Get(key string) bool {
// Len returns the amount of element in the slice.
func (opts *ListOpts) Len() int {
return len((*opts.values))
return len(*opts.values)
}
// Type returns a string name for this Option type
@ -263,6 +263,16 @@ func ValidateLabel(val string) (string, error) {
return val, nil
}
// ValidateSingleGenericResource validates that a single entry in the
// generic resource list is valid.
// i.e 'GPU=UID1' is valid however 'GPU:UID1' or 'UID1' isn't
func ValidateSingleGenericResource(val string) (string, error) {
if strings.Count(val, "=") < 1 {
return "", fmt.Errorf("invalid node-generic-resource format `%s` expected `name=value`", val)
}
return val, nil
}
// ParseLink parses and validates the specified string as a link format (name:alias)
func ParseLink(val string) (string, string, error) {
if val == "" {

Some files were not shown because too many files have changed in this diff Show more