mirror of
https://github.com/containers/skopeo.git
synced 2025-09-17 15:30:38 +00:00
switch to vndr
vndr is almost exactly the same as our old good hack/vendor.sh. Except it's cleaner and it allows to re-vendor just one dependency if needed (which we do a lot for containers/image). Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
19
vendor/github.com/containers/storage/NOTICE
generated
vendored
Normal file
19
vendor/github.com/containers/storage/NOTICE
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Docker
|
||||
Copyright 2012-2016 Docker, Inc.
|
||||
|
||||
This product includes software developed at Docker, Inc. (https://www.docker.com).
|
||||
|
||||
This product contains software (https://github.com/kr/pty) developed
|
||||
by Keith Rarick, licensed under the MIT License.
|
||||
|
||||
The following is courtesy of our legal counsel:
|
||||
|
||||
|
||||
Use and transfer of Docker may be subject to certain restrictions by the
|
||||
United States and other governments.
|
||||
It is your responsibility to ensure that your use and/or transfer does not
|
||||
violate applicable laws.
|
||||
|
||||
For more information, please see https://www.bis.doc.gov
|
||||
|
||||
See also https://www.apache.org/dev/crypto.html and/or seek legal counsel.
|
779
vendor/github.com/containers/storage/drivers/windows/windows.go
generated
vendored
Normal file
779
vendor/github.com/containers/storage/drivers/windows/windows.go
generated
vendored
Normal file
@@ -0,0 +1,779 @@
|
||||
//+build windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/Microsoft/go-winio"
|
||||
"github.com/Microsoft/go-winio/archive/tar"
|
||||
"github.com/Microsoft/go-winio/backuptar"
|
||||
"github.com/Microsoft/hcsshim"
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/containers/storage/drivers"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/ioutils"
|
||||
"github.com/containers/storage/pkg/longpath"
|
||||
"github.com/containers/storage/pkg/reexec"
|
||||
"github.com/containers/storage/pkg/system"
|
||||
"github.com/vbatts/tar-split/tar/storage"
|
||||
)
|
||||
|
||||
// filterDriver is an HCSShim driver type for the Windows Filter driver.
|
||||
const filterDriver = 1
|
||||
|
||||
// init registers the windows graph drivers to the register.
|
||||
func init() {
|
||||
graphdriver.Register("windowsfilter", InitFilter)
|
||||
reexec.Register("docker-windows-write-layer", writeLayer)
|
||||
}
|
||||
|
||||
type checker struct {
|
||||
}
|
||||
|
||||
func (c *checker) IsMounted(path string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Driver represents a windows graph driver.
|
||||
type Driver struct {
|
||||
// info stores the shim driver information
|
||||
info hcsshim.DriverInfo
|
||||
ctr *graphdriver.RefCounter
|
||||
// it is safe for windows to use a cache here because it does not support
|
||||
// restoring containers when the daemon dies.
|
||||
cacheMu sync.Mutex
|
||||
cache map[string]string
|
||||
}
|
||||
|
||||
func isTP5OrOlder() bool {
|
||||
return system.GetOSVersion().Build <= 14300
|
||||
}
|
||||
|
||||
// InitFilter returns a new Windows storage filter driver.
|
||||
func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
||||
logrus.Debugf("WindowsGraphDriver InitFilter at %s", home)
|
||||
d := &Driver{
|
||||
info: hcsshim.DriverInfo{
|
||||
HomeDir: home,
|
||||
Flavour: filterDriver,
|
||||
},
|
||||
cache: make(map[string]string),
|
||||
ctr: graphdriver.NewRefCounter(&checker{}),
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// String returns the string representation of a driver. This should match
|
||||
// the name the graph driver has been registered with.
|
||||
func (d *Driver) String() string {
|
||||
return "windowsfilter"
|
||||
}
|
||||
|
||||
// Status returns the status of the driver.
|
||||
func (d *Driver) Status() [][2]string {
|
||||
return [][2]string{
|
||||
{"Windows", ""},
|
||||
}
|
||||
}
|
||||
|
||||
// Exists returns true if the given id is registered with this driver.
|
||||
func (d *Driver) Exists(id string) bool {
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
result, err := hcsshim.LayerExists(d.info, rID)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CreateReadWrite creates a layer that is writable for use as a container
|
||||
// file system.
|
||||
func (d *Driver) CreateReadWrite(id, parent, mountLabel string, storageOpt map[string]string) error {
|
||||
return d.create(id, parent, mountLabel, false, storageOpt)
|
||||
}
|
||||
|
||||
// Create creates a new read-only layer with the given id.
|
||||
func (d *Driver) Create(id, parent, mountLabel string, storageOpt map[string]string) error {
|
||||
return d.create(id, parent, mountLabel, true, storageOpt)
|
||||
}
|
||||
|
||||
func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt map[string]string) error {
|
||||
if len(storageOpt) != 0 {
|
||||
return fmt.Errorf("--storage-opt is not supported for windows")
|
||||
}
|
||||
|
||||
rPId, err := d.resolveID(parent)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
parentChain, err := d.getLayerChain(rPId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var layerChain []string
|
||||
|
||||
if rPId != "" {
|
||||
parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(parentPath, "Files")); err == nil {
|
||||
// This is a legitimate parent layer (not the empty "-init" layer),
|
||||
// so include it in the layer chain.
|
||||
layerChain = []string{parentPath}
|
||||
}
|
||||
}
|
||||
|
||||
layerChain = append(layerChain, parentChain...)
|
||||
|
||||
if readOnly {
|
||||
if err := hcsshim.CreateLayer(d.info, id, rPId); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
var parentPath string
|
||||
if len(layerChain) != 0 {
|
||||
parentPath = layerChain[0]
|
||||
}
|
||||
|
||||
if isTP5OrOlder() {
|
||||
// Pre-create the layer directory, providing an ACL to give the Hyper-V Virtual Machines
|
||||
// group access. This is necessary to ensure that Hyper-V containers can access the
|
||||
// virtual machine data. This is not necessary post-TP5.
|
||||
path, err := syscall.UTF16FromString(filepath.Join(d.info.HomeDir, id))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Give system and administrators full control, and VMs read, write, and execute.
|
||||
// Mark these ACEs as inherited.
|
||||
sd, err := winio.SddlToSecurityDescriptor("D:(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FRFWFX;;;S-1-5-83-0)")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = syscall.CreateDirectory(&path[0], &syscall.SecurityAttributes{
|
||||
Length: uint32(unsafe.Sizeof(syscall.SecurityAttributes{})),
|
||||
SecurityDescriptor: uintptr(unsafe.Pointer(&sd[0])),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := hcsshim.CreateSandboxLayer(d.info, id, parentPath, layerChain); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Lstat(d.dir(parent)); err != nil {
|
||||
if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
|
||||
logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2)
|
||||
}
|
||||
return fmt.Errorf("Cannot create layer with missing parent %s: %s", parent, err)
|
||||
}
|
||||
|
||||
if err := d.setLayerChain(id, layerChain); err != nil {
|
||||
if err2 := hcsshim.DestroyLayer(d.info, id); err2 != nil {
|
||||
logrus.Warnf("Failed to DestroyLayer %s: %s", id, err2)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dir returns the absolute path to the layer.
|
||||
func (d *Driver) dir(id string) string {
|
||||
return filepath.Join(d.info.HomeDir, filepath.Base(id))
|
||||
}
|
||||
|
||||
// Remove unmounts and removes the dir information.
|
||||
func (d *Driver) Remove(id string) error {
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
os.RemoveAll(filepath.Join(d.info.HomeDir, "sysfile-backups", rID)) // ok to fail
|
||||
return hcsshim.DestroyLayer(d.info, rID)
|
||||
}
|
||||
|
||||
// Get returns the rootfs path for the id. This will mount the dir at it's given path.
|
||||
func (d *Driver) Get(id, mountLabel string) (string, error) {
|
||||
logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel)
|
||||
var dir string
|
||||
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if count := d.ctr.Increment(rID); count > 1 {
|
||||
return d.cache[rID], nil
|
||||
}
|
||||
|
||||
// Getting the layer paths must be done outside of the lock.
|
||||
layerChain, err := d.getLayerChain(rID)
|
||||
if err != nil {
|
||||
d.ctr.Decrement(rID)
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := hcsshim.ActivateLayer(d.info, rID); err != nil {
|
||||
d.ctr.Decrement(rID)
|
||||
return "", err
|
||||
}
|
||||
if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
|
||||
d.ctr.Decrement(rID)
|
||||
if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
|
||||
logrus.Warnf("Failed to Deactivate %s: %s", id, err)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
|
||||
mountPath, err := hcsshim.GetLayerMountPath(d.info, rID)
|
||||
if err != nil {
|
||||
d.ctr.Decrement(rID)
|
||||
if err2 := hcsshim.DeactivateLayer(d.info, rID); err2 != nil {
|
||||
logrus.Warnf("Failed to Deactivate %s: %s", id, err)
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
d.cacheMu.Lock()
|
||||
d.cache[rID] = mountPath
|
||||
d.cacheMu.Unlock()
|
||||
|
||||
// If the layer has a mount path, use that. Otherwise, use the
|
||||
// folder path.
|
||||
if mountPath != "" {
|
||||
dir = mountPath
|
||||
} else {
|
||||
dir = d.dir(id)
|
||||
}
|
||||
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
// Put adds a new layer to the driver.
|
||||
func (d *Driver) Put(id string) error {
|
||||
logrus.Debugf("WindowsGraphDriver Put() id %s", id)
|
||||
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if count := d.ctr.Decrement(rID); count > 0 {
|
||||
return nil
|
||||
}
|
||||
d.cacheMu.Lock()
|
||||
delete(d.cache, rID)
|
||||
d.cacheMu.Unlock()
|
||||
|
||||
if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
|
||||
return err
|
||||
}
|
||||
return hcsshim.DeactivateLayer(d.info, rID)
|
||||
}
|
||||
|
||||
// Cleanup ensures the information the driver stores is properly removed.
|
||||
func (d *Driver) Cleanup() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Diff produces an archive of the changes between the specified
|
||||
// layer and its parent layer which may be "".
|
||||
// The layer should be mounted when calling this function
|
||||
func (d *Driver) Diff(id, parent string) (_ archive.Archive, err error) {
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
layerChain, err := d.getLayerChain(rID)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// this is assuming that the layer is unmounted
|
||||
if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prepare := func() {
|
||||
if err := hcsshim.PrepareLayer(d.info, rID, layerChain); err != nil {
|
||||
logrus.Warnf("Failed to Deactivate %s: %s", rID, err)
|
||||
}
|
||||
}
|
||||
|
||||
arch, err := d.exportLayer(rID, layerChain)
|
||||
if err != nil {
|
||||
prepare()
|
||||
return
|
||||
}
|
||||
return ioutils.NewReadCloserWrapper(arch, func() error {
|
||||
err := arch.Close()
|
||||
prepare()
|
||||
return err
|
||||
}), nil
|
||||
}
|
||||
|
||||
// Changes produces a list of changes between the specified layer
|
||||
// and its parent layer. If parent is "", then all changes will be ADD changes.
|
||||
// The layer should be mounted when calling this function
|
||||
func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
|
||||
rID, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentChain, err := d.getLayerChain(rID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// this is assuming that the layer is unmounted
|
||||
if err := hcsshim.UnprepareLayer(d.info, rID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if err := hcsshim.PrepareLayer(d.info, rID, parentChain); err != nil {
|
||||
logrus.Warnf("Failed to Deactivate %s: %s", rID, err)
|
||||
}
|
||||
}()
|
||||
|
||||
var changes []archive.Change
|
||||
err = winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
|
||||
r, err := hcsshim.NewLayerReader(d.info, id, parentChain)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer r.Close()
|
||||
|
||||
for {
|
||||
name, _, fileInfo, err := r.Next()
|
||||
if err == io.EOF {
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
name = filepath.ToSlash(name)
|
||||
if fileInfo == nil {
|
||||
changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeDelete})
|
||||
} else {
|
||||
// Currently there is no way to tell between an add and a modify.
|
||||
changes = append(changes, archive.Change{Path: name, Kind: archive.ChangeModify})
|
||||
}
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
// ApplyDiff extracts the changeset from the given diff into the
|
||||
// layer with the specified id and parent, returning the size of the
|
||||
// new layer in bytes.
|
||||
// The layer should not be mounted when calling this function
|
||||
func (d *Driver) ApplyDiff(id, parent string, diff archive.Reader) (int64, error) {
|
||||
var layerChain []string
|
||||
if parent != "" {
|
||||
rPId, err := d.resolveID(parent)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
parentChain, err := d.getLayerChain(rPId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
parentPath, err := hcsshim.GetLayerMountPath(d.info, rPId)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
layerChain = append(layerChain, parentPath)
|
||||
layerChain = append(layerChain, parentChain...)
|
||||
}
|
||||
|
||||
size, err := d.importLayer(id, diff, layerChain)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if err = d.setLayerChain(id, layerChain); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// DiffSize calculates the changes between the specified layer
|
||||
// and its parent and returns the size in bytes of the changes
|
||||
// relative to its base filesystem directory.
|
||||
func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
|
||||
rPId, err := d.resolveID(parent)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
changes, err := d.Changes(id, rPId)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
layerFs, err := d.Get(id, "")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer d.Put(id)
|
||||
|
||||
return archive.ChangesSize(layerFs, changes), nil
|
||||
}
|
||||
|
||||
// GetMetadata returns custom driver information.
|
||||
func (d *Driver) GetMetadata(id string) (map[string]string, error) {
|
||||
m := make(map[string]string)
|
||||
m["dir"] = d.dir(id)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func writeTarFromLayer(r hcsshim.LayerReader, w io.Writer) error {
|
||||
t := tar.NewWriter(w)
|
||||
for {
|
||||
name, size, fileInfo, err := r.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if fileInfo == nil {
|
||||
// Write a whiteout file.
|
||||
hdr := &tar.Header{
|
||||
Name: filepath.ToSlash(filepath.Join(filepath.Dir(name), archive.WhiteoutPrefix+filepath.Base(name))),
|
||||
}
|
||||
err := t.WriteHeader(hdr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
err = backuptar.WriteTarFileFromBackupStream(t, r, name, size, fileInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return t.Close()
|
||||
}
|
||||
|
||||
// exportLayer generates an archive from a layer based on the given ID.
|
||||
func (d *Driver) exportLayer(id string, parentLayerPaths []string) (archive.Archive, error) {
|
||||
archive, w := io.Pipe()
|
||||
go func() {
|
||||
err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
|
||||
r, err := hcsshim.NewLayerReader(d.info, id, parentLayerPaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = writeTarFromLayer(r, w)
|
||||
cerr := r.Close()
|
||||
if err == nil {
|
||||
err = cerr
|
||||
}
|
||||
return err
|
||||
})
|
||||
w.CloseWithError(err)
|
||||
}()
|
||||
|
||||
return archive, nil
|
||||
}
|
||||
|
||||
func writeLayerFromTar(r archive.Reader, w hcsshim.LayerWriter) (int64, error) {
|
||||
t := tar.NewReader(r)
|
||||
hdr, err := t.Next()
|
||||
totalSize := int64(0)
|
||||
buf := bufio.NewWriter(nil)
|
||||
for err == nil {
|
||||
base := path.Base(hdr.Name)
|
||||
if strings.HasPrefix(base, archive.WhiteoutPrefix) {
|
||||
name := path.Join(path.Dir(hdr.Name), base[len(archive.WhiteoutPrefix):])
|
||||
err = w.Remove(filepath.FromSlash(name))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
hdr, err = t.Next()
|
||||
} else if hdr.Typeflag == tar.TypeLink {
|
||||
err = w.AddLink(filepath.FromSlash(hdr.Name), filepath.FromSlash(hdr.Linkname))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
hdr, err = t.Next()
|
||||
} else {
|
||||
var (
|
||||
name string
|
||||
size int64
|
||||
fileInfo *winio.FileBasicInfo
|
||||
)
|
||||
name, size, fileInfo, err = backuptar.FileInfoFromHeader(hdr)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
err = w.Add(filepath.FromSlash(name), fileInfo)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf.Reset(w)
|
||||
|
||||
// Add the Hyper-V Virtual Machine group ACE to the security descriptor
|
||||
// for TP5 so that Xenons can access all files. This is not necessary
|
||||
// for post-TP5 builds.
|
||||
if isTP5OrOlder() {
|
||||
if sddl, ok := hdr.Winheaders["sd"]; ok {
|
||||
var ace string
|
||||
if hdr.Typeflag == tar.TypeDir {
|
||||
ace = "(A;OICI;0x1200a9;;;S-1-5-83-0)"
|
||||
} else {
|
||||
ace = "(A;;0x1200a9;;;S-1-5-83-0)"
|
||||
}
|
||||
if hdr.Winheaders["sd"], ok = addAceToSddlDacl(sddl, ace); !ok {
|
||||
logrus.Debugf("failed to add VM ACE to %s", sddl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hdr, err = backuptar.WriteBackupStreamFromTarFile(buf, t, hdr)
|
||||
ferr := buf.Flush()
|
||||
if ferr != nil {
|
||||
err = ferr
|
||||
}
|
||||
totalSize += size
|
||||
}
|
||||
}
|
||||
if err != io.EOF {
|
||||
return 0, err
|
||||
}
|
||||
return totalSize, nil
|
||||
}
|
||||
|
||||
func addAceToSddlDacl(sddl, ace string) (string, bool) {
|
||||
daclStart := strings.Index(sddl, "D:")
|
||||
if daclStart < 0 {
|
||||
return sddl, false
|
||||
}
|
||||
|
||||
dacl := sddl[daclStart:]
|
||||
daclEnd := strings.Index(dacl, "S:")
|
||||
if daclEnd < 0 {
|
||||
daclEnd = len(dacl)
|
||||
}
|
||||
dacl = dacl[:daclEnd]
|
||||
|
||||
if strings.Contains(dacl, ace) {
|
||||
return sddl, true
|
||||
}
|
||||
|
||||
i := 2
|
||||
for i+1 < len(dacl) {
|
||||
if dacl[i] != '(' {
|
||||
return sddl, false
|
||||
}
|
||||
|
||||
if dacl[i+1] == 'A' {
|
||||
break
|
||||
}
|
||||
|
||||
i += 2
|
||||
for p := 1; i < len(dacl) && p > 0; i++ {
|
||||
if dacl[i] == '(' {
|
||||
p++
|
||||
} else if dacl[i] == ')' {
|
||||
p--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return sddl[:daclStart+i] + ace + sddl[daclStart+i:], true
|
||||
}
|
||||
|
||||
// importLayer adds a new layer to the tag and graph store based on the given data.
|
||||
func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) {
|
||||
cmd := reexec.Command(append([]string{"docker-windows-write-layer", d.info.HomeDir, id}, parentLayerPaths...)...)
|
||||
output := bytes.NewBuffer(nil)
|
||||
cmd.Stdin = layerData
|
||||
cmd.Stdout = output
|
||||
cmd.Stderr = output
|
||||
|
||||
if err = cmd.Start(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = cmd.Wait(); err != nil {
|
||||
return 0, fmt.Errorf("re-exec error: %v: output: %s", err, output)
|
||||
}
|
||||
|
||||
return strconv.ParseInt(output.String(), 10, 64)
|
||||
}
|
||||
|
||||
// writeLayer is the re-exec entry point for writing a layer from a tar file
|
||||
func writeLayer() {
|
||||
home := os.Args[1]
|
||||
id := os.Args[2]
|
||||
parentLayerPaths := os.Args[3:]
|
||||
|
||||
err := func() error {
|
||||
err := winio.EnableProcessPrivileges([]string{winio.SeBackupPrivilege, winio.SeRestorePrivilege})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info := hcsshim.DriverInfo{
|
||||
Flavour: filterDriver,
|
||||
HomeDir: home,
|
||||
}
|
||||
|
||||
w, err := hcsshim.NewLayerWriter(info, id, parentLayerPaths)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
size, err := writeLayerFromTar(os.Stdin, w)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = w.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprint(os.Stdout, size)
|
||||
return nil
|
||||
}()
|
||||
|
||||
if err != nil {
|
||||
fmt.Fprint(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// resolveID computes the layerID information based on the given id.
|
||||
func (d *Driver) resolveID(id string) (string, error) {
|
||||
content, err := ioutil.ReadFile(filepath.Join(d.dir(id), "layerID"))
|
||||
if os.IsNotExist(err) {
|
||||
return id, nil
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), nil
|
||||
}
|
||||
|
||||
// setID stores the layerId in disk.
|
||||
func (d *Driver) setID(id, altID string) error {
|
||||
err := ioutil.WriteFile(filepath.Join(d.dir(id), "layerId"), []byte(altID), 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getLayerChain returns the layer chain information.
|
||||
func (d *Driver) getLayerChain(id string) ([]string, error) {
|
||||
jPath := filepath.Join(d.dir(id), "layerchain.json")
|
||||
content, err := ioutil.ReadFile(jPath)
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("Unable to read layerchain file - %s", err)
|
||||
}
|
||||
|
||||
var layerChain []string
|
||||
err = json.Unmarshal(content, &layerChain)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to unmarshall layerchain json - %s", err)
|
||||
}
|
||||
|
||||
return layerChain, nil
|
||||
}
|
||||
|
||||
// setLayerChain stores the layer chain information in disk.
|
||||
func (d *Driver) setLayerChain(id string, chain []string) error {
|
||||
content, err := json.Marshal(&chain)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to marshall layerchain json - %s", err)
|
||||
}
|
||||
|
||||
jPath := filepath.Join(d.dir(id), "layerchain.json")
|
||||
err = ioutil.WriteFile(jPath, content, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Unable to write layerchain file - %s", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type fileGetCloserWithBackupPrivileges struct {
|
||||
path string
|
||||
}
|
||||
|
||||
func (fg *fileGetCloserWithBackupPrivileges) Get(filename string) (io.ReadCloser, error) {
|
||||
var f *os.File
|
||||
// Open the file while holding the Windows backup privilege. This ensures that the
|
||||
// file can be opened even if the caller does not actually have access to it according
|
||||
// to the security descriptor.
|
||||
err := winio.RunWithPrivilege(winio.SeBackupPrivilege, func() error {
|
||||
path := longpath.AddPrefix(filepath.Join(fg.path, filename))
|
||||
p, err := syscall.UTF16FromString(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h, err := syscall.CreateFile(&p[0], syscall.GENERIC_READ, syscall.FILE_SHARE_READ, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_BACKUP_SEMANTICS, 0)
|
||||
if err != nil {
|
||||
return &os.PathError{Op: "open", Path: path, Err: err}
|
||||
}
|
||||
f = os.NewFile(uintptr(h), path)
|
||||
return nil
|
||||
})
|
||||
return f, err
|
||||
}
|
||||
|
||||
func (fg *fileGetCloserWithBackupPrivileges) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type fileGetDestroyCloser struct {
|
||||
storage.FileGetter
|
||||
path string
|
||||
}
|
||||
|
||||
func (f *fileGetDestroyCloser) Close() error {
|
||||
// TODO: activate layers and release here?
|
||||
return os.RemoveAll(f.path)
|
||||
}
|
||||
|
||||
// DiffGetter returns a FileGetCloser that can read files from the directory that
|
||||
// contains files for the layer differences. Used for direct access for tar-split.
|
||||
func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
|
||||
id, err := d.resolveID(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &fileGetCloserWithBackupPrivileges{d.dir(id)}, nil
|
||||
}
|
2
vendor/github.com/containers/storage/drivers/zfs/MAINTAINERS
generated
vendored
2
vendor/github.com/containers/storage/drivers/zfs/MAINTAINERS
generated
vendored
@@ -1,2 +0,0 @@
|
||||
Jörg Thalheim <joerg@higgsboson.tk> (@Mic92)
|
||||
Arthur Gautier <baloo@gandi.net> (@baloose)
|
26
vendor/github.com/containers/storage/pkg/longpath/longpath.go
generated
vendored
Normal file
26
vendor/github.com/containers/storage/pkg/longpath/longpath.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
// longpath introduces some constants and helper functions for handling long paths
|
||||
// in Windows, which are expected to be prepended with `\\?\` and followed by either
|
||||
// a drive letter, a UNC server\share, or a volume identifier.
|
||||
|
||||
package longpath
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Prefix is the longpath prefix for Windows file paths.
|
||||
const Prefix = `\\?\`
|
||||
|
||||
// AddPrefix will add the Windows long path prefix to the path provided if
|
||||
// it does not already have it.
|
||||
func AddPrefix(path string) string {
|
||||
if !strings.HasPrefix(path, Prefix) {
|
||||
if strings.HasPrefix(path, `\\`) {
|
||||
// This is a UNC path, so we need to add 'UNC' to the path as well.
|
||||
path = Prefix + `UNC` + path[1:]
|
||||
} else {
|
||||
path = Prefix + path
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
27
vendor/github.com/containers/storage/pkg/mflag/LICENSE
generated
vendored
27
vendor/github.com/containers/storage/pkg/mflag/LICENSE
generated
vendored
@@ -1,27 +0,0 @@
|
||||
Copyright (c) 2014-2016 The Docker & Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
188
vendor/github.com/containers/storage/pkg/plugins/client.go
generated
vendored
Normal file
188
vendor/github.com/containers/storage/pkg/plugins/client.go
generated
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/containers/storage/pkg/plugins/transport"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultTimeOut = 30
|
||||
)
|
||||
|
||||
// NewClient creates a new plugin client (http).
|
||||
func NewClient(addr string, tlsConfig *tlsconfig.Options) (*Client, error) {
|
||||
tr := &http.Transport{}
|
||||
|
||||
if tlsConfig != nil {
|
||||
c, err := tlsconfig.Client(*tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tr.TLSClientConfig = c
|
||||
}
|
||||
|
||||
u, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
socket := u.Host
|
||||
if socket == "" {
|
||||
// valid local socket addresses have the host empty.
|
||||
socket = u.Path
|
||||
}
|
||||
if err := sockets.ConfigureTransport(tr, u.Scheme, socket); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
scheme := httpScheme(u)
|
||||
|
||||
clientTransport := transport.NewHTTPTransport(tr, scheme, socket)
|
||||
return NewClientWithTransport(clientTransport), nil
|
||||
}
|
||||
|
||||
// NewClientWithTransport creates a new plugin client with a given transport.
|
||||
func NewClientWithTransport(tr transport.Transport) *Client {
|
||||
return &Client{
|
||||
http: &http.Client{
|
||||
Transport: tr,
|
||||
},
|
||||
requestFactory: tr,
|
||||
}
|
||||
}
|
||||
|
||||
// Client represents a plugin client.
|
||||
type Client struct {
|
||||
http *http.Client // http client to use
|
||||
requestFactory transport.RequestFactory
|
||||
}
|
||||
|
||||
// Call calls the specified method with the specified arguments for the plugin.
|
||||
// It will retry for 30 seconds if a failure occurs when calling.
|
||||
func (c *Client) Call(serviceMethod string, args interface{}, ret interface{}) error {
|
||||
var buf bytes.Buffer
|
||||
if args != nil {
|
||||
if err := json.NewEncoder(&buf).Encode(args); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
body, err := c.callWithRetry(serviceMethod, &buf, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer body.Close()
|
||||
if ret != nil {
|
||||
if err := json.NewDecoder(body).Decode(&ret); err != nil {
|
||||
logrus.Errorf("%s: error reading plugin resp: %v", serviceMethod, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stream calls the specified method with the specified arguments for the plugin and returns the response body
|
||||
func (c *Client) Stream(serviceMethod string, args interface{}) (io.ReadCloser, error) {
|
||||
var buf bytes.Buffer
|
||||
if err := json.NewEncoder(&buf).Encode(args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.callWithRetry(serviceMethod, &buf, true)
|
||||
}
|
||||
|
||||
// SendFile calls the specified method, and passes through the IO stream
|
||||
func (c *Client) SendFile(serviceMethod string, data io.Reader, ret interface{}) error {
|
||||
body, err := c.callWithRetry(serviceMethod, data, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer body.Close()
|
||||
if err := json.NewDecoder(body).Decode(&ret); err != nil {
|
||||
logrus.Errorf("%s: error reading plugin resp: %v", serviceMethod, err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) callWithRetry(serviceMethod string, data io.Reader, retry bool) (io.ReadCloser, error) {
|
||||
req, err := c.requestFactory.NewRequest(serviceMethod, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var retries int
|
||||
start := time.Now()
|
||||
|
||||
for {
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
if !retry {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeOff := backoff(retries)
|
||||
if abort(start, timeOff) {
|
||||
return nil, err
|
||||
}
|
||||
retries++
|
||||
logrus.Warnf("Unable to connect to plugin: %s%s: %v, retrying in %v", req.URL.Host, req.URL.Path, err, timeOff)
|
||||
time.Sleep(timeOff)
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, &statusError{resp.StatusCode, serviceMethod, err.Error()}
|
||||
}
|
||||
|
||||
// Plugins' Response(s) should have an Err field indicating what went
|
||||
// wrong. Try to unmarshal into ResponseErr. Otherwise fallback to just
|
||||
// return the string(body)
|
||||
type responseErr struct {
|
||||
Err string
|
||||
}
|
||||
remoteErr := responseErr{}
|
||||
if err := json.Unmarshal(b, &remoteErr); err == nil {
|
||||
if remoteErr.Err != "" {
|
||||
return nil, &statusError{resp.StatusCode, serviceMethod, remoteErr.Err}
|
||||
}
|
||||
}
|
||||
// old way...
|
||||
return nil, &statusError{resp.StatusCode, serviceMethod, string(b)}
|
||||
}
|
||||
return resp.Body, nil
|
||||
}
|
||||
}
|
||||
|
||||
func backoff(retries int) time.Duration {
|
||||
b, max := 1, defaultTimeOut
|
||||
for b < max && retries > 0 {
|
||||
b *= 2
|
||||
retries--
|
||||
}
|
||||
if b > max {
|
||||
b = max
|
||||
}
|
||||
return time.Duration(b) * time.Second
|
||||
}
|
||||
|
||||
func abort(start time.Time, timeOff time.Duration) bool {
|
||||
return timeOff+time.Since(start) >= time.Duration(defaultTimeOut)*time.Second
|
||||
}
|
||||
|
||||
func httpScheme(u *url.URL) string {
|
||||
scheme := u.Scheme
|
||||
if scheme != "https" {
|
||||
scheme = "http"
|
||||
}
|
||||
return scheme
|
||||
}
|
132
vendor/github.com/containers/storage/pkg/plugins/discovery.go
generated
vendored
Normal file
132
vendor/github.com/containers/storage/pkg/plugins/discovery.go
generated
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotFound plugin not found
|
||||
ErrNotFound = errors.New("plugin not found")
|
||||
socketsPath = "/run/oci-storage/plugins"
|
||||
specsPaths = []string{"/etc/oci-storage/plugins", "/usr/lib/oci-storage/plugins"}
|
||||
)
|
||||
|
||||
// localRegistry defines a registry that is local (using unix socket).
|
||||
type localRegistry struct{}
|
||||
|
||||
func newLocalRegistry() localRegistry {
|
||||
return localRegistry{}
|
||||
}
|
||||
|
||||
// Scan scans all the plugin paths and returns all the names it found
|
||||
func Scan() ([]string, error) {
|
||||
var names []string
|
||||
if err := filepath.Walk(socketsPath, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if fi.Mode()&os.ModeSocket != 0 {
|
||||
name := strings.TrimSuffix(fi.Name(), filepath.Ext(fi.Name()))
|
||||
names = append(names, name)
|
||||
}
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, path := range specsPaths {
|
||||
if err := filepath.Walk(path, func(p string, fi os.FileInfo, err error) error {
|
||||
if err != nil || fi.IsDir() {
|
||||
return nil
|
||||
}
|
||||
name := strings.TrimSuffix(fi.Name(), filepath.Ext(fi.Name()))
|
||||
names = append(names, name)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return names, nil
|
||||
}
|
||||
|
||||
// Plugin returns the plugin registered with the given name (or returns an error).
|
||||
func (l *localRegistry) Plugin(name string) (*Plugin, error) {
|
||||
socketpaths := pluginPaths(socketsPath, name, ".sock")
|
||||
|
||||
for _, p := range socketpaths {
|
||||
if fi, err := os.Stat(p); err == nil && fi.Mode()&os.ModeSocket != 0 {
|
||||
return NewLocalPlugin(name, "unix://"+p), nil
|
||||
}
|
||||
}
|
||||
|
||||
var txtspecpaths []string
|
||||
for _, p := range specsPaths {
|
||||
txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".spec")...)
|
||||
txtspecpaths = append(txtspecpaths, pluginPaths(p, name, ".json")...)
|
||||
}
|
||||
|
||||
for _, p := range txtspecpaths {
|
||||
if _, err := os.Stat(p); err == nil {
|
||||
if strings.HasSuffix(p, ".json") {
|
||||
return readPluginJSONInfo(name, p)
|
||||
}
|
||||
return readPluginInfo(name, p)
|
||||
}
|
||||
}
|
||||
return nil, ErrNotFound
|
||||
}
|
||||
|
||||
func readPluginInfo(name, path string) (*Plugin, error) {
|
||||
content, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr := strings.TrimSpace(string(content))
|
||||
|
||||
u, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(u.Scheme) == 0 {
|
||||
return nil, fmt.Errorf("Unknown protocol")
|
||||
}
|
||||
|
||||
return NewLocalPlugin(name, addr), nil
|
||||
}
|
||||
|
||||
func readPluginJSONInfo(name, path string) (*Plugin, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var p Plugin
|
||||
if err := json.NewDecoder(f).Decode(&p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p.name = name
|
||||
if len(p.TLSConfig.CAFile) == 0 {
|
||||
p.TLSConfig.InsecureSkipVerify = true
|
||||
}
|
||||
p.activateWait = sync.NewCond(&sync.Mutex{})
|
||||
|
||||
return &p, nil
|
||||
}
|
||||
|
||||
func pluginPaths(base, name, ext string) []string {
|
||||
return []string{
|
||||
filepath.Join(base, name+ext),
|
||||
filepath.Join(base, name, name+ext),
|
||||
}
|
||||
}
|
33
vendor/github.com/containers/storage/pkg/plugins/errors.go
generated
vendored
Normal file
33
vendor/github.com/containers/storage/pkg/plugins/errors.go
generated
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type statusError struct {
|
||||
status int
|
||||
method string
|
||||
err string
|
||||
}
|
||||
|
||||
// Error returns a formatted string for this error type
|
||||
func (e *statusError) Error() string {
|
||||
return fmt.Sprintf("%s: %v", e.method, e.err)
|
||||
}
|
||||
|
||||
// IsNotFound indicates if the passed in error is from an http.StatusNotFound from the plugin
|
||||
func IsNotFound(err error) bool {
|
||||
return isStatusError(err, http.StatusNotFound)
|
||||
}
|
||||
|
||||
func isStatusError(err error, status int) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
e, ok := err.(*statusError)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return e.status == status
|
||||
}
|
270
vendor/github.com/containers/storage/pkg/plugins/plugins.go
generated
vendored
Normal file
270
vendor/github.com/containers/storage/pkg/plugins/plugins.go
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
// Package plugins provides structures and helper functions to manage Docker
|
||||
// plugins.
|
||||
//
|
||||
// Storage discovers plugins by looking for them in the plugin directory whenever
|
||||
// a user or container tries to use one by name. UNIX domain socket files must
|
||||
// be located under /run/oci-storage/plugins, whereas spec files can be located
|
||||
// either under /etc/oci-storage/plugins or /usr/lib/oci-storage/plugins. This
|
||||
// is handled by the Registry interface, which lets you list all plugins or get
|
||||
// a plugin by its name if it exists.
|
||||
//
|
||||
// The plugins need to implement an HTTP server and bind this to the UNIX socket
|
||||
// or the address specified in the spec files.
|
||||
// A handshake is send at /Plugin.Activate, and plugins are expected to return
|
||||
// a Manifest with a list of subsystems which this plugin implements. As of
|
||||
// this writing, the known subsystem is "GraphDriver".
|
||||
//
|
||||
// In order to use a plugins, you can use the ``Get`` with the name of the
|
||||
// plugin and the subsystem it implements.
|
||||
//
|
||||
// plugin, err := plugins.Get("example", "VolumeDriver")
|
||||
// if err != nil {
|
||||
// return fmt.Errorf("Error looking up volume plugin example: %v", err)
|
||||
// }
|
||||
package plugins
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/docker/go-connections/tlsconfig"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNotImplements is returned if the plugin does not implement the requested driver.
|
||||
ErrNotImplements = errors.New("Plugin does not implement the requested driver")
|
||||
)
|
||||
|
||||
type plugins struct {
|
||||
sync.Mutex
|
||||
plugins map[string]*Plugin
|
||||
}
|
||||
|
||||
var (
|
||||
storage = plugins{plugins: make(map[string]*Plugin)}
|
||||
extpointHandlers = make(map[string]func(string, *Client))
|
||||
)
|
||||
|
||||
// Manifest lists what a plugin implements.
|
||||
type Manifest struct {
|
||||
// List of subsystem the plugin implements.
|
||||
Implements []string
|
||||
}
|
||||
|
||||
// Plugin is the definition of a storage plugin.
|
||||
type Plugin struct {
|
||||
// Name of the plugin
|
||||
name string
|
||||
// Address of the plugin
|
||||
Addr string
|
||||
// TLS configuration of the plugin
|
||||
TLSConfig *tlsconfig.Options
|
||||
// Client attached to the plugin
|
||||
client *Client
|
||||
// Manifest of the plugin (see above)
|
||||
Manifest *Manifest `json:"-"`
|
||||
|
||||
// error produced by activation
|
||||
activateErr error
|
||||
// specifies if the activation sequence is completed (not if it is successful or not)
|
||||
activated bool
|
||||
// wait for activation to finish
|
||||
activateWait *sync.Cond
|
||||
}
|
||||
|
||||
// Name returns the name of the plugin.
|
||||
func (p *Plugin) Name() string {
|
||||
return p.name
|
||||
}
|
||||
|
||||
// Client returns a ready-to-use plugin client that can be used to communicate with the plugin.
|
||||
func (p *Plugin) Client() *Client {
|
||||
return p.client
|
||||
}
|
||||
|
||||
// NewLocalPlugin creates a new local plugin.
|
||||
func NewLocalPlugin(name, addr string) *Plugin {
|
||||
return &Plugin{
|
||||
name: name,
|
||||
Addr: addr,
|
||||
// TODO: change to nil
|
||||
TLSConfig: &tlsconfig.Options{InsecureSkipVerify: true},
|
||||
activateWait: sync.NewCond(&sync.Mutex{}),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Plugin) activate() error {
|
||||
p.activateWait.L.Lock()
|
||||
if p.activated {
|
||||
p.activateWait.L.Unlock()
|
||||
return p.activateErr
|
||||
}
|
||||
|
||||
p.activateErr = p.activateWithLock()
|
||||
p.activated = true
|
||||
|
||||
p.activateWait.L.Unlock()
|
||||
p.activateWait.Broadcast()
|
||||
return p.activateErr
|
||||
}
|
||||
|
||||
func (p *Plugin) activateWithLock() error {
|
||||
c, err := NewClient(p.Addr, p.TLSConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
p.client = c
|
||||
|
||||
m := new(Manifest)
|
||||
if err = p.client.Call("Plugin.Activate", nil, m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Manifest = m
|
||||
|
||||
for _, iface := range m.Implements {
|
||||
handler, handled := extpointHandlers[iface]
|
||||
if !handled {
|
||||
continue
|
||||
}
|
||||
handler(p.name, p.client)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Plugin) waitActive() error {
|
||||
p.activateWait.L.Lock()
|
||||
for !p.activated {
|
||||
p.activateWait.Wait()
|
||||
}
|
||||
p.activateWait.L.Unlock()
|
||||
return p.activateErr
|
||||
}
|
||||
|
||||
func (p *Plugin) implements(kind string) bool {
|
||||
if err := p.waitActive(); err != nil {
|
||||
return false
|
||||
}
|
||||
for _, driver := range p.Manifest.Implements {
|
||||
if driver == kind {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func load(name string) (*Plugin, error) {
|
||||
return loadWithRetry(name, true)
|
||||
}
|
||||
|
||||
func loadWithRetry(name string, retry bool) (*Plugin, error) {
|
||||
registry := newLocalRegistry()
|
||||
start := time.Now()
|
||||
|
||||
var retries int
|
||||
for {
|
||||
pl, err := registry.Plugin(name)
|
||||
if err != nil {
|
||||
if !retry {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
timeOff := backoff(retries)
|
||||
if abort(start, timeOff) {
|
||||
return nil, err
|
||||
}
|
||||
retries++
|
||||
logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff)
|
||||
time.Sleep(timeOff)
|
||||
continue
|
||||
}
|
||||
|
||||
storage.Lock()
|
||||
storage.plugins[name] = pl
|
||||
storage.Unlock()
|
||||
|
||||
err = pl.activate()
|
||||
|
||||
if err != nil {
|
||||
storage.Lock()
|
||||
delete(storage.plugins, name)
|
||||
storage.Unlock()
|
||||
}
|
||||
|
||||
return pl, err
|
||||
}
|
||||
}
|
||||
|
||||
func get(name string) (*Plugin, error) {
|
||||
storage.Lock()
|
||||
pl, ok := storage.plugins[name]
|
||||
storage.Unlock()
|
||||
if ok {
|
||||
return pl, pl.activate()
|
||||
}
|
||||
return load(name)
|
||||
}
|
||||
|
||||
// Get returns the plugin given the specified name and requested implementation.
|
||||
func Get(name, imp string) (*Plugin, error) {
|
||||
pl, err := get(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if pl.implements(imp) {
|
||||
logrus.Debugf("%s implements: %s", name, imp)
|
||||
return pl, nil
|
||||
}
|
||||
return nil, ErrNotImplements
|
||||
}
|
||||
|
||||
// Handle adds the specified function to the extpointHandlers.
|
||||
func Handle(iface string, fn func(string, *Client)) {
|
||||
extpointHandlers[iface] = fn
|
||||
}
|
||||
|
||||
// GetAll returns all the plugins for the specified implementation
|
||||
func GetAll(imp string) ([]*Plugin, error) {
|
||||
pluginNames, err := Scan()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
type plLoad struct {
|
||||
pl *Plugin
|
||||
err error
|
||||
}
|
||||
|
||||
chPl := make(chan *plLoad, len(pluginNames))
|
||||
var wg sync.WaitGroup
|
||||
for _, name := range pluginNames {
|
||||
if pl, ok := storage.plugins[name]; ok {
|
||||
chPl <- &plLoad{pl, nil}
|
||||
continue
|
||||
}
|
||||
|
||||
wg.Add(1)
|
||||
go func(name string) {
|
||||
defer wg.Done()
|
||||
pl, err := loadWithRetry(name, false)
|
||||
chPl <- &plLoad{pl, err}
|
||||
}(name)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(chPl)
|
||||
|
||||
var out []*Plugin
|
||||
for pl := range chPl {
|
||||
if pl.err != nil {
|
||||
logrus.Error(pl.err)
|
||||
continue
|
||||
}
|
||||
if pl.pl.implements(imp) {
|
||||
out = append(out, pl.pl)
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
36
vendor/github.com/containers/storage/pkg/plugins/transport/http.go
generated
vendored
Normal file
36
vendor/github.com/containers/storage/pkg/plugins/transport/http.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// httpTransport holds an http.RoundTripper
|
||||
// and information about the scheme and address the transport
|
||||
// sends request to.
|
||||
type httpTransport struct {
|
||||
http.RoundTripper
|
||||
scheme string
|
||||
addr string
|
||||
}
|
||||
|
||||
// NewHTTPTransport creates a new httpTransport.
|
||||
func NewHTTPTransport(r http.RoundTripper, scheme, addr string) Transport {
|
||||
return httpTransport{
|
||||
RoundTripper: r,
|
||||
scheme: scheme,
|
||||
addr: addr,
|
||||
}
|
||||
}
|
||||
|
||||
// NewRequest creates a new http.Request and sets the URL
|
||||
// scheme and address with the transport's fields.
|
||||
func (t httpTransport) NewRequest(path string, data io.Reader) (*http.Request, error) {
|
||||
req, err := newHTTPRequest(path, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.URL.Scheme = t.scheme
|
||||
req.URL.Host = t.addr
|
||||
return req, nil
|
||||
}
|
36
vendor/github.com/containers/storage/pkg/plugins/transport/transport.go
generated
vendored
Normal file
36
vendor/github.com/containers/storage/pkg/plugins/transport/transport.go
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
package transport
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// VersionMimetype is the Content-Type the engine sends to plugins.
|
||||
const VersionMimetype = "application/vnd.docker.plugins.v1.2+json"
|
||||
|
||||
// RequestFactory defines an interface that
|
||||
// transports can implement to create new requests.
|
||||
type RequestFactory interface {
|
||||
NewRequest(path string, data io.Reader) (*http.Request, error)
|
||||
}
|
||||
|
||||
// Transport defines an interface that plugin transports
|
||||
// must implement.
|
||||
type Transport interface {
|
||||
http.RoundTripper
|
||||
RequestFactory
|
||||
}
|
||||
|
||||
// newHTTPRequest creates a new request with a path and a body.
|
||||
func newHTTPRequest(path string, data io.Reader) (*http.Request, error) {
|
||||
if !strings.HasPrefix(path, "/") {
|
||||
path = "/" + path
|
||||
}
|
||||
req, err := http.NewRequest("POST", path, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Add("Accept", VersionMimetype)
|
||||
return req, nil
|
||||
}
|
Reference in New Issue
Block a user