Merge pull request #924 from Random-Liu/add-timeout-for-recover

Add timeout for recover and event monitor
This commit is contained in:
Lantao Liu 2018-09-27 02:40:57 -07:00 committed by GitHub
commit 4d553cbef8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
143 changed files with 2374 additions and 732 deletions

View File

@ -42,11 +42,17 @@ const (
backOffInitDuration = 1 * time.Second backOffInitDuration = 1 * time.Second
backOffMaxDuration = 5 * time.Minute backOffMaxDuration = 5 * time.Minute
backOffExpireCheckDuration = 1 * time.Second backOffExpireCheckDuration = 1 * time.Second
// handleEventTimeout is the timeout for handling 1 event. Event monitor
// handles events in serial, if one event blocks the event monitor, no
// other events can be handled.
// Add a timeout for each event handling, events that timeout will be requeued and
// handled again in the future.
handleEventTimeout = 10 * time.Second
) )
// eventMonitor monitors containerd event and updates internal state correspondingly. // eventMonitor monitors containerd event and updates internal state correspondingly.
// TODO(random-liu): [P1] Figure out is it possible to drop event during containerd // TODO(random-liu): Handle event for each container in a separate goroutine.
// is running. If it is, we should do periodically list to sync state with containerd.
type eventMonitor struct { type eventMonitor struct {
containerStore *containerstore.Store containerStore *containerstore.Store
sandboxStore *sandboxstore.Store sandboxStore *sandboxstore.Store
@ -189,6 +195,9 @@ func (em *eventMonitor) stop() {
// handleEvent handles a containerd event. // handleEvent handles a containerd event.
func (em *eventMonitor) handleEvent(any interface{}) error { func (em *eventMonitor) handleEvent(any interface{}) error {
ctx := ctrdutil.NamespacedContext() ctx := ctrdutil.NamespacedContext()
ctx, cancel := context.WithTimeout(ctx, handleEventTimeout)
defer cancel()
switch any.(type) { switch any.(type) {
// If containerd-shim exits unexpectedly, there will be no corresponding event. // If containerd-shim exits unexpectedly, there will be no corresponding event.
// However, containerd could not retrieve container state in that case, so it's // However, containerd could not retrieve container state in that case, so it's

View File

@ -136,8 +136,23 @@ func (c *criService) recover(ctx context.Context) error {
return nil return nil
} }
// loadContainerTimeout is the default timeout for loading a container/sandbox.
// One container/sandbox hangs (e.g. containerd#2438) should not affect other
// containers/sandboxes.
// Most CRI container/sandbox related operations are per container, the ones
// which handle multiple containers at a time are:
// * ListPodSandboxes: Don't talk with containerd services.
// * ListContainers: Don't talk with containerd services.
// * ListContainerStats: Not in critical code path, a default timeout will
// be applied at CRI level.
// * Recovery logic: We should set a time for each container/sandbox recovery.
// * Event montior: We should set a timeout for each container/sandbox event handling.
const loadContainerTimeout = 10 * time.Second
// loadContainer loads container from containerd and status checkpoint. // loadContainer loads container from containerd and status checkpoint.
func (c *criService) loadContainer(ctx context.Context, cntr containerd.Container) (containerstore.Container, error) { func (c *criService) loadContainer(ctx context.Context, cntr containerd.Container) (containerstore.Container, error) {
ctx, cancel := context.WithTimeout(ctx, loadContainerTimeout)
defer cancel()
id := cntr.ID() id := cntr.ID()
containerDir := c.getContainerRootDir(id) containerDir := c.getContainerRootDir(id)
volatileContainerDir := c.getVolatileContainerRootDir(id) volatileContainerDir := c.getVolatileContainerRootDir(id)
@ -290,9 +305,9 @@ const (
// unknownContainerStatus returns the default container status when its status is unknown. // unknownContainerStatus returns the default container status when its status is unknown.
func unknownContainerStatus() containerstore.Status { func unknownContainerStatus() containerstore.Status {
return containerstore.Status{ return containerstore.Status{
CreatedAt: time.Now().UnixNano(), CreatedAt: 0,
StartedAt: time.Now().UnixNano(), StartedAt: 0,
FinishedAt: time.Now().UnixNano(), FinishedAt: 0,
ExitCode: unknownExitCode, ExitCode: unknownExitCode,
Reason: unknownExitReason, Reason: unknownExitReason,
} }
@ -300,6 +315,8 @@ func unknownContainerStatus() containerstore.Status {
// loadSandbox loads sandbox from containerd. // loadSandbox loads sandbox from containerd.
func loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.Sandbox, error) { func loadSandbox(ctx context.Context, cntr containerd.Container) (sandboxstore.Sandbox, error) {
ctx, cancel := context.WithTimeout(ctx, loadContainerTimeout)
defer cancel()
var sandbox sandboxstore.Sandbox var sandbox sandboxstore.Sandbox
// Load sandbox metadata. // Load sandbox metadata.
exts, err := cntr.Extensions(ctx) exts, err := cntr.Extensions(ctx)

View File

@ -27,7 +27,7 @@ func NewFakeStore(images []Image) (*Store, error) {
s.refCache[ref] = i.ID s.refCache[ref] = i.ID
} }
if err := s.store.add(i); err != nil { if err := s.store.add(i); err != nil {
return nil, errors.Wrapf(err, "add image %q", i) return nil, errors.Wrapf(err, "add image %+v", i)
} }
} }
return s, nil return s, nil

View File

@ -1,15 +1,14 @@
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9 github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
github.com/blang/semver v3.1.0 github.com/blang/semver v3.1.0
github.com/boltdb/bolt v1.3.1
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23 github.com/containerd/console c12b1e7919c14469339a5d38f2f8ed9b64a9de23
github.com/containerd/containerd 1950f791d9225ffe061c77e74e292bcb3c428a04 github.com/containerd/containerd f88d3e5d6dfe9b7d7941ac5241649ad8240b9282
github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537 github.com/containerd/continuity 7f53d412b9eb1cbf744c2063185d703a0ee34700
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/go-cni 6d7b509a054a3cb1c35ed1865d4fde2f0cb547cd github.com/containerd/go-cni 6d7b509a054a3cb1c35ed1865d4fde2f0cb547cd
github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3 github.com/containerd/go-runc 5a6d9f37cfa36b15efba46dc7ea349fa9b7143c3
github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containernetworking/cni v0.6.0 github.com/containernetworking/cni v0.6.0
github.com/containernetworking/plugins v0.7.0 github.com/containernetworking/plugins v0.7.0
@ -35,12 +34,12 @@ github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f
github.com/json-iterator/go 1.1.5 github.com/json-iterator/go 1.1.5
github.com/matttproud/golang_protobuf_extensions v1.0.0 github.com/matttproud/golang_protobuf_extensions v1.0.0
github.com/Microsoft/go-winio v0.4.10 github.com/Microsoft/go-winio v0.4.10
github.com/Microsoft/hcsshim 44c060121b68e8bdc40b411beba551f3b4ee9e55 github.com/Microsoft/hcsshim v0.7.4
github.com/modern-go/concurrent 1.0.3 github.com/modern-go/concurrent 1.0.3
github.com/modern-go/reflect2 1.0.1 github.com/modern-go/reflect2 1.0.1
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7 github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
github.com/opencontainers/image-spec v1.0.1 github.com/opencontainers/image-spec v1.0.1
github.com/opencontainers/runc 20aff4f0488c6d4b8df4d85b4f63f1f704c11abd github.com/opencontainers/runc 00dc70017d222b178a002ed30e9321b12647af2d
github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353
github.com/opencontainers/runtime-tools fb101d5d42ab9c040f7d0a004e78336e5d5cb197 github.com/opencontainers/runtime-tools fb101d5d42ab9c040f7d0a004e78336e5d5cb197
github.com/opencontainers/selinux b6fa367ed7f534f9ba25391cc2d467085dbb445a github.com/opencontainers/selinux b6fa367ed7f534f9ba25391cc2d467085dbb445a
@ -59,6 +58,7 @@ github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c
github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6 github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6
github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b
github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874 github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874
go.etcd.io/bbolt v1.3.1-etcd.8
golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067 golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067
golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4 golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4

View File

@ -2,10 +2,16 @@ package guid
import ( import (
"crypto/rand" "crypto/rand"
"encoding/json"
"fmt" "fmt"
"io" "io"
"strconv"
"strings"
) )
var _ = (json.Marshaler)(&GUID{})
var _ = (json.Unmarshaler)(&GUID{})
type GUID [16]byte type GUID [16]byte
func New() GUID { func New() GUID {
@ -20,3 +26,44 @@ func New() GUID {
func (g GUID) String() string { func (g GUID) String() string {
return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x-%02x", g[3], g[2], g[1], g[0], g[5], g[4], g[7], g[6], g[8:10], g[10:]) return fmt.Sprintf("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x-%02x", g[3], g[2], g[1], g[0], g[5], g[4], g[7], g[6], g[8:10], g[10:])
} }
func FromString(s string) GUID {
if len(s) != 36 {
panic(fmt.Sprintf("invalid GUID length: %d", len(s)))
}
if s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
panic("invalid GUID format")
}
indexOrder := [16]int{
0, 2, 4, 6,
9, 11,
14, 16,
19, 21,
24, 26, 28, 30, 32, 34,
}
byteOrder := [16]int{
3, 2, 1, 0,
5, 4,
7, 6,
8, 9,
10, 11, 12, 13, 14, 15,
}
var g GUID
for i, x := range indexOrder {
b, err := strconv.ParseInt(s[x:x+2], 16, 16)
if err != nil {
panic(err)
}
g[byteOrder[i]] = byte(b)
}
return g
}
func (g GUID) MarshalJSON() ([]byte, error) {
return json.Marshal(g.String())
}
func (g *GUID) UnmarshalJSON(data []byte) error {
*g = FromString(strings.Trim(string(data), "\""))
return nil
}

View File

@ -2,6 +2,7 @@ package hcs
import ( import (
"encoding/json" "encoding/json"
"fmt"
"io" "io"
"sync" "sync"
"syscall" "syscall"
@ -83,7 +84,10 @@ func (process *Process) Kill() error {
} }
var resultp *uint16 var resultp *uint16
completed := false
go syscallWatcher(fmt.Sprintf("TerminateProcess %s: %d", process.SystemID(), process.Pid()), &completed)
err := hcsTerminateProcess(process.handle, &resultp) err := hcsTerminateProcess(process.handle, &resultp)
completed = true
events := processHcsResult(resultp) events := processHcsResult(resultp)
if err != nil { if err != nil {
return makeProcessError(process, operation, err, events) return makeProcessError(process, operation, err, events)
@ -177,7 +181,10 @@ func (process *Process) Properties() (*ProcessStatus, error) {
resultp *uint16 resultp *uint16
propertiesp *uint16 propertiesp *uint16
) )
completed := false
go syscallWatcher(fmt.Sprintf("GetProcessProperties %s: %d", process.SystemID(), process.Pid()), &completed)
err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp) err := hcsGetProcessProperties(process.handle, &propertiesp, &resultp)
completed = true
events := processHcsResult(resultp) events := processHcsResult(resultp)
if err != nil { if err != nil {
return nil, makeProcessError(process, operation, err, events) return nil, makeProcessError(process, operation, err, events)

View File

@ -2,6 +2,7 @@ package hcs
import ( import (
"encoding/json" "encoding/json"
"fmt"
"os" "os"
"strconv" "strconv"
"sync" "sync"
@ -63,7 +64,10 @@ func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (*System,
resultp *uint16 resultp *uint16
identity syscall.Handle identity syscall.Handle
) )
completed := false
go syscallWatcher(fmt.Sprintf("CreateCompleteSystem %s: %s", id, hcsDocument), &completed)
createError := hcsCreateComputeSystem(id, hcsDocument, identity, &computeSystem.handle, &resultp) createError := hcsCreateComputeSystem(id, hcsDocument, identity, &computeSystem.handle, &resultp)
completed = true
if createError == nil || IsPending(createError) { if createError == nil || IsPending(createError) {
if err := computeSystem.registerCallback(); err != nil { if err := computeSystem.registerCallback(); err != nil {
@ -74,7 +78,7 @@ func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (*System,
} }
} }
events, err := processAsyncHcsResult(createError, resultp, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.Duration) events, err := processAsyncHcsResult(createError, resultp, computeSystem.callbackNumber, hcsNotificationSystemCreateCompleted, &timeout.SystemCreate)
if err != nil { if err != nil {
if err == ErrTimeout { if err == ErrTimeout {
// Terminate the compute system if it still exists. We're okay to // Terminate the compute system if it still exists. We're okay to
@ -135,7 +139,10 @@ func GetComputeSystems(q schema1.ComputeSystemQuery) ([]schema1.ContainerPropert
resultp *uint16 resultp *uint16
computeSystemsp *uint16 computeSystemsp *uint16
) )
completed := false
go syscallWatcher(fmt.Sprintf("GetComputeSystems %s:", query), &completed)
err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp) err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp)
completed = true
events := processHcsResult(resultp) events := processHcsResult(resultp)
if err != nil { if err != nil {
return nil, &HcsError{Op: operation, Err: err, Events: events} return nil, &HcsError{Op: operation, Err: err, Events: events}
@ -192,8 +199,11 @@ func (computeSystem *System) Start() error {
} }
var resultp *uint16 var resultp *uint16
completed := false
go syscallWatcher(fmt.Sprintf("StartComputeSystem %s:", computeSystem.ID()), &completed)
err := hcsStartComputeSystem(computeSystem.handle, "", &resultp) err := hcsStartComputeSystem(computeSystem.handle, "", &resultp)
events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.Duration) completed = true
events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemStartCompleted, &timeout.SystemStart)
if err != nil { if err != nil {
return makeSystemError(computeSystem, "Start", "", err, events) return makeSystemError(computeSystem, "Start", "", err, events)
} }
@ -219,7 +229,10 @@ func (computeSystem *System) Shutdown() error {
} }
var resultp *uint16 var resultp *uint16
completed := false
go syscallWatcher(fmt.Sprintf("ShutdownComputeSystem %s:", computeSystem.ID()), &completed)
err := hcsShutdownComputeSystem(computeSystem.handle, "", &resultp) err := hcsShutdownComputeSystem(computeSystem.handle, "", &resultp)
completed = true
events := processHcsResult(resultp) events := processHcsResult(resultp)
if err != nil { if err != nil {
return makeSystemError(computeSystem, "Shutdown", "", err, events) return makeSystemError(computeSystem, "Shutdown", "", err, events)
@ -242,7 +255,10 @@ func (computeSystem *System) Terminate() error {
} }
var resultp *uint16 var resultp *uint16
completed := false
go syscallWatcher(fmt.Sprintf("TerminateComputeSystem %s:", computeSystem.ID()), &completed)
err := hcsTerminateComputeSystem(computeSystem.handle, "", &resultp) err := hcsTerminateComputeSystem(computeSystem.handle, "", &resultp)
completed = true
events := processHcsResult(resultp) events := processHcsResult(resultp)
if err != nil { if err != nil {
return makeSystemError(computeSystem, "Terminate", "", err, events) return makeSystemError(computeSystem, "Terminate", "", err, events)
@ -291,7 +307,10 @@ func (computeSystem *System) Properties(types ...schema1.PropertyType) (*schema1
} }
var resultp, propertiesp *uint16 var resultp, propertiesp *uint16
completed := false
go syscallWatcher(fmt.Sprintf("GetComputeSystemProperties %s:", computeSystem.ID()), &completed)
err = hcsGetComputeSystemProperties(computeSystem.handle, string(queryj), &propertiesp, &resultp) err = hcsGetComputeSystemProperties(computeSystem.handle, string(queryj), &propertiesp, &resultp)
completed = true
events := processHcsResult(resultp) events := processHcsResult(resultp)
if err != nil { if err != nil {
return nil, makeSystemError(computeSystem, "Properties", "", err, events) return nil, makeSystemError(computeSystem, "Properties", "", err, events)
@ -320,8 +339,11 @@ func (computeSystem *System) Pause() error {
} }
var resultp *uint16 var resultp *uint16
completed := false
go syscallWatcher(fmt.Sprintf("PauseComputeSystem %s:", computeSystem.ID()), &completed)
err := hcsPauseComputeSystem(computeSystem.handle, "", &resultp) err := hcsPauseComputeSystem(computeSystem.handle, "", &resultp)
events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.Duration) completed = true
events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemPauseCompleted, &timeout.SystemPause)
if err != nil { if err != nil {
return makeSystemError(computeSystem, "Pause", "", err, events) return makeSystemError(computeSystem, "Pause", "", err, events)
} }
@ -342,8 +364,11 @@ func (computeSystem *System) Resume() error {
} }
var resultp *uint16 var resultp *uint16
completed := false
go syscallWatcher(fmt.Sprintf("ResumeComputeSystem %s:", computeSystem.ID()), &completed)
err := hcsResumeComputeSystem(computeSystem.handle, "", &resultp) err := hcsResumeComputeSystem(computeSystem.handle, "", &resultp)
events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.Duration) completed = true
events, err := processAsyncHcsResult(err, resultp, computeSystem.callbackNumber, hcsNotificationSystemResumeCompleted, &timeout.SystemResume)
if err != nil { if err != nil {
return makeSystemError(computeSystem, "Resume", "", err, events) return makeSystemError(computeSystem, "Resume", "", err, events)
} }
@ -375,7 +400,10 @@ func (computeSystem *System) CreateProcess(c interface{}) (*Process, error) {
configuration := string(configurationb) configuration := string(configurationb)
logrus.Debugf(title+" config=%s", configuration) logrus.Debugf(title+" config=%s", configuration)
completed := false
go syscallWatcher(fmt.Sprintf("CreateProcess %s: %s", computeSystem.ID(), configuration), &completed)
err = hcsCreateProcess(computeSystem.handle, configuration, &processInfo, &processHandle, &resultp) err = hcsCreateProcess(computeSystem.handle, configuration, &processInfo, &processHandle, &resultp)
completed = true
events := processHcsResult(resultp) events := processHcsResult(resultp)
if err != nil { if err != nil {
return nil, makeSystemError(computeSystem, "CreateProcess", configuration, err, events) return nil, makeSystemError(computeSystem, "CreateProcess", configuration, err, events)
@ -415,7 +443,10 @@ func (computeSystem *System) OpenProcess(pid int) (*Process, error) {
return nil, makeSystemError(computeSystem, "OpenProcess", "", ErrAlreadyClosed, nil) return nil, makeSystemError(computeSystem, "OpenProcess", "", ErrAlreadyClosed, nil)
} }
completed := false
go syscallWatcher(fmt.Sprintf("OpenProcess %s: %d", computeSystem.ID(), pid), &completed)
err := hcsOpenProcess(computeSystem.handle, uint32(pid), &processHandle, &resultp) err := hcsOpenProcess(computeSystem.handle, uint32(pid), &processHandle, &resultp)
completed = true
events := processHcsResult(resultp) events := processHcsResult(resultp)
if err != nil { if err != nil {
return nil, makeSystemError(computeSystem, "OpenProcess", "", err, events) return nil, makeSystemError(computeSystem, "OpenProcess", "", err, events)
@ -451,7 +482,11 @@ func (computeSystem *System) Close() error {
return makeSystemError(computeSystem, "Close", "", err, nil) return makeSystemError(computeSystem, "Close", "", err, nil)
} }
if err := hcsCloseComputeSystem(computeSystem.handle); err != nil { completed := false
go syscallWatcher(fmt.Sprintf("CloseComputeSystem %s:", computeSystem.ID()), &completed)
err := hcsCloseComputeSystem(computeSystem.handle)
completed = true
if err != nil {
return makeSystemError(computeSystem, "Close", "", err, nil) return makeSystemError(computeSystem, "Close", "", err, nil)
} }
@ -537,7 +572,10 @@ func (computeSystem *System) Modify(config interface{}) error {
logrus.Debugf(title + " " + requestString) logrus.Debugf(title + " " + requestString)
var resultp *uint16 var resultp *uint16
completed := false
go syscallWatcher(fmt.Sprintf("ModifyComputeSystem %s: %s", computeSystem.ID(), requestString), &completed)
err = hcsModifyComputeSystem(computeSystem.handle, requestString, &resultp) err = hcsModifyComputeSystem(computeSystem.handle, requestString, &resultp)
completed = true
events := processHcsResult(resultp) events := processHcsResult(resultp)
if err != nil { if err != nil {
return makeSystemError(computeSystem, "Modify", requestString, err, events) return makeSystemError(computeSystem, "Modify", requestString, err, events)

View File

@ -0,0 +1,30 @@
package hcs
import (
"time"
"github.com/Microsoft/hcsshim/internal/timeout"
"github.com/sirupsen/logrus"
)
// syscallWatcher is used as a very simple goroutine around calls into
// the platform. In some cases, we have seen HCS APIs not returning due to
// various bugs, and the goroutine making the syscall ends up not returning,
// prior to its async callback. By spinning up a syscallWatcher, it allows
// us to at least log a warning if a syscall doesn't complete in a reasonable
// amount of time.
//
// Usage is:
//
// completed := false
// go syscallWatcher("some description", &completed)
// <syscall>
// completed = true
//
func syscallWatcher(description string, syscallCompleted *bool) {
time.Sleep(timeout.SyscallWatcher)
if *syscallCompleted {
return
}
logrus.Warnf("%s: Did not complete within %s. This may indicate a platform issue. If it appears to be making no forward progress, obtain the stacks and see is there is a syscall stuck in the platform API for a significant length of time.", description, timeout.SyscallWatcher)
}

View File

@ -1,4 +1,4 @@
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT // Code generated by 'go generate'; DO NOT EDIT.
package interop package interop

View File

@ -1,4 +1,4 @@
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT // Code generated by 'go generate'; DO NOT EDIT.
package safefile package safefile

View File

@ -6,21 +6,65 @@ import (
"time" "time"
) )
// Duration is the default time to wait for various operations. var (
// - Waiting for async notifications from HCS // defaultTimeout is the timeout for most operations that is not overridden.
// - Waiting for processes to launch through defaultTimeout = 4 * time.Minute
// - Waiting to copy data to/from a launched processes stdio pipes.
//
// This can be overridden through environment variable `HCS_TIMEOUT_SECONDS`
var Duration = 4 * time.Minute // defaultTimeoutTestdRetry is the retry loop timeout for testd to respond
// for a disk to come online in LCOW.
defaultTimeoutTestdRetry = 5 * time.Second
)
// External variables for HCSShim consumers to use.
var (
// SystemCreate is the timeout for creating a compute system
SystemCreate time.Duration = defaultTimeout
// SystemStart is the timeout for starting a compute system
SystemStart time.Duration = defaultTimeout
// SystemPause is the timeout for pausing a compute system
SystemPause time.Duration = defaultTimeout
// SystemResume is the timeout for resuming a compute system
SystemResume time.Duration = defaultTimeout
// SyscallWatcher is the timeout before warning of a potential stuck platform syscall.
SyscallWatcher time.Duration = defaultTimeout
// Tar2VHD is the timeout for the tar2vhd operation to complete
Tar2VHD time.Duration = defaultTimeout
// ExternalCommandToStart is the timeout for external commands to start
ExternalCommandToStart = defaultTimeout
// ExternalCommandToComplete is the timeout for external commands to complete.
// Generally this means copying data from their stdio pipes.
ExternalCommandToComplete = defaultTimeout
// TestDRetryLoop is the timeout for testd retry loop when onlining a SCSI disk in LCOW
TestDRetryLoop = defaultTimeoutTestdRetry
)
func init() { func init() {
envTimeout := os.Getenv("HCSSHIM_TIMEOUT_SECONDS") SystemCreate = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMCREATE", SystemCreate)
SystemStart = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMSTART", SystemStart)
SystemPause = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMPAUSE", SystemPause)
SystemResume = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSTEMRESUME", SystemResume)
SyscallWatcher = durationFromEnvironment("HCSSHIM_TIMEOUT_SYSCALLWATCHER", SyscallWatcher)
Tar2VHD = durationFromEnvironment("HCSSHIM_TIMEOUT_TAR2VHD", Tar2VHD)
ExternalCommandToStart = durationFromEnvironment("HCSSHIM_TIMEOUT_EXTERNALCOMMANDSTART", ExternalCommandToStart)
ExternalCommandToComplete = durationFromEnvironment("HCSSHIM_TIMEOUT_EXTERNALCOMMANDCOMPLETE", ExternalCommandToComplete)
TestDRetryLoop = durationFromEnvironment("HCSSHIM_TIMEOUT_TESTDRETRYLOOP", TestDRetryLoop)
}
func durationFromEnvironment(env string, defaultValue time.Duration) time.Duration {
envTimeout := os.Getenv(env)
if len(envTimeout) > 0 { if len(envTimeout) > 0 {
e, err := strconv.Atoi(envTimeout) e, err := strconv.Atoi(envTimeout)
if err == nil && e > 0 { if err == nil && e > 0 {
Duration = time.Second * time.Duration(e) return time.Second * time.Duration(e)
} }
} }
return defaultValue
} }

View File

@ -9,15 +9,15 @@ import (
// the parent layer provided. // the parent layer provided.
func CreateLayer(path, parent string) error { func CreateLayer(path, parent string) error {
title := "hcsshim::CreateLayer " title := "hcsshim::CreateLayer "
logrus.Debugf(title+"Flavour %d ID %s parent %s", path, parent) logrus.Debugf(title+"ID %s parent %s", path, parent)
err := createLayer(&stdDriverInfo, path, parent) err := createLayer(&stdDriverInfo, path, parent)
if err != nil { if err != nil {
err = hcserror.Errorf(err, title, "path=%s parent=%s flavour=%d", path, parent) err = hcserror.Errorf(err, title, "path=%s parent=%s", path, parent)
logrus.Error(err) logrus.Error(err)
return err return err
} }
logrus.Debugf(title+" - succeeded path=%s parent=%s flavour=%d", path, parent) logrus.Debugf(title+"- succeeded path=%s parent=%s", path, parent)
return nil return nil
} }

View File

@ -224,7 +224,7 @@ This will be the best place to discuss design and implementation.
For sync communication we have a community slack with a #containerd channel that everyone is welcome to join and chat about development. For sync communication we have a community slack with a #containerd channel that everyone is welcome to join and chat about development.
**Slack:** https://dockr.ly/community **Slack:** https://join.slack.com/t/dockercommunity/shared_invite/enQtNDM4NjAwNDMyOTUwLWZlMDZmYWRjZjk4Zjc5ZGQ5NWZkOWI1Yjk2NGE3ZWVlYjYxM2VhYjczOWIyZDFhZTE3NTUwZWQzMjhmNGYyZTg
### Reporting security issues ### Reporting security issues

View File

@ -443,7 +443,7 @@ type ContentClient interface {
// Only one active stream may exist at a time for each ref. // Only one active stream may exist at a time for each ref.
// //
// Once a write stream has started, it may only write to a single ref, thus // Once a write stream has started, it may only write to a single ref, thus
// once a stream is started, the ref may be ommitted on subsequent writes. // once a stream is started, the ref may be omitted on subsequent writes.
// //
// For any write transaction represented by a ref, only a single write may // For any write transaction represented by a ref, only a single write may
// be made to a given offset. If overlapping writes occur, it is an error. // be made to a given offset. If overlapping writes occur, it is an error.
@ -658,7 +658,7 @@ type ContentServer interface {
// Only one active stream may exist at a time for each ref. // Only one active stream may exist at a time for each ref.
// //
// Once a write stream has started, it may only write to a single ref, thus // Once a write stream has started, it may only write to a single ref, thus
// once a stream is started, the ref may be ommitted on subsequent writes. // once a stream is started, the ref may be omitted on subsequent writes.
// //
// For any write transaction represented by a ref, only a single write may // For any write transaction represented by a ref, only a single write may
// be made to a given offset. If overlapping writes occur, it is an error. // be made to a given offset. If overlapping writes occur, it is an error.

View File

@ -55,7 +55,7 @@ service Content {
// Only one active stream may exist at a time for each ref. // Only one active stream may exist at a time for each ref.
// //
// Once a write stream has started, it may only write to a single ref, thus // Once a write stream has started, it may only write to a single ref, thus
// once a stream is started, the ref may be ommitted on subsequent writes. // once a stream is started, the ref may be omitted on subsequent writes.
// //
// For any write transaction represented by a ref, only a single write may // For any write transaction represented by a ref, only a single write may
// be made to a given offset. If overlapping writes occur, it is an error. // be made to a given offset. If overlapping writes occur, it is an error.

View File

@ -20,9 +20,15 @@ import (
"bufio" "bufio"
"bytes" "bytes"
"compress/gzip" "compress/gzip"
"context"
"fmt" "fmt"
"io" "io"
"os"
"os/exec"
"strconv"
"sync" "sync"
"github.com/containerd/containerd/log"
) )
type ( type (
@ -37,6 +43,13 @@ const (
Gzip Gzip
) )
const disablePigzEnv = "CONTAINERD_DISABLE_PIGZ"
var (
initPigz sync.Once
unpigzPath string
)
var ( var (
bufioReader32KPool = &sync.Pool{ bufioReader32KPool = &sync.Pool{
New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) }, New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
@ -120,11 +133,18 @@ func DecompressStream(archive io.Reader) (DecompressReadCloser, error) {
readBufWrapper := &readCloserWrapper{buf, compression, closer} readBufWrapper := &readCloserWrapper{buf, compression, closer}
return readBufWrapper, nil return readBufWrapper, nil
case Gzip: case Gzip:
gzReader, err := gzip.NewReader(buf) ctx, cancel := context.WithCancel(context.Background())
gzReader, err := gzipDecompress(ctx, buf)
if err != nil { if err != nil {
cancel()
return nil, err return nil, err
} }
readBufWrapper := &readCloserWrapper{gzReader, compression, closer}
readBufWrapper := &readCloserWrapper{gzReader, compression, func() error {
cancel()
return closer()
}}
return readBufWrapper, nil return readBufWrapper, nil
default: default:
return nil, fmt.Errorf("unsupported compression format %s", (&compression).Extension()) return nil, fmt.Errorf("unsupported compression format %s", (&compression).Extension())
@ -151,3 +171,67 @@ func (compression *Compression) Extension() string {
} }
return "" return ""
} }
func gzipDecompress(ctx context.Context, buf io.Reader) (io.ReadCloser, error) {
initPigz.Do(func() {
if unpigzPath = detectPigz(); unpigzPath != "" {
log.L.Debug("using pigz for decompression")
}
})
if unpigzPath == "" {
return gzip.NewReader(buf)
}
return cmdStream(exec.CommandContext(ctx, unpigzPath, "-d", "-c"), buf)
}
func cmdStream(cmd *exec.Cmd, in io.Reader) (io.ReadCloser, error) {
reader, writer := io.Pipe()
cmd.Stdin = in
cmd.Stdout = writer
var errBuf bytes.Buffer
cmd.Stderr = &errBuf
if err := cmd.Start(); err != nil {
return nil, err
}
go func() {
if err := cmd.Wait(); err != nil {
writer.CloseWithError(fmt.Errorf("%s: %s", err, errBuf.String()))
} else {
writer.Close()
}
}()
return reader, nil
}
func detectPigz() string {
path, err := exec.LookPath("unpigz")
if err != nil {
log.L.WithError(err).Debug("unpigz not found, falling back to go gzip")
return ""
}
// Check if pigz disabled via CONTAINERD_DISABLE_PIGZ env variable
value := os.Getenv(disablePigzEnv)
if value == "" {
return path
}
disable, err := strconv.ParseBool(value)
if err != nil {
log.L.WithError(err).Warnf("could not parse %s: %s", disablePigzEnv, value)
return path
}
if disable {
return ""
}
return path
}

View File

@ -99,7 +99,7 @@ func connect(address string, d func(string, time.Duration) (net.Conn, error)) (*
grpc.FailOnNonTempDialError(true), grpc.FailOnNonTempDialError(true),
grpc.WithBackoffMaxDelay(3 * time.Second), grpc.WithBackoffMaxDelay(3 * time.Second),
} }
ctx, cancel := gocontext.WithTimeout(gocontext.Background(), 60*time.Second) ctx, cancel := gocontext.WithTimeout(gocontext.Background(), 2*time.Second)
defer cancel() defer cancel()
conn, err := grpc.DialContext(ctx, dialer.DialAddress(address), gopts...) conn, err := grpc.DialContext(ctx, dialer.DialAddress(address), gopts...)
if err != nil { if err != nil {

View File

@ -124,6 +124,10 @@ var (
Name: "gpus", Name: "gpus",
Usage: "add gpus to the container", Usage: "add gpus to the container",
}, },
cli.BoolFlag{
Name: "allow-new-privs",
Usage: "turn off OCI spec's NoNewPrivileges feature flag",
},
} }
) )

View File

@ -20,10 +20,11 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images/archive"
oci "github.com/containerd/containerd/images/oci"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -34,45 +35,58 @@ var importCommand = cli.Command{
ArgsUsage: "[flags] <in>", ArgsUsage: "[flags] <in>",
Description: `Import images from a tar stream. Description: `Import images from a tar stream.
Implemented formats: Implemented formats:
- oci.v1 (default) - oci.v1
- docker.v1.1
- docker.v1.2
For oci.v1 format, you need to specify --oci-name because an OCI archive contains image refs (tags) For OCI v1, you may need to specify --base-name because an OCI archive may
but does not contain the base image name. contain only partial image references (tags without the base image name).
If no base image name is provided, a name will be generated as "import-%{yyyy-MM-dd}".
e.g. e.g.
$ ctr images import --format oci.v1 --oci-name foo/bar foobar.tar $ ctr images import --base-name foo/bar foobar.tar
If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadbeef", the command will create If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadbeef", the command will create
"foo/bar:latest" and "foo/bar@sha256:deadbeef" images in the containerd store. "foo/bar:latest" and "foo/bar@sha256:deadbeef" images in the containerd store.
`, `,
Flags: append([]cli.Flag{ Flags: append([]cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "format", Name: "base-name",
Value: "oci.v1", Value: "",
Usage: "image format. See DESCRIPTION.", Usage: "base image name for added images, when provided only images with this name prefix are imported",
},
cli.BoolFlag{
Name: "digests",
Usage: "whether to create digest images (default: false)",
}, },
cli.StringFlag{ cli.StringFlag{
Name: "oci-name", Name: "index-name",
Value: "unknown/unknown", Usage: "image name to keep index as, by default index is discarded",
Usage: "prefix added to either oci.v1 ref annotation or digest",
}, },
// TODO(AkihiroSuda): support commands.LabelFlag (for all children objects)
}, commands.SnapshotterFlags...), }, commands.SnapshotterFlags...),
Action: func(context *cli.Context) error { Action: func(context *cli.Context) error {
var ( var (
in = context.Args().First() in = context.Args().First()
imageImporter images.Importer opts []containerd.ImportOpt
) )
switch format := context.String("format"); format { prefix := context.String("base-name")
case "oci.v1": if prefix == "" {
imageImporter = &oci.V1Importer{ prefix = fmt.Sprintf("import-%s", time.Now().Format("2006-01-02"))
ImageName: context.String("oci-name"), opts = append(opts, containerd.WithImageRefTranslator(archive.AddRefPrefix(prefix)))
} } else {
default: // When provided, filter out references which do not match
return fmt.Errorf("unknown format %s", format) opts = append(opts, containerd.WithImageRefTranslator(archive.FilterRefPrefix(prefix)))
}
if context.Bool("digests") {
opts = append(opts, containerd.WithDigestRef(archive.DigestTranslator(prefix)))
}
if idxName := context.String("index-name"); idxName != "" {
opts = append(opts, containerd.WithIndexName(idxName))
} }
client, ctx, cancel, err := commands.NewClient(context) client, ctx, cancel, err := commands.NewClient(context)
@ -90,20 +104,24 @@ If foobar.tar contains an OCI ref named "latest" and anonymous ref "sha256:deadb
return err return err
} }
} }
imgs, err := client.Import(ctx, imageImporter, r) imgs, err := client.Import(ctx, r, opts...)
closeErr := r.Close()
if err != nil { if err != nil {
return err return err
} }
if err = r.Close(); err != nil { if closeErr != nil {
return err return closeErr
} }
log.G(ctx).Debugf("unpacking %d images", len(imgs)) log.G(ctx).Debugf("unpacking %d images", len(imgs))
for _, img := range imgs { for _, img := range imgs {
// TODO: Allow configuration of the platform
image := containerd.NewImage(client, img)
// TODO: Show unpack status // TODO: Show unpack status
fmt.Printf("unpacking %s (%s)...", img.Name(), img.Target().Digest) fmt.Printf("unpacking %s (%s)...", img.Name, img.Target.Digest)
err = img.Unpack(ctx, context.String("snapshotter")) err = image.Unpack(ctx, context.String("snapshotter"))
if err != nil { if err != nil {
return err return err
} }

View File

@ -106,6 +106,10 @@ var Command = cli.Command{
Name: "fifo-dir", Name: "fifo-dir",
Usage: "directory used for storing IO FIFOs", Usage: "directory used for storing IO FIFOs",
}, },
cli.BoolFlag{
Name: "isolated",
Usage: "run the container with vm isolation",
},
}, append(commands.SnapshotterFlags, commands.ContainerFlags...)...), }, append(commands.SnapshotterFlags, commands.ContainerFlags...)...),
Action: func(context *cli.Context) error { Action: func(context *cli.Context) error {
var ( var (

View File

@ -136,6 +136,9 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
if context.IsSet("gpus") { if context.IsSet("gpus") {
opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.Int("gpus")), nvidia.WithAllCapabilities)) opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.Int("gpus")), nvidia.WithAllCapabilities))
} }
if context.IsSet("allow-new-privs") {
opts = append(opts, oci.WithNewPrivileges)
}
} }
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
@ -146,9 +149,8 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
cOpts = append(cOpts, spec) cOpts = append(cOpts, spec)
// oci.WithImageConfig (WithUsername, WithUserID) depends on rootfs snapshot for resolving /etc/passwd. // oci.WithImageConfig (WithUsername, WithUserID) depends on access to rootfs for resolving via
// So cOpts needs to have precedence over opts. // the /etc/{passwd,group} files. So cOpts needs to have precedence over opts.
// TODO: WithUsername, WithUserID should additionally support non-snapshot rootfs
return client.NewContainer(ctx, id, cOpts...) return client.NewContainer(ctx, id, cOpts...)
} }

View File

@ -31,57 +31,80 @@ import (
// NewContainer creates a new container // NewContainer creates a new container
func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) { func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli.Context) (containerd.Container, error) {
var ( var (
ref = context.Args().First() id string
id = context.Args().Get(1)
args = context.Args()[2:]
)
image, err := client.GetImage(ctx, ref)
if err != nil {
return nil, err
}
var (
opts []oci.SpecOpts opts []oci.SpecOpts
cOpts []containerd.NewContainerOpts cOpts []containerd.NewContainerOpts
spec containerd.NewContainerOpts spec containerd.NewContainerOpts
config = context.IsSet("config")
) )
if context.IsSet("config") { if config {
id = context.Args().First()
opts = append(opts, oci.WithSpecFromFile(context.String("config"))) opts = append(opts, oci.WithSpecFromFile(context.String("config")))
} else { } else {
opts = append(opts, oci.WithDefaultSpec()) var (
} ref = context.Args().First()
args = context.Args()[2:]
)
opts = append(opts, oci.WithImageConfig(image)) id = context.Args().Get(1)
opts = append(opts, oci.WithEnv(context.StringSlice("env"))) snapshotter := context.String("snapshotter")
opts = append(opts, withMounts(context)) if snapshotter == "windows-lcow" {
if context.Bool("tty") { opts = append(opts, oci.WithDefaultSpecForPlatform("linux/amd64"))
opts = append(opts, oci.WithTTY) // Clear the rootfs section.
opts = append(opts, oci.WithRootFSPath(""))
con := console.Current() } else {
size, err := con.Size() opts = append(opts, oci.WithDefaultSpec())
if err != nil { }
logrus.WithError(err).Error("console size") opts = append(opts, oci.WithEnv(context.StringSlice("env")))
opts = append(opts, withMounts(context))
image, err := client.GetImage(ctx, ref)
if err != nil {
return nil, err
}
unpacked, err := image.IsUnpacked(ctx, snapshotter)
if err != nil {
return nil, err
}
if !unpacked {
if err := image.Unpack(ctx, snapshotter); err != nil {
return nil, err
}
}
opts = append(opts, oci.WithImageConfig(image))
cOpts = append(cOpts, containerd.WithImage(image))
cOpts = append(cOpts, containerd.WithSnapshotter(snapshotter))
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
if len(args) > 0 {
opts = append(opts, oci.WithProcessArgs(args...))
}
if cwd := context.String("cwd"); cwd != "" {
opts = append(opts, oci.WithProcessCwd(cwd))
}
if context.Bool("tty") {
opts = append(opts, oci.WithTTY)
con := console.Current()
size, err := con.Size()
if err != nil {
logrus.WithError(err).Error("console size")
}
opts = append(opts, oci.WithTTYSize(int(size.Width), int(size.Height)))
}
if context.Bool("isolated") {
opts = append(opts, oci.WithWindowsHyperV)
} }
opts = append(opts, oci.WithTTYSize(int(size.Width), int(size.Height)))
} }
if len(args) > 0 { cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
opts = append(opts, oci.WithProcessArgs(args...)) cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
}
if cwd := context.String("cwd"); cwd != "" {
opts = append(opts, oci.WithProcessCwd(cwd))
}
var s specs.Spec var s specs.Spec
spec = containerd.WithSpec(&s, opts...) spec = containerd.WithSpec(&s, opts...)
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
cOpts = append(cOpts, containerd.WithImage(image))
cOpts = append(cOpts, containerd.WithSnapshotter(context.String("snapshotter")))
cOpts = append(cOpts, containerd.WithNewSnapshot(id, image))
cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil))
cOpts = append(cOpts, spec) cOpts = append(cOpts, spec)
return client.NewContainer(ctx, id, cOpts...) return client.NewContainer(ctx, id, cOpts...)

View File

@ -28,12 +28,12 @@ import (
// //
// The resources specified in this object are used to create tasks from the container. // The resources specified in this object are used to create tasks from the container.
type Container struct { type Container struct {
// ID uniquely identifies the container in a nameapace. // ID uniquely identifies the container in a namespace.
// //
// This property is required and cannot be changed after creation. // This property is required and cannot be changed after creation.
ID string ID string
// Labels provide metadata extension for a contaienr. // Labels provide metadata extension for a container.
// //
// These are optional and fully mutable. // These are optional and fully mutable.
Labels map[string]string Labels map[string]string

View File

@ -70,7 +70,7 @@ func WriteBlob(ctx context.Context, cs Ingester, ref string, r io.Reader, desc o
cw, err := OpenWriter(ctx, cs, WithRef(ref), WithDescriptor(desc)) cw, err := OpenWriter(ctx, cs, WithRef(ref), WithDescriptor(desc))
if err != nil { if err != nil {
if !errdefs.IsAlreadyExists(err) { if !errdefs.IsAlreadyExists(err) {
return err return errors.Wrap(err, "failed to open writer")
} }
return nil // all ready present return nil // all ready present
@ -127,7 +127,7 @@ func OpenWriter(ctx context.Context, cs Ingester, opts ...WriterOpt) (Writer, er
func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected digest.Digest, opts ...Opt) error { func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected digest.Digest, opts ...Opt) error {
ws, err := cw.Status() ws, err := cw.Status()
if err != nil { if err != nil {
return err return errors.Wrap(err, "failed to get status")
} }
if ws.Offset > 0 { if ws.Offset > 0 {
@ -138,7 +138,7 @@ func Copy(ctx context.Context, cw Writer, r io.Reader, size int64, expected dige
} }
if _, err := copyWithBuffer(cw, r); err != nil { if _, err := copyWithBuffer(cw, r); err != nil {
return err return errors.Wrap(err, "failed to copy")
} }
if err := cw.Commit(ctx, size, expected, opts...); err != nil { if err := cw.Commit(ctx, size, expected, opts...); err != nil {

View File

@ -132,11 +132,11 @@ func (w *writer) Commit(ctx context.Context, size int64, expected digest.Digest,
// clean up!! // clean up!!
defer os.RemoveAll(w.path) defer os.RemoveAll(w.path)
if _, err := os.Stat(target); err == nil {
// collision with the target file!
return errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", dgst)
}
if err := os.Rename(ingest, target); err != nil { if err := os.Rename(ingest, target); err != nil {
if os.IsExist(err) {
// collision with the target file!
return errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", dgst)
}
return err return err
} }
commitTime := time.Now() commitTime := time.Now()

View File

@ -57,7 +57,7 @@ func (rw *remoteWriter) Status() (content.Status, error) {
Action: contentapi.WriteActionStat, Action: contentapi.WriteActionStat,
}) })
if err != nil { if err != nil {
return content.Status{}, errors.Wrap(err, "error getting writer status") return content.Status{}, errors.Wrap(errdefs.FromGRPC(err), "error getting writer status")
} }
return content.Status{ return content.Status{
@ -82,7 +82,7 @@ func (rw *remoteWriter) Write(p []byte) (n int, err error) {
Data: p, Data: p,
}) })
if err != nil { if err != nil {
return 0, err return 0, errors.Wrap(errdefs.FromGRPC(err), "failed to send write")
} }
n = int(resp.Offset - offset) n = int(resp.Offset - offset)
@ -112,7 +112,7 @@ func (rw *remoteWriter) Commit(ctx context.Context, size int64, expected digest.
Labels: base.Labels, Labels: base.Labels,
}) })
if err != nil { if err != nil {
return errdefs.FromGRPC(err) return errors.Wrap(errdefs.FromGRPC(err), "commit failed")
} }
if size != 0 && resp.Offset != size { if size != 0 && resp.Offset != size {

View File

@ -139,7 +139,9 @@ func (s *walkingDiff) Compare(ctx context.Context, lower, upper []mount.Mount, o
dgst := cw.Digest() dgst := cw.Digest()
if err := cw.Commit(ctx, 0, dgst, commitopts...); err != nil { if err := cw.Commit(ctx, 0, dgst, commitopts...); err != nil {
return errors.Wrap(err, "failed to commit") if !errdefs.IsAlreadyExists(err) {
return errors.Wrap(err, "failed to commit")
}
} }
info, err := s.store.Info(ctx, dgst) info, err := s.store.Info(ctx, dgst)

View File

@ -22,6 +22,7 @@ import (
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
) )
type exportOpts struct { type exportOpts struct {
@ -51,7 +52,7 @@ func (c *Client) Export(ctx context.Context, exporter images.Exporter, desc ocis
} }
pr, pw := io.Pipe() pr, pw := io.Pipe()
go func() { go func() {
pw.CloseWithError(exporter.Export(ctx, c.ContentStore(), desc, pw)) pw.CloseWithError(errors.Wrap(exporter.Export(ctx, c.ContentStore(), desc, pw), "export failed"))
}() }()
return pr, nil return pr, nil
} }

View File

@ -37,6 +37,8 @@ type Image interface {
Name() string Name() string
// Target descriptor for the image content // Target descriptor for the image content
Target() ocispec.Descriptor Target() ocispec.Descriptor
// Labels of the image
Labels() map[string]string
// Unpack unpacks the image's content into a snapshot // Unpack unpacks the image's content into a snapshot
Unpack(context.Context, string) error Unpack(context.Context, string) error
// RootFS returns the unpacked diffids that make up images rootfs. // RootFS returns the unpacked diffids that make up images rootfs.
@ -86,6 +88,10 @@ func (i *image) Target() ocispec.Descriptor {
return i.i.Target return i.i.Target
} }
func (i *image) Labels() map[string]string {
return i.i.Labels
}
func (i *image) RootFS(ctx context.Context) ([]digest.Digest, error) { func (i *image) RootFS(ctx context.Context) ([]digest.Digest, error) {
provider := i.client.ContentStore() provider := i.client.ContentStore()
return i.i.RootFS(ctx, provider, i.platform) return i.i.RootFS(ctx, provider, i.platform)

View File

@ -0,0 +1,254 @@
/*
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 archive provides a Docker and OCI compatible importer
package archive
import (
"archive/tar"
"bytes"
"context"
"encoding/json"
"io"
"io/ioutil"
"path"
"github.com/containerd/containerd/archive/compression"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
digest "github.com/opencontainers/go-digest"
specs "github.com/opencontainers/image-spec/specs-go"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// ImportIndex imports an index from a tar achive image bundle
// - implements Docker v1.1, v1.2 and OCI v1.
// - prefers OCI v1 when provided
// - creates OCI index for Docker formats
// - normalizes Docker references and adds as OCI ref name
// e.g. alpine:latest -> docker.io/library/alpine:latest
// - existing OCI reference names are untouched
// - TODO: support option to compress layers on ingest
func ImportIndex(ctx context.Context, store content.Store, reader io.Reader) (ocispec.Descriptor, error) {
var (
tr = tar.NewReader(reader)
ociLayout ocispec.ImageLayout
mfsts []struct {
Config string
RepoTags []string
Layers []string
}
symlinks = make(map[string]string)
blobs = make(map[string]ocispec.Descriptor)
)
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return ocispec.Descriptor{}, err
}
if hdr.Typeflag == tar.TypeSymlink {
symlinks[hdr.Name] = path.Join(path.Dir(hdr.Name), hdr.Linkname)
}
if hdr.Typeflag != tar.TypeReg && hdr.Typeflag != tar.TypeRegA {
if hdr.Typeflag != tar.TypeDir {
log.G(ctx).WithField("file", hdr.Name).Debug("file type ignored")
}
continue
}
hdrName := path.Clean(hdr.Name)
if hdrName == ocispec.ImageLayoutFile {
if err = onUntarJSON(tr, &ociLayout); err != nil {
return ocispec.Descriptor{}, errors.Wrapf(err, "untar oci layout %q", hdr.Name)
}
} else if hdrName == "manifest.json" {
if err = onUntarJSON(tr, &mfsts); err != nil {
return ocispec.Descriptor{}, errors.Wrapf(err, "untar manifest %q", hdr.Name)
}
} else {
dgst, err := onUntarBlob(ctx, tr, store, hdr.Size, "tar-"+hdrName)
if err != nil {
return ocispec.Descriptor{}, errors.Wrapf(err, "failed to ingest %q", hdr.Name)
}
blobs[hdrName] = ocispec.Descriptor{
Digest: dgst,
Size: hdr.Size,
}
}
}
// If OCI layout was given, interpret the tar as an OCI layout.
// When not provided, the layout of the tar will be interpretted
// as Docker v1.1 or v1.2.
if ociLayout.Version != "" {
if ociLayout.Version != ocispec.ImageLayoutVersion {
return ocispec.Descriptor{}, errors.Errorf("unsupported OCI version %s", ociLayout.Version)
}
idx, ok := blobs["index.json"]
if !ok {
return ocispec.Descriptor{}, errors.Errorf("missing index.json in OCI layout %s", ocispec.ImageLayoutVersion)
}
idx.MediaType = ocispec.MediaTypeImageIndex
return idx, nil
}
for name, linkname := range symlinks {
desc, ok := blobs[linkname]
if !ok {
return ocispec.Descriptor{}, errors.Errorf("no target for symlink layer from %q to %q", name, linkname)
}
blobs[name] = desc
}
var idx ocispec.Index
for _, mfst := range mfsts {
config, ok := blobs[mfst.Config]
if !ok {
return ocispec.Descriptor{}, errors.Errorf("image config %q not found", mfst.Config)
}
config.MediaType = ocispec.MediaTypeImageConfig
layers, err := resolveLayers(ctx, store, mfst.Layers, blobs)
if err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "failed to resolve layers")
}
manifest := ocispec.Manifest{
Versioned: specs.Versioned{
SchemaVersion: 2,
},
Config: config,
Layers: layers,
}
desc, err := writeManifest(ctx, store, manifest, ocispec.MediaTypeImageManifest)
if err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "write docker manifest")
}
platforms, err := images.Platforms(ctx, store, desc)
if err != nil {
return ocispec.Descriptor{}, errors.Wrap(err, "unable to resolve platform")
}
if len(platforms) > 0 {
// Only one platform can be resolved from non-index manifest,
// The platform can only come from the config included above,
// if the config has no platform it can be safely ommitted.
desc.Platform = &platforms[0]
}
if len(mfst.RepoTags) == 0 {
idx.Manifests = append(idx.Manifests, desc)
} else {
// Add descriptor per tag
for _, ref := range mfst.RepoTags {
mfstdesc := desc
normalized, err := normalizeReference(ref)
if err != nil {
return ocispec.Descriptor{}, err
}
mfstdesc.Annotations = map[string]string{
ocispec.AnnotationRefName: normalized,
}
idx.Manifests = append(idx.Manifests, mfstdesc)
}
}
}
return writeManifest(ctx, store, idx, ocispec.MediaTypeImageIndex)
}
func onUntarJSON(r io.Reader, j interface{}) error {
b, err := ioutil.ReadAll(r)
if err != nil {
return err
}
if err := json.Unmarshal(b, j); err != nil {
return err
}
return nil
}
func onUntarBlob(ctx context.Context, r io.Reader, store content.Ingester, size int64, ref string) (digest.Digest, error) {
dgstr := digest.Canonical.Digester()
if err := content.WriteBlob(ctx, store, ref, io.TeeReader(r, dgstr.Hash()), ocispec.Descriptor{Size: size}); err != nil {
return "", err
}
return dgstr.Digest(), nil
}
func resolveLayers(ctx context.Context, store content.Store, layerFiles []string, blobs map[string]ocispec.Descriptor) ([]ocispec.Descriptor, error) {
var layers []ocispec.Descriptor
for _, f := range layerFiles {
desc, ok := blobs[f]
if !ok {
return nil, errors.Errorf("layer %q not found", f)
}
// Open blob, resolve media type
ra, err := store.ReaderAt(ctx, desc)
if err != nil {
return nil, errors.Wrapf(err, "failed to open %q (%s)", f, desc.Digest)
}
s, err := compression.DecompressStream(content.NewReader(ra))
if err != nil {
return nil, errors.Wrapf(err, "failed to detect compression for %q", f)
}
if s.GetCompression() == compression.Uncompressed {
// TODO: Support compressing and writing back to content store
desc.MediaType = ocispec.MediaTypeImageLayer
} else {
desc.MediaType = ocispec.MediaTypeImageLayerGzip
}
s.Close()
layers = append(layers, desc)
}
return layers, nil
}
func writeManifest(ctx context.Context, cs content.Ingester, manifest interface{}, mediaType string) (ocispec.Descriptor, error) {
manifestBytes, err := json.Marshal(manifest)
if err != nil {
return ocispec.Descriptor{}, err
}
desc := ocispec.Descriptor{
MediaType: mediaType,
Digest: digest.FromBytes(manifestBytes),
Size: int64(len(manifestBytes)),
}
if err := content.WriteBlob(ctx, cs, "manifest-"+desc.Digest.String(), bytes.NewReader(manifestBytes), desc); err != nil {
return ocispec.Descriptor{}, err
}
return desc, nil
}

View File

@ -0,0 +1,86 @@
/*
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 archive
import (
"strings"
"github.com/containerd/cri/pkg/util"
digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors"
)
// FilterRefPrefix restricts references to having the given image
// prefix. Tag-only references will have the prefix prepended.
func FilterRefPrefix(image string) func(string) string {
return refTranslator(image, true)
}
// AddRefPrefix prepends the given image prefix to tag-only references,
// while leaving returning full references unmodified.
func AddRefPrefix(image string) func(string) string {
return refTranslator(image, false)
}
// refTranslator creates a reference which only has a tag or verifies
// a full reference.
func refTranslator(image string, checkPrefix bool) func(string) string {
return func(ref string) string {
// Check if ref is full reference
if strings.ContainsAny(ref, "/:@") {
// If not prefixed, don't include image
if checkPrefix && !isImagePrefix(ref, image) {
return ""
}
return ref
}
return image + ":" + ref
}
}
func isImagePrefix(s, prefix string) bool {
if !strings.HasPrefix(s, prefix) {
return false
}
if len(s) > len(prefix) {
switch s[len(prefix)] {
case '/', ':', '@':
// Prevent matching partial namespaces
default:
return false
}
}
return true
}
func normalizeReference(ref string) (string, error) {
// TODO: Replace this function to not depend on reference package
normalized, err := util.NormalizeImageRef(ref)
if err != nil {
return "", errors.Wrapf(err, "normalize image ref %q", ref)
}
return normalized.String(), nil
}
// DigestTranslator creates a digest reference by adding the
// digest to an image name
func DigestTranslator(prefix string) func(digest.Digest) string {
return func(dgst digest.Digest) string {
return prefix + "@" + dgst.String()
}
}

View File

@ -27,7 +27,7 @@ import (
// Importer is the interface for image importer. // Importer is the interface for image importer.
type Importer interface { type Importer interface {
// Import imports an image from a tar stream. // Import imports an image from a tar stream.
Import(ctx context.Context, store content.Store, reader io.Reader) ([]Image, error) Import(ctx context.Context, store content.Store, reader io.Reader) (ocispec.Descriptor, error)
} }
// Exporter is the interface for image exporter. // Exporter is the interface for image exporter.

View File

@ -94,7 +94,7 @@ func blobRecord(cs content.Provider, desc ocispec.Descriptor) tarRecord {
CopyTo: func(ctx context.Context, w io.Writer) (int64, error) { CopyTo: func(ctx context.Context, w io.Writer) (int64, error) {
r, err := cs.ReaderAt(ctx, desc) r, err := cs.ReaderAt(ctx, desc)
if err != nil { if err != nil {
return 0, err return 0, errors.Wrap(err, "failed to get reader")
} }
defer r.Close() defer r.Close()
@ -103,7 +103,7 @@ func blobRecord(cs content.Provider, desc ocispec.Descriptor) tarRecord {
n, err := io.Copy(io.MultiWriter(w, dgstr.Hash()), content.NewReader(r)) n, err := io.Copy(io.MultiWriter(w, dgstr.Hash()), content.NewReader(r))
if err != nil { if err != nil {
return 0, err return 0, errors.Wrap(err, "failed to copy to tar")
} }
if dgstr.Digest() != desc.Digest { if dgstr.Digest() != desc.Digest {
return 0, errors.Errorf("unexpected digest %s copied", dgstr.Digest()) return 0, errors.Errorf("unexpected digest %s copied", dgstr.Digest())

View File

@ -1,204 +0,0 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package oci provides the importer and the exporter for OCI Image Spec.
package oci
import (
"archive/tar"
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"path"
"strings"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
// V1Importer implements OCI Image Spec v1.
type V1Importer struct {
// ImageName is preprended to either `:` + OCI ref name or `@` + digest (for anonymous refs).
// This field is mandatory atm, but may change in the future. maybe ref map[string]string as in moby/moby#33355
ImageName string
}
var _ images.Importer = &V1Importer{}
// Import implements Importer.
func (oi *V1Importer) Import(ctx context.Context, store content.Store, reader io.Reader) ([]images.Image, error) {
if oi.ImageName == "" {
return nil, errors.New("ImageName not set")
}
tr := tar.NewReader(reader)
var imgrecs []images.Image
foundIndexJSON := false
for {
hdr, err := tr.Next()
if err == io.EOF {
break
}
if err != nil {
return nil, err
}
if hdr.Typeflag != tar.TypeReg && hdr.Typeflag != tar.TypeRegA {
continue
}
hdrName := path.Clean(hdr.Name)
if hdrName == "index.json" {
if foundIndexJSON {
return nil, errors.New("duplicated index.json")
}
foundIndexJSON = true
imgrecs, err = onUntarIndexJSON(tr, oi.ImageName)
if err != nil {
return nil, err
}
continue
}
if strings.HasPrefix(hdrName, "blobs/") {
if err := onUntarBlob(ctx, tr, store, hdrName, hdr.Size); err != nil {
return nil, err
}
}
}
if !foundIndexJSON {
return nil, errors.New("no index.json found")
}
for _, img := range imgrecs {
err := setGCRefContentLabels(ctx, store, img.Target)
if err != nil {
return imgrecs, err
}
}
// FIXME(AkihiroSuda): set GC labels for unreferrenced blobs (i.e. with unknown media types)?
return imgrecs, nil
}
func onUntarIndexJSON(r io.Reader, imageName string) ([]images.Image, error) {
b, err := ioutil.ReadAll(r)
if err != nil {
return nil, err
}
var idx ocispec.Index
if err := json.Unmarshal(b, &idx); err != nil {
return nil, err
}
var imgrecs []images.Image
for _, m := range idx.Manifests {
ref, err := normalizeImageRef(imageName, m)
if err != nil {
return nil, err
}
imgrecs = append(imgrecs, images.Image{
Name: ref,
Target: m,
})
}
return imgrecs, nil
}
func normalizeImageRef(imageName string, manifest ocispec.Descriptor) (string, error) {
digest := manifest.Digest
if digest == "" {
return "", errors.Errorf("manifest with empty digest: %v", manifest)
}
ociRef := manifest.Annotations[ocispec.AnnotationRefName]
if ociRef == "" {
return imageName + "@" + digest.String(), nil
}
return imageName + ":" + ociRef, nil
}
func onUntarBlob(ctx context.Context, r io.Reader, store content.Ingester, name string, size int64) error {
// name is like "blobs/sha256/deadbeef"
split := strings.Split(name, "/")
if len(split) != 3 {
return errors.Errorf("unexpected name: %q", name)
}
algo := digest.Algorithm(split[1])
if !algo.Available() {
return errors.Errorf("unsupported algorithm: %s", algo)
}
dgst := digest.NewDigestFromHex(algo.String(), split[2])
return content.WriteBlob(ctx, store, "unknown-"+dgst.String(), r, ocispec.Descriptor{Size: size, Digest: dgst})
}
// GetChildrenDescriptors returns children blob descriptors for the following supported types:
// - images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest
// - images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex
func GetChildrenDescriptors(r io.Reader, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
switch desc.MediaType {
case images.MediaTypeDockerSchema2Manifest, ocispec.MediaTypeImageManifest:
var manifest ocispec.Manifest
if err := json.NewDecoder(r).Decode(&manifest); err != nil {
return nil, err
}
return append([]ocispec.Descriptor{manifest.Config}, manifest.Layers...), nil
case images.MediaTypeDockerSchema2ManifestList, ocispec.MediaTypeImageIndex:
var index ocispec.Index
if err := json.NewDecoder(r).Decode(&index); err != nil {
return nil, err
}
return index.Manifests, nil
}
return nil, nil
}
func setGCRefContentLabels(ctx context.Context, store content.Store, desc ocispec.Descriptor) error {
info, err := store.Info(ctx, desc.Digest)
if err != nil {
if errdefs.IsNotFound(err) {
// when the archive is created from multi-arch image,
// it may contain only blobs for a certain platform.
// So ErrNotFound (on manifest list) is expected here.
return nil
}
return err
}
ra, err := store.ReaderAt(ctx, desc)
if err != nil {
return err
}
defer ra.Close()
r := content.NewReader(ra)
children, err := GetChildrenDescriptors(r, desc)
if err != nil {
return err
}
if info.Labels == nil {
info.Labels = map[string]string{}
}
for i, child := range children {
// Note: child blob is not guaranteed to be written to the content store. (multi-arch)
info.Labels[fmt.Sprintf("containerd.io/gc.ref.content.%d", i)] = child.Digest.String()
}
if _, err := store.Update(ctx, info, "labels"); err != nil {
return err
}
for _, child := range children {
if err := setGCRefContentLabels(ctx, store, child); err != nil {
return err
}
}
return nil
}

View File

@ -18,35 +18,61 @@ package containerd
import ( import (
"context" "context"
"encoding/json"
"io" "io"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/images/archive"
digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
) )
type importOpts struct { type importOpts struct {
indexName string
imageRefT func(string) string
dgstRefT func(digest.Digest) string
} }
// ImportOpt allows the caller to specify import specific options // ImportOpt allows the caller to specify import specific options
type ImportOpt func(c *importOpts) error type ImportOpt func(*importOpts) error
func resolveImportOpt(opts ...ImportOpt) (importOpts, error) { // WithImageRefTranslator is used to translate the index reference
var iopts importOpts // to an image reference for the image store.
for _, o := range opts { func WithImageRefTranslator(f func(string) string) ImportOpt {
if err := o(&iopts); err != nil { return func(c *importOpts) error {
return iopts, err c.imageRefT = f
} return nil
}
}
// WithDigestRef is used to create digest images for each
// manifest in the index.
func WithDigestRef(f func(digest.Digest) string) ImportOpt {
return func(c *importOpts) error {
c.dgstRefT = f
return nil
}
}
// WithIndexName creates a tag pointing to the imported index
func WithIndexName(name string) ImportOpt {
return func(c *importOpts) error {
c.indexName = name
return nil
} }
return iopts, nil
} }
// Import imports an image from a Tar stream using reader. // Import imports an image from a Tar stream using reader.
// Caller needs to specify importer. Future version may use oci.v1 as the default. // Caller needs to specify importer. Future version may use oci.v1 as the default.
// Note that unreferrenced blobs may be imported to the content store as well. // Note that unreferrenced blobs may be imported to the content store as well.
func (c *Client) Import(ctx context.Context, importer images.Importer, reader io.Reader, opts ...ImportOpt) ([]Image, error) { func (c *Client) Import(ctx context.Context, reader io.Reader, opts ...ImportOpt) ([]images.Image, error) {
_, err := resolveImportOpt(opts...) // unused now var iopts importOpts
if err != nil { for _, o := range opts {
return nil, err if err := o(&iopts); err != nil {
return nil, err
}
} }
ctx, done, err := c.WithLease(ctx) ctx, done, err := c.WithLease(ctx)
@ -55,31 +81,86 @@ func (c *Client) Import(ctx context.Context, importer images.Importer, reader io
} }
defer done(ctx) defer done(ctx)
imgrecs, err := importer.Import(ctx, c.ContentStore(), reader) index, err := archive.ImportIndex(ctx, c.ContentStore(), reader)
if err != nil { if err != nil {
// is.Update() is not called on error
return nil, err return nil, err
} }
is := c.ImageService() var (
var images []Image imgs []images.Image
for _, imgrec := range imgrecs { cs = c.ContentStore()
if updated, err := is.Update(ctx, imgrec, "target"); err != nil { is = c.ImageService()
)
if iopts.indexName != "" {
imgs = append(imgs, images.Image{
Name: iopts.indexName,
Target: index,
})
}
var handler images.HandlerFunc
handler = func(ctx context.Context, desc ocispec.Descriptor) ([]ocispec.Descriptor, error) {
// Only save images at top level
if desc.Digest != index.Digest {
return images.Children(ctx, cs, desc)
}
p, err := content.ReadBlob(ctx, cs, desc)
if err != nil {
return nil, err
}
var idx ocispec.Index
if err := json.Unmarshal(p, &idx); err != nil {
return nil, err
}
for _, m := range idx.Manifests {
if ref := m.Annotations[ocispec.AnnotationRefName]; ref != "" {
if iopts.imageRefT != nil {
ref = iopts.imageRefT(ref)
}
if ref != "" {
imgs = append(imgs, images.Image{
Name: ref,
Target: m,
})
}
}
if iopts.dgstRefT != nil {
ref := iopts.dgstRefT(m.Digest)
if ref != "" {
imgs = append(imgs, images.Image{
Name: ref,
Target: m,
})
}
}
}
return idx.Manifests, nil
}
handler = images.SetChildrenLabels(cs, handler)
if err := images.Walk(ctx, handler, index); err != nil {
return nil, err
}
for i := range imgs {
img, err := is.Update(ctx, imgs[i], "target")
if err != nil {
if !errdefs.IsNotFound(err) { if !errdefs.IsNotFound(err) {
return nil, err return nil, err
} }
created, err := is.Create(ctx, imgrec) img, err = is.Create(ctx, imgs[i])
if err != nil { if err != nil {
return nil, err return nil, err
} }
imgrec = created
} else {
imgrec = updated
} }
imgs[i] = img
images = append(images, NewImage(c, imgrec))
} }
return images, nil
return imgs, nil
} }

View File

@ -19,8 +19,8 @@ package metadata
import ( import (
"context" "context"
"github.com/boltdb/bolt"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
type transactionKey struct{} type transactionKey struct{}

View File

@ -19,8 +19,8 @@ package boltutil
import ( import (
"time" "time"
"github.com/boltdb/bolt"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
var ( var (

View File

@ -17,8 +17,8 @@
package metadata package metadata
import ( import (
"github.com/boltdb/bolt"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
bolt "go.etcd.io/bbolt"
) )
// The layout where a "/" delineates a bucket is described in the following // The layout where a "/" delineates a bucket is described in the following
@ -164,11 +164,11 @@ func getSnapshotterBucket(tx *bolt.Tx, namespace, snapshotter string) *bolt.Buck
} }
func createBlobBucket(tx *bolt.Tx, namespace string, dgst digest.Digest) (*bolt.Bucket, error) { func createBlobBucket(tx *bolt.Tx, namespace string, dgst digest.Digest) (*bolt.Bucket, error) {
bkt, err := createBucketIfNotExists(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectContent, bucketKeyObjectBlob, []byte(dgst.String())) bkt, err := createBucketIfNotExists(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectContent, bucketKeyObjectBlob)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return bkt, nil return bkt.CreateBucket([]byte(dgst.String()))
} }
func getBlobsBucket(tx *bolt.Tx, namespace string) *bolt.Bucket { func getBlobsBucket(tx *bolt.Tx, namespace string) *bolt.Bucket {

View File

@ -21,7 +21,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/filters" "github.com/containerd/containerd/filters"
@ -32,6 +31,7 @@ import (
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
"github.com/gogo/protobuf/types" "github.com/gogo/protobuf/types"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
type containerStore struct { type containerStore struct {

View File

@ -23,7 +23,6 @@ import (
"sync" "sync"
"time" "time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/filters" "github.com/containerd/containerd/filters"
@ -34,6 +33,7 @@ import (
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
type contentStore struct { type contentStore struct {
@ -592,9 +592,6 @@ func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64,
} }
size = nw.desc.Size size = nw.desc.Size
actual = nw.desc.Digest actual = nw.desc.Digest
if getBlobBucket(tx, nw.namespace, actual) != nil {
return "", errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", actual)
}
} else { } else {
status, err := nw.w.Status() status, err := nw.w.Status()
if err != nil { if err != nil {
@ -606,18 +603,16 @@ func (nw *namespacedWriter) commit(ctx context.Context, tx *bolt.Tx, size int64,
size = status.Offset size = status.Offset
actual = nw.w.Digest() actual = nw.w.Digest()
if err := nw.w.Commit(ctx, size, expected); err != nil { if err := nw.w.Commit(ctx, size, expected); err != nil && !errdefs.IsAlreadyExists(err) {
if !errdefs.IsAlreadyExists(err) { return "", err
return "", err
}
if getBlobBucket(tx, nw.namespace, actual) != nil {
return "", errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", actual)
}
} }
} }
bkt, err := createBlobBucket(tx, nw.namespace, actual) bkt, err := createBlobBucket(tx, nw.namespace, actual)
if err != nil { if err != nil {
if err == bolt.ErrBucketExists {
return "", errors.Wrapf(errdefs.ErrAlreadyExists, "content %v", actual)
}
return "", err return "", err
} }

View File

@ -23,12 +23,12 @@ import (
"sync" "sync"
"time" "time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/gc" "github.com/containerd/containerd/gc"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
const ( const (
@ -43,7 +43,7 @@ const (
// dbVersion represents updates to the schema // dbVersion represents updates to the schema
// version which are additions and compatible with // version which are additions and compatible with
// prior version of the same schema. // prior version of the same schema.
dbVersion = 2 dbVersion = 3
) )
// DB represents a metadata database backed by a bolt // DB represents a metadata database backed by a bolt

View File

@ -23,10 +23,10 @@ import (
"strings" "strings"
"time" "time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/gc" "github.com/containerd/containerd/gc"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
const ( const (

View File

@ -23,7 +23,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/filters" "github.com/containerd/containerd/filters"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
@ -33,6 +32,7 @@ import (
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
type imageStore struct { type imageStore struct {

View File

@ -20,7 +20,6 @@ import (
"context" "context"
"time" "time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/filters" "github.com/containerd/containerd/filters"
"github.com/containerd/containerd/leases" "github.com/containerd/containerd/leases"
@ -28,6 +27,7 @@ import (
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
// LeaseManager manages the create/delete lifecyle of leases // LeaseManager manages the create/delete lifecyle of leases

View File

@ -16,7 +16,7 @@
package metadata package metadata
import "github.com/boltdb/bolt" import bolt "go.etcd.io/bbolt"
type migration struct { type migration struct {
schema string schema string
@ -45,6 +45,11 @@ var migrations = []migration{
version: 2, version: 2,
migrate: migrateIngests, migrate: migrateIngests,
}, },
{
schema: "v1",
version: 3,
migrate: noOpMigration,
},
} }
// addChildLinks Adds children key to the snapshotters to enforce snapshot // addChildLinks Adds children key to the snapshotters to enforce snapshot
@ -154,3 +159,10 @@ func migrateIngests(tx *bolt.Tx) error {
return nil return nil
} }
// noOpMigration was for a database change from boltdb/bolt which is no
// longer being supported, to go.etcd.io/bbolt which is the currently
// maintained repo for boltdb.
func noOpMigration(tx *bolt.Tx) error {
return nil
}

View File

@ -19,11 +19,11 @@ package metadata
import ( import (
"context" "context"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
l "github.com/containerd/containerd/labels" l "github.com/containerd/containerd/labels"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
type namespaceStore struct { type namespaceStore struct {

View File

@ -23,7 +23,6 @@ import (
"sync" "sync"
"time" "time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/labels" "github.com/containerd/containerd/labels"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
@ -32,6 +31,7 @@ import (
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
type snapshotter struct { type snapshotter struct {

View File

@ -167,6 +167,7 @@ func populateDefaultUnixSpec(ctx context.Context, s *Spec, id string) error {
Destination: "/proc", Destination: "/proc",
Type: "proc", Type: "proc",
Source: "proc", Source: "proc",
Options: []string{"nosuid", "noexec", "nodev"},
}, },
{ {
Destination: "/dev", Destination: "/dev",

View File

@ -268,6 +268,14 @@ func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
} }
} }
// WithNewPrivileges turns off the NoNewPrivileges feature flag in the spec
func WithNewPrivileges(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
setProcess(s)
s.Process.NoNewPrivileges = false
return nil
}
// WithImageConfig configures the spec to from the configuration of an Image // WithImageConfig configures the spec to from the configuration of an Image
func WithImageConfig(image Image) SpecOpts { func WithImageConfig(image Image) SpecOpts {
return WithImageConfigArgs(image, nil) return WithImageConfigArgs(image, nil)
@ -315,8 +323,14 @@ func WithImageConfigArgs(image Image, args []string) SpecOpts {
} }
s.Process.Cwd = cwd s.Process.Cwd = cwd
if config.User != "" { if config.User != "" {
return WithUser(config.User)(ctx, client, c, s) if err := WithUser(config.User)(ctx, client, c, s); err != nil {
return err
}
return WithAdditionalGIDs(fmt.Sprintf("%d", s.Process.User.UID))(ctx, client, c, s)
} }
// we should query the image's /etc/group for additional GIDs
// even if there is no specified user in the image config
return WithAdditionalGIDs("root")(ctx, client, c, s)
} else if s.Windows != nil { } else if s.Windows != nil {
s.Process.Env = config.Env s.Process.Env = config.Env
s.Process.Args = append(config.Entrypoint, config.Cmd...) s.Process.Args = append(config.Entrypoint, config.Cmd...)
@ -997,3 +1011,14 @@ var WithPrivileged = Compose(
WithApparmorProfile(""), WithApparmorProfile(""),
WithSeccompUnconfined, WithSeccompUnconfined,
) )
// WithWindowsHyperV sets the Windows.HyperV section for HyperV isolation of containers.
func WithWindowsHyperV(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
if s.Windows == nil {
s.Windows = &specs.Windows{}
}
if s.Windows.HyperV == nil {
s.Windows.HyperV = &specs.WindowsHyperV{}
}
return nil
}

View File

@ -46,6 +46,9 @@ func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) {
return nil, err return nil, err
} }
path = filepath.Join(path, id) path = filepath.Join(path, id)
if err := os.Mkdir(path, 0711); err != nil {
return nil, err
}
defer func() { defer func() {
if err != nil { if err != nil {
os.RemoveAll(path) os.RemoveAll(path)
@ -60,10 +63,6 @@ func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) {
os.RemoveAll(workDir) os.RemoveAll(workDir)
} }
}() }()
if err := os.Mkdir(path, 0711); err != nil {
return nil, err
}
if err := os.Mkdir(filepath.Join(path, "rootfs"), 0711); err != nil { if err := os.Mkdir(filepath.Join(path, "rootfs"), 0711); err != nil {
return nil, err return nil, err
} }

View File

@ -26,7 +26,6 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/boltdb/bolt"
eventstypes "github.com/containerd/containerd/api/events" eventstypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/api/types" "github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
@ -49,6 +48,7 @@ import (
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
bolt "go.etcd.io/bbolt"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )

View File

@ -23,7 +23,6 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/events/exchange" "github.com/containerd/containerd/events/exchange"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
@ -32,6 +31,7 @@ import (
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime"
bolt "go.etcd.io/bbolt"
) )
func init() { func init() {

View File

@ -20,7 +20,6 @@ import (
"context" "context"
"io" "io"
"github.com/boltdb/bolt"
eventstypes "github.com/containerd/containerd/api/events" eventstypes "github.com/containerd/containerd/api/events"
api "github.com/containerd/containerd/api/services/containers/v1" api "github.com/containerd/containerd/api/services/containers/v1"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
@ -30,6 +29,7 @@ import (
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/services" "github.com/containerd/containerd/services"
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
bolt "go.etcd.io/bbolt"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
grpcm "google.golang.org/grpc/metadata" grpcm "google.golang.org/grpc/metadata"

View File

@ -213,7 +213,7 @@ func (s *service) Read(req *api.ReadContentRequest, session api.Content_ReadServ
_, err = io.CopyBuffer( _, err = io.CopyBuffer(
&readResponseWriter{session: session}, &readResponseWriter{session: session},
io.NewSectionReader(ra, offset, size), *p) io.NewSectionReader(ra, offset, size), *p)
return err return errdefs.ToGRPC(err)
} }
// readResponseWriter is a writer that places the output into ReadContentRequest messages. // readResponseWriter is a writer that places the output into ReadContentRequest messages.
@ -420,7 +420,7 @@ func (s *service) Write(session api.Content_WriteServer) (err error) {
// maintain the offset as append only, we just issue the write. // maintain the offset as append only, we just issue the write.
n, err := wr.Write(req.Data) n, err := wr.Write(req.Data)
if err != nil { if err != nil {
return err return errdefs.ToGRPC(err)
} }
if n != len(req.Data) { if n != len(req.Data) {
@ -438,7 +438,7 @@ func (s *service) Write(session api.Content_WriteServer) (err error) {
opts = append(opts, content.WithLabels(req.Labels)) opts = append(opts, content.WithLabels(req.Labels))
} }
if err := wr.Commit(ctx, total, expected, opts...); err != nil { if err := wr.Commit(ctx, total, expected, opts...); err != nil {
return err return errdefs.ToGRPC(err)
} }
} }

View File

@ -19,12 +19,12 @@ package leases
import ( import (
"context" "context"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/gc" "github.com/containerd/containerd/gc"
"github.com/containerd/containerd/leases" "github.com/containerd/containerd/leases"
"github.com/containerd/containerd/metadata" "github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/services" "github.com/containerd/containerd/services"
bolt "go.etcd.io/bbolt"
) )
func init() { func init() {

View File

@ -20,7 +20,6 @@ import (
"context" "context"
"strings" "strings"
"github.com/boltdb/bolt"
eventstypes "github.com/containerd/containerd/api/events" eventstypes "github.com/containerd/containerd/api/events"
api "github.com/containerd/containerd/api/services/namespaces/v1" api "github.com/containerd/containerd/api/services/namespaces/v1"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
@ -30,6 +29,7 @@ import (
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/services" "github.com/containerd/containerd/services"
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
bolt "go.etcd.io/bbolt"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"

View File

@ -29,7 +29,6 @@ import (
"sync" "sync"
"time" "time"
"github.com/boltdb/bolt"
csapi "github.com/containerd/containerd/api/services/content/v1" csapi "github.com/containerd/containerd/api/services/content/v1"
ssapi "github.com/containerd/containerd/api/services/snapshots/v1" ssapi "github.com/containerd/containerd/api/services/snapshots/v1"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
@ -46,6 +45,7 @@ import (
metrics "github.com/docker/go-metrics" metrics "github.com/docker/go-metrics"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus" grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
"google.golang.org/grpc" "google.golang.org/grpc"
) )

View File

@ -26,7 +26,6 @@ import (
"path/filepath" "path/filepath"
"time" "time"
"github.com/boltdb/bolt"
api "github.com/containerd/containerd/api/services/tasks/v1" api "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/api/types" "github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/api/types/task" "github.com/containerd/containerd/api/types/task"
@ -48,6 +47,7 @@ import (
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
ocispec "github.com/opencontainers/image-spec/specs-go/v1" ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/codes" "google.golang.org/grpc/codes"
"google.golang.org/grpc/status" "google.golang.org/grpc/status"

View File

@ -23,11 +23,11 @@ import (
"strings" "strings"
"time" "time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/metadata/boltutil" "github.com/containerd/containerd/metadata/boltutil"
"github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
var ( var (

View File

@ -25,9 +25,9 @@ import (
"context" "context"
"sync" "sync"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/snapshots" "github.com/containerd/containerd/snapshots"
"github.com/pkg/errors" "github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
) )
// Transactor is used to finalize an active transaction. // Transactor is used to finalize an active transaction.

View File

@ -607,8 +607,11 @@ func writeContent(ctx context.Context, store content.Ingester, mediaType, ref st
if err != nil { if err != nil {
return d, err return d, err
} }
if err := writer.Commit(ctx, size, "", opts...); err != nil { if err := writer.Commit(ctx, size, "", opts...); err != nil {
return d, err if !errdefs.IsAlreadyExists(err) {
return d, err
}
} }
return v1.Descriptor{ return v1.Descriptor{
MediaType: mediaType, MediaType: mediaType,

View File

@ -4,7 +4,7 @@ github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244 github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244
github.com/containerd/continuity f44b615e492bdfb371aae2f76ec694d9da1db537 github.com/containerd/continuity 7f53d412b9eb1cbf744c2063185d703a0ee34700
github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6 github.com/coreos/go-systemd 48702e0da86bd25e76cfef347e2adeb434a0d0a6
github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098 github.com/docker/go-metrics 4ea375f7759c82740c893fc030bc37088d2ec098
github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9 github.com/docker/go-events 9461782956ad83b30282bf90e31fa6a70c255ba9
@ -19,8 +19,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.0
github.com/gogo/protobuf v1.0.0 github.com/gogo/protobuf v1.0.0
github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef
github.com/golang/protobuf v1.1.0 github.com/golang/protobuf v1.1.0
github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce # v1.0.1-43-gd810dbc github.com/opencontainers/runtime-spec eba862dc2470385a233c7507392675cbeadf7353 # v1.0.1-45-geba862d
github.com/opencontainers/runc 20aff4f0488c6d4b8df4d85b4f63f1f704c11abd github.com/opencontainers/runc 00dc70017d222b178a002ed30e9321b12647af2d
github.com/sirupsen/logrus v1.0.0 github.com/sirupsen/logrus v1.0.0
github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c
golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
@ -33,18 +33,18 @@ golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
github.com/Microsoft/go-winio v0.4.10 github.com/Microsoft/go-winio v0.4.10
github.com/Microsoft/hcsshim 44c060121b68e8bdc40b411beba551f3b4ee9e55 github.com/Microsoft/hcsshim v0.7.4
github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a
github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16 github.com/syndtr/gocapability db04d3cc01c8b54962a58ec7e491717d06cfcc16
gotest.tools v2.1.0 gotest.tools v2.1.0
github.com/google/go-cmp v0.1.0 github.com/google/go-cmp v0.1.0
go.etcd.io/bbolt v1.3.1-etcd.8
# cri dependencies # cri dependencies
github.com/containerd/cri v1.11.1 github.com/containerd/cri 9f39e3289533fc228c5e5fcac0a6dbdd60c6047b # release/1.2 branch
github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534 github.com/containerd/go-cni 6d7b509a054a3cb1c35ed1865d4fde2f0cb547cd
github.com/blang/semver v3.1.0 github.com/blang/semver v3.1.0
github.com/containernetworking/cni v0.6.0 github.com/containernetworking/cni v0.6.0
github.com/containernetworking/plugins v0.7.0 github.com/containernetworking/plugins v0.7.0
@ -52,32 +52,33 @@ github.com/davecgh/go-spew v1.1.0
github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621 github.com/docker/distribution b38e5838b7b2f2ad48e06ec4b500011976080621
github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00 github.com/docker/docker 86f080cff0914e9694068ed78d503701667c4c00
github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528 github.com/docker/spdystream 449fdfce4d962303d702fec724ef0ad181c92528
github.com/emicklei/go-restful ff4f55a206334ef123e4f79bbf348980da81ca46 github.com/emicklei/go-restful v2.2.1
github.com/ghodss/yaml 73d445a93680fa1a78ae23a5839bad48f32ba1ee github.com/ghodss/yaml v1.0.0
github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed github.com/golang/glog 44145f04b68cf362d9c4df2182967c2275eaefed
github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c github.com/google/gofuzz 44d81051d367757e1c7c6a5a86423ece9afcf63c
github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55 github.com/hashicorp/errwrap 7554cd9344cec97297fa6649b055a8c98c2a1e55
github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f github.com/hashicorp/go-multierror ed905158d87462226a13fe39ddf685ea65f1c11f
github.com/json-iterator/go f2b4162afba35581b6d4a50d3b8f34e33c144682 github.com/json-iterator/go 1.1.5
github.com/modern-go/reflect2 05fbef0ca5da472bbf96c9322b84a53edc03c9fd github.com/modern-go/reflect2 1.0.1
github.com/modern-go/concurrent 1.0.3 github.com/modern-go/concurrent 1.0.3
github.com/opencontainers/runtime-tools v0.6.0 github.com/opencontainers/runtime-tools v0.6.0
github.com/opencontainers/selinux 4a2974bf1ee960774ffd517717f1f45325af0206 github.com/opencontainers/selinux b6fa367ed7f534f9ba25391cc2d467085dbb445a
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0 github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
github.com/tchap/go-patricia 5ad6cdb7538b0097d5598c7e57f0a24072adf7dc github.com/tchap/go-patricia v2.2.6
github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6 github.com/xeipuuv/gojsonpointer 4e3ac2762d5f479393488629ee9370b50873b3a6
github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b github.com/xeipuuv/gojsonreference bd5ef7bd5415a7ac448318e64f11a24cd21e594b
github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874 github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874
golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067 golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067
golang.org/x/oauth2 a6bd8cefa1811bd24b86f8902872e4e8225f74c4
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4 gopkg.in/inf.v0 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
gopkg.in/yaml.v2 v2.2.1 gopkg.in/yaml.v2 v2.2.1
k8s.io/api 9e5ffd1f1320950b238cfce291b926411f0af722 k8s.io/api 012f271b5d41baad56190c5f1ae19bff16df0fd8
k8s.io/apimachinery ed135c5b96450fd24e5e981c708114fbbd950697 k8s.io/apimachinery 6429050ef506887d121f3e7306e894f8900d8a63
k8s.io/apiserver a90e3a95c2e91b944bfca8225c4e0d12e42a9eb5 k8s.io/apiserver e9312c15296b6c2c923ebd5031ff5d1d5fd022d7
k8s.io/client-go 03bfb9bdcfe5482795b999f39ca3ed9ad42ce5bb k8s.io/client-go 37c3c02ec96533daec0dbda1f39a6b1d68505c79
k8s.io/kubernetes v1.11.0 k8s.io/kubernetes v1.12.0-beta.1
k8s.io/utils 733eca437aa39379e4bcc25e726439dfca40fcff k8s.io/utils 982821ea41da7e7c15f3d3738921eb2e7e241ccd
# zfs dependencies # zfs dependencies
github.com/containerd/zfs 9a0b8b8b5982014b729cd34eb7cd7a11062aa6ec github.com/containerd/zfs 9a0b8b8b5982014b729cd34eb7cd7a11062aa6ec

View File

@ -21,7 +21,7 @@ var (
Package = "github.com/containerd/containerd" Package = "github.com/containerd/containerd"
// Version holds the complete version number. Filled in at linking time. // Version holds the complete version number. Filled in at linking time.
Version = "1.2.0-beta.2+unknown" Version = "1.2.0-rc.0+unknown"
// Revision is filled with the VCS (e.g. git) revision being used to build // Revision is filled with the VCS (e.g. git) revision being used to build
// the program at linking time. // the program at linking time.

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 continuity package continuity
import ( 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 devices package devices
import "fmt" import "fmt"

View File

@ -1,5 +1,21 @@
// +build linux darwin freebsd solaris // +build linux darwin freebsd 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 devices package devices
import ( 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 devices package devices
import ( 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 continuity package continuity
import ( 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 driver package driver
import ( import (

View File

@ -1,5 +1,21 @@
// +build linux darwin freebsd solaris // +build linux darwin freebsd 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 driver package driver
import ( import (
@ -13,7 +29,11 @@ import (
) )
func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error { func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error {
return devices.Mknod(path, mode, major, minor) err := devices.Mknod(path, mode, major, minor)
if err != nil {
err = &os.PathError{Op: "mknod", Path: path, Err: err}
}
return err
} }
func (d *driver) Mkfifo(path string, mode os.FileMode) error { func (d *driver) Mkfifo(path string, mode os.FileMode) error {
@ -22,7 +42,11 @@ func (d *driver) Mkfifo(path string, mode os.FileMode) error {
} }
// mknod with a mode that has ModeNamedPipe set creates a fifo, not a // mknod with a mode that has ModeNamedPipe set creates a fifo, not a
// device. // device.
return devices.Mknod(path, mode, 0, 0) err := devices.Mknod(path, mode, 0, 0)
if err != nil {
err = &os.PathError{Op: "mkfifo", Path: path, Err: err}
}
return err
} }
// Getxattr returns all of the extended attributes for the file at path p. // Getxattr returns all of the extended attributes for the file at path p.

View File

@ -1,18 +1,33 @@
/*
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 driver package driver
import ( import (
"os" "os"
"github.com/containerd/continuity/sysx" "github.com/containerd/continuity/sysx"
"github.com/pkg/errors"
) )
func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error { func (d *driver) Mknod(path string, mode os.FileMode, major, minor int) error {
return errors.Wrap(ErrNotSupported, "cannot create device node on Windows") return &os.PathError{Op: "mknod", Path: path, Err: ErrNotSupported}
} }
func (d *driver) Mkfifo(path string, mode os.FileMode) error { func (d *driver) Mkfifo(path string, mode os.FileMode) error {
return errors.Wrap(ErrNotSupported, "cannot create fifo on Windows") return &os.PathError{Op: "mkfifo", Path: path, Err: ErrNotSupported}
} }
// Lchmod changes the mode of an file not following symlinks. // Lchmod changes the mode of an file not following symlinks.

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 driver package driver
import ( import (
@ -15,5 +31,9 @@ func (d *driver) Lchmod(path string, mode os.FileMode) error {
return nil return nil
} }
return unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), 0) err := unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), 0)
if err != nil {
err = &os.PathError{Op: "lchmod", Path: path, Err: err}
}
return err
} }

View File

@ -1,5 +1,21 @@
// +build darwin freebsd solaris // +build darwin freebsd 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 driver package driver
import ( import (
@ -10,5 +26,9 @@ import (
// Lchmod changes the mode of a file not following symlinks. // Lchmod changes the mode of a file not following symlinks.
func (d *driver) Lchmod(path string, mode os.FileMode) error { func (d *driver) Lchmod(path string, mode os.FileMode) error {
return unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), unix.AT_SYMLINK_NOFOLLOW) err := unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), unix.AT_SYMLINK_NOFOLLOW)
if err != nil {
err = &os.PathError{Op: "lchmod", Path: path, Err: err}
}
return err
} }

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 driver package driver
import ( 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 fs package fs
import ( 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 fs package fs
import ( import (

View File

@ -1,5 +1,21 @@
// +build solaris darwin freebsd // +build solaris darwin freebsd
/*
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 fs package fs
import ( 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 fs package fs
import ( 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 fs package fs
import ( import (

View File

@ -1,5 +1,21 @@
// +build !windows // +build !windows
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fs package fs
import ( 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 fs package fs
import ( import (

View File

@ -1,5 +1,21 @@
// +build linux // +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 fs package fs
import ( 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 fs package fs
import "context" import "context"

View File

@ -1,5 +1,21 @@
// +build !windows // +build !windows
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fs package fs
import ( import (

View File

@ -1,5 +1,21 @@
// +build windows // +build windows
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fs package fs
import ( 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 fs package fs
import "os" import "os"

View File

@ -1,5 +1,21 @@
// +build !windows // +build !windows
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fs package fs
import ( 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 fs package fs
import "os" import "os"

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 fs package fs
import ( import (
@ -232,12 +248,6 @@ func walkLink(root, path string, linksWalked *int) (newpath string, islink bool,
if err != nil { if err != nil {
return "", false, err return "", false, err
} }
if filepath.IsAbs(newpath) && strings.HasPrefix(newpath, root) {
newpath = newpath[:len(root)]
if !strings.HasPrefix(newpath, "/") {
newpath = "/" + newpath
}
}
*linksWalked++ *linksWalked++
return newpath, true, nil return newpath, true, nil
} }

View File

@ -1,5 +1,21 @@
// +build darwin freebsd // +build darwin freebsd
/*
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 fs package fs
import ( 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 fs package fs
import ( 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 fs package fs
import "time" import "time"

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 continuity package continuity
import ( 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 continuity package continuity
import ( import (

View File

@ -1,5 +1,21 @@
// +build linux darwin freebsd solaris // +build linux darwin freebsd 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 continuity package continuity
import ( 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 continuity package continuity
import "os" import "os"

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 continuity package continuity
import ( 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 continuity package continuity
import ( 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 pathdriver package pathdriver
import ( 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 proto package proto
//go:generate protoc --go_out=. manifest.proto //go:generate protoc --go_out=. manifest.proto

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