Update containerd to b9eeaa1ce8.

Signed-off-by: Lantao Liu <lantaol@google.com>
This commit is contained in:
Lantao Liu 2018-08-09 01:53:44 -07:00
parent c9d6151526
commit 6379fd0346
352 changed files with 43348 additions and 9244 deletions

View File

@ -25,7 +25,7 @@ import (
_ "github.com/containerd/containerd/diff/walking/plugin" _ "github.com/containerd/containerd/diff/walking/plugin"
_ "github.com/containerd/containerd/gc/scheduler" _ "github.com/containerd/containerd/gc/scheduler"
_ "github.com/containerd/containerd/metrics/cgroups" _ "github.com/containerd/containerd/metrics/cgroups"
_ "github.com/containerd/containerd/runtime/linux" _ "github.com/containerd/containerd/runtime/v1/linux"
_ "github.com/containerd/containerd/services/containers" _ "github.com/containerd/containerd/services/containers"
_ "github.com/containerd/containerd/services/content" _ "github.com/containerd/containerd/services/content"
_ "github.com/containerd/containerd/services/diff" _ "github.com/containerd/containerd/services/diff"

4
cri.go
View File

@ -24,10 +24,10 @@ import (
"github.com/containerd/containerd/api/services/containers/v1" "github.com/containerd/containerd/api/services/containers/v1"
"github.com/containerd/containerd/api/services/diff/v1" "github.com/containerd/containerd/api/services/diff/v1"
"github.com/containerd/containerd/api/services/images/v1" "github.com/containerd/containerd/api/services/images/v1"
"github.com/containerd/containerd/api/services/leases/v1"
"github.com/containerd/containerd/api/services/namespaces/v1" "github.com/containerd/containerd/api/services/namespaces/v1"
"github.com/containerd/containerd/api/services/tasks/v1" "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
@ -137,7 +137,7 @@ func getServicesOpts(ic *plugin.InitContext) ([]containerd.ServicesOpt, error) {
return containerd.WithNamespaceService(s.(namespaces.NamespacesClient)) return containerd.WithNamespaceService(s.(namespaces.NamespacesClient))
}, },
services.LeasesService: func(s interface{}) containerd.ServicesOpt { services.LeasesService: func(s interface{}) containerd.ServicesOpt {
return containerd.WithLeasesService(s.(leases.LeasesClient)) return containerd.WithLeasesService(s.(leases.Manager))
}, },
} { } {
p := plugins[s] p := plugins[s]

View File

@ -123,7 +123,7 @@ func Import(ctx context.Context, client *containerd.Client, reader io.Reader, op
defer deferCancel() defer deferCancel()
if err := done(deferCtx); err != nil { if err := done(deferCtx); err != nil {
// Get lease id from context still works after context is done. // Get lease id from context still works after context is done.
leaseID, _ := leases.Lease(ctx) leaseID, _ := leases.FromContext(ctx)
log.G(ctx).WithError(err).Errorf("Failed to release lease %q", leaseID) log.G(ctx).WithError(err).Errorf("Failed to release lease %q", leaseID)
} }
}() }()

View File

@ -2,13 +2,13 @@ github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
github.com/blang/semver v3.1.0 github.com/blang/semver v3.1.0
github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130 github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
github.com/containerd/console 5d1b48d6114b8c9666f0c8b916f871af97b0a761 github.com/containerd/console 4d8a41f4ce5b9bae77c41786ea2458330f43f081
github.com/containerd/containerd b382b6fe0bdbf7604c0a4f5c2089c0b159ad58b2 github.com/containerd/containerd b9eeaa1ce83dd9970605ddbd0b35d4d3fa5f87bd
github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534 github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534
github.com/containerd/go-runc f271fa2021de855d4d918dbef83c5fe19db1bdd5 github.com/containerd/go-runc edcf3de1f4971445c42d61f20d506b30612aa031
github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40 github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containernetworking/cni v0.6.0 github.com/containernetworking/cni v0.6.0
@ -41,7 +41,7 @@ github.com/modern-go/reflect2 05fbef0ca5da472bbf96c9322b84a53edc03c9fd
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 69663f0bd4b60df09991c08812a60108003fa340 github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340
github.com/opencontainers/runtime-spec v1.0.1 github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce
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 4a2974bf1ee960774ffd517717f1f45325af0206
github.com/pkg/errors v0.8.0 github.com/pkg/errors v0.8.0
@ -62,7 +62,7 @@ github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874
golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067 golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067
golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 https://github.com/golang/sys golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2 https://github.com/golang/sys
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631 golang.org/x/time f51c12702a4d776e4c1fa9b0fabab841babae631
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944

View File

@ -137,6 +137,36 @@ func (c *cgroup) add(process Process) error {
return nil return nil
} }
// AddTask moves the provided tasks (threads) into the new cgroup
func (c *cgroup) AddTask(process Process) error {
if process.Pid <= 0 {
return ErrInvalidPid
}
c.mu.Lock()
defer c.mu.Unlock()
if c.err != nil {
return c.err
}
return c.addTask(process)
}
func (c *cgroup) addTask(process Process) error {
for _, s := range pathers(c.subsystems) {
p, err := c.path(s.Name())
if err != nil {
return err
}
if err := ioutil.WriteFile(
filepath.Join(s.Path(p), cgroupTasks),
[]byte(strconv.Itoa(process.Pid)),
defaultFilePerm,
); err != nil {
return err
}
}
return nil
}
// Delete will remove the control group from each of the subsystems registered // Delete will remove the control group from each of the subsystems registered
func (c *cgroup) Delete() error { func (c *cgroup) Delete() error {
c.mu.Lock() c.mu.Lock()

View File

@ -24,6 +24,7 @@ import (
const ( const (
cgroupProcs = "cgroup.procs" cgroupProcs = "cgroup.procs"
cgroupTasks = "tasks"
defaultDirPerm = 0755 defaultDirPerm = 0755
) )
@ -48,8 +49,10 @@ type Process struct {
type Cgroup interface { type Cgroup interface {
// New creates a new cgroup under the calling cgroup // New creates a new cgroup under the calling cgroup
New(string, *specs.LinuxResources) (Cgroup, error) New(string, *specs.LinuxResources) (Cgroup, error)
// Add adds a process to the cgroup // Add adds a process to the cgroup (cgroup.procs)
Add(Process) error Add(Process) error
// AddTask adds a process to the cgroup (tasks)
AddTask(Process) error
// Delete removes the cgroup as a whole // Delete removes the cgroup as a whole
Delete() error Delete() error
// MoveTo moves all the processes under the calling cgroup to the provided one // MoveTo moves all the processes under the calling cgroup to the provided one

View File

@ -82,6 +82,10 @@ func (c *cpusetController) Create(path string, resources *specs.LinuxResources)
return nil return nil
} }
func (c *cpusetController) Update(path string, resources *specs.LinuxResources) error {
return c.Create(path, resources)
}
func (c *cpusetController) getValues(path string) (cpus []byte, mems []byte, err error) { func (c *cpusetController) getValues(path string) (cpus []byte, mems []byte, err error) {
if cpus, err = ioutil.ReadFile(filepath.Join(path, "cpuset.cpus")); err != nil && !os.IsNotExist(err) { if cpus, err = ioutil.ReadFile(filepath.Join(path, "cpuset.cpus")); err != nil && !os.IsNotExist(err) {
return return

View File

@ -19,6 +19,8 @@
MemoryEntry MemoryEntry
BlkIOStat BlkIOStat
BlkIOEntry BlkIOEntry
RdmaStat
RdmaEntry
*/ */
package cgroups package cgroups
@ -49,6 +51,7 @@ type Metrics struct {
CPU *CPUStat `protobuf:"bytes,3,opt,name=cpu" json:"cpu,omitempty"` CPU *CPUStat `protobuf:"bytes,3,opt,name=cpu" json:"cpu,omitempty"`
Memory *MemoryStat `protobuf:"bytes,4,opt,name=memory" json:"memory,omitempty"` Memory *MemoryStat `protobuf:"bytes,4,opt,name=memory" json:"memory,omitempty"`
Blkio *BlkIOStat `protobuf:"bytes,5,opt,name=blkio" json:"blkio,omitempty"` Blkio *BlkIOStat `protobuf:"bytes,5,opt,name=blkio" json:"blkio,omitempty"`
Rdma *RdmaStat `protobuf:"bytes,6,opt,name=rdma" json:"rdma,omitempty"`
} }
func (m *Metrics) Reset() { *m = Metrics{} } func (m *Metrics) Reset() { *m = Metrics{} }
@ -187,6 +190,25 @@ func (m *BlkIOEntry) Reset() { *m = BlkIOEntry{} }
func (*BlkIOEntry) ProtoMessage() {} func (*BlkIOEntry) ProtoMessage() {}
func (*BlkIOEntry) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{9} } func (*BlkIOEntry) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{9} }
type RdmaStat struct {
Current []*RdmaEntry `protobuf:"bytes,1,rep,name=current" json:"current,omitempty"`
Limit []*RdmaEntry `protobuf:"bytes,2,rep,name=limit" json:"limit,omitempty"`
}
func (m *RdmaStat) Reset() { *m = RdmaStat{} }
func (*RdmaStat) ProtoMessage() {}
func (*RdmaStat) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{10} }
type RdmaEntry struct {
Device string `protobuf:"bytes,1,opt,name=device,proto3" json:"device,omitempty"`
HcaHandles uint32 `protobuf:"varint,2,opt,name=hca_handles,json=hcaHandles,proto3" json:"hca_handles,omitempty"`
HcaObjects uint32 `protobuf:"varint,3,opt,name=hca_objects,json=hcaObjects,proto3" json:"hca_objects,omitempty"`
}
func (m *RdmaEntry) Reset() { *m = RdmaEntry{} }
func (*RdmaEntry) ProtoMessage() {}
func (*RdmaEntry) Descriptor() ([]byte, []int) { return fileDescriptorMetrics, []int{11} }
func init() { func init() {
proto.RegisterType((*Metrics)(nil), "io.containerd.cgroups.v1.Metrics") proto.RegisterType((*Metrics)(nil), "io.containerd.cgroups.v1.Metrics")
proto.RegisterType((*HugetlbStat)(nil), "io.containerd.cgroups.v1.HugetlbStat") proto.RegisterType((*HugetlbStat)(nil), "io.containerd.cgroups.v1.HugetlbStat")
@ -198,6 +220,8 @@ func init() {
proto.RegisterType((*MemoryEntry)(nil), "io.containerd.cgroups.v1.MemoryEntry") proto.RegisterType((*MemoryEntry)(nil), "io.containerd.cgroups.v1.MemoryEntry")
proto.RegisterType((*BlkIOStat)(nil), "io.containerd.cgroups.v1.BlkIOStat") proto.RegisterType((*BlkIOStat)(nil), "io.containerd.cgroups.v1.BlkIOStat")
proto.RegisterType((*BlkIOEntry)(nil), "io.containerd.cgroups.v1.BlkIOEntry") proto.RegisterType((*BlkIOEntry)(nil), "io.containerd.cgroups.v1.BlkIOEntry")
proto.RegisterType((*RdmaStat)(nil), "io.containerd.cgroups.v1.RdmaStat")
proto.RegisterType((*RdmaEntry)(nil), "io.containerd.cgroups.v1.RdmaEntry")
} }
func (m *Metrics) Marshal() (dAtA []byte, err error) { func (m *Metrics) Marshal() (dAtA []byte, err error) {
size := m.Size() size := m.Size()
@ -266,6 +290,16 @@ func (m *Metrics) MarshalTo(dAtA []byte) (int, error) {
} }
i += n4 i += n4
} }
if m.Rdma != nil {
dAtA[i] = 0x32
i++
i = encodeVarintMetrics(dAtA, i, uint64(m.Rdma.Size()))
n5, err := m.Rdma.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n5
}
return i, nil return i, nil
} }
@ -732,6 +766,7 @@ func (m *MemoryEntry) MarshalTo(dAtA []byte) (int, error) {
_ = i _ = i
var l int var l int
_ = l _ = l
if m.Limit != 0 { if m.Limit != 0 {
dAtA[i] = 0x8 dAtA[i] = 0x8
i++ i++
@ -914,6 +949,82 @@ func (m *BlkIOEntry) MarshalTo(dAtA []byte) (int, error) {
return i, nil return i, nil
} }
func (m *RdmaStat) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RdmaStat) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.Current) > 0 {
for _, msg := range m.Current {
dAtA[i] = 0xa
i++
i = encodeVarintMetrics(dAtA, i, uint64(msg.Size()))
n, err := msg.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n
}
}
if len(m.Limit) > 0 {
for _, msg := range m.Limit {
dAtA[i] = 0x12
i++
i = encodeVarintMetrics(dAtA, i, uint64(msg.Size()))
n, err := msg.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n
}
}
return i, nil
}
func (m *RdmaEntry) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *RdmaEntry) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.Device) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintMetrics(dAtA, i, uint64(len(m.Device)))
i += copy(dAtA[i:], m.Device)
}
if m.HcaHandles != 0 {
dAtA[i] = 0x10
i++
i = encodeVarintMetrics(dAtA, i, uint64(m.HcaHandles))
}
if m.HcaObjects != 0 {
dAtA[i] = 0x18
i++
i = encodeVarintMetrics(dAtA, i, uint64(m.HcaObjects))
}
return i, nil
}
func encodeFixed64Metrics(dAtA []byte, offset int, v uint64) int { func encodeFixed64Metrics(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v) dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8) dAtA[offset+1] = uint8(v >> 8)
@ -966,6 +1077,10 @@ func (m *Metrics) Size() (n int) {
l = m.Blkio.Size() l = m.Blkio.Size()
n += 1 + l + sovMetrics(uint64(l)) n += 1 + l + sovMetrics(uint64(l))
} }
if m.Rdma != nil {
l = m.Rdma.Size()
n += 1 + l + sovMetrics(uint64(l))
}
return n return n
} }
@ -1264,6 +1379,40 @@ func (m *BlkIOEntry) Size() (n int) {
return n return n
} }
func (m *RdmaStat) Size() (n int) {
var l int
_ = l
if len(m.Current) > 0 {
for _, e := range m.Current {
l = e.Size()
n += 1 + l + sovMetrics(uint64(l))
}
}
if len(m.Limit) > 0 {
for _, e := range m.Limit {
l = e.Size()
n += 1 + l + sovMetrics(uint64(l))
}
}
return n
}
func (m *RdmaEntry) Size() (n int) {
var l int
_ = l
l = len(m.Device)
if l > 0 {
n += 1 + l + sovMetrics(uint64(l))
}
if m.HcaHandles != 0 {
n += 1 + sovMetrics(uint64(m.HcaHandles))
}
if m.HcaObjects != 0 {
n += 1 + sovMetrics(uint64(m.HcaObjects))
}
return n
}
func sovMetrics(x uint64) (n int) { func sovMetrics(x uint64) (n int) {
for { for {
n++ n++
@ -1287,6 +1436,7 @@ func (this *Metrics) String() string {
`CPU:` + strings.Replace(fmt.Sprintf("%v", this.CPU), "CPUStat", "CPUStat", 1) + `,`, `CPU:` + strings.Replace(fmt.Sprintf("%v", this.CPU), "CPUStat", "CPUStat", 1) + `,`,
`Memory:` + strings.Replace(fmt.Sprintf("%v", this.Memory), "MemoryStat", "MemoryStat", 1) + `,`, `Memory:` + strings.Replace(fmt.Sprintf("%v", this.Memory), "MemoryStat", "MemoryStat", 1) + `,`,
`Blkio:` + strings.Replace(fmt.Sprintf("%v", this.Blkio), "BlkIOStat", "BlkIOStat", 1) + `,`, `Blkio:` + strings.Replace(fmt.Sprintf("%v", this.Blkio), "BlkIOStat", "BlkIOStat", 1) + `,`,
`Rdma:` + strings.Replace(fmt.Sprintf("%v", this.Rdma), "RdmaStat", "RdmaStat", 1) + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@ -1440,6 +1590,29 @@ func (this *BlkIOEntry) String() string {
}, "") }, "")
return s return s
} }
func (this *RdmaStat) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&RdmaStat{`,
`Current:` + strings.Replace(fmt.Sprintf("%v", this.Current), "RdmaEntry", "RdmaEntry", 1) + `,`,
`Limit:` + strings.Replace(fmt.Sprintf("%v", this.Limit), "RdmaEntry", "RdmaEntry", 1) + `,`,
`}`,
}, "")
return s
}
func (this *RdmaEntry) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&RdmaEntry{`,
`Device:` + fmt.Sprintf("%v", this.Device) + `,`,
`HcaHandles:` + fmt.Sprintf("%v", this.HcaHandles) + `,`,
`HcaObjects:` + fmt.Sprintf("%v", this.HcaObjects) + `,`,
`}`,
}, "")
return s
}
func valueToStringMetrics(v interface{}) string { func valueToStringMetrics(v interface{}) string {
rv := reflect.ValueOf(v) rv := reflect.ValueOf(v)
if rv.IsNil() { if rv.IsNil() {
@ -1451,6 +1624,7 @@ func valueToStringMetrics(v interface{}) string {
func (m *Metrics) Unmarshal(dAtA []byte) error { func (m *Metrics) Unmarshal(dAtA []byte) error {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
for iNdEx < l { for iNdEx < l {
preIndex := iNdEx preIndex := iNdEx
var wire uint64 var wire uint64
@ -1640,6 +1814,39 @@ func (m *Metrics) Unmarshal(dAtA []byte) error {
return err return err
} }
iNdEx = postIndex iNdEx = postIndex
case 6:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Rdma", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMetrics
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthMetrics
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Rdma == nil {
m.Rdma = &RdmaStat{}
}
if err := m.Rdma.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipMetrics(dAtA[iNdEx:]) skippy, err := skipMetrics(dAtA[iNdEx:])
@ -3656,6 +3863,236 @@ func (m *BlkIOEntry) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *RdmaStat) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMetrics
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RdmaStat: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RdmaStat: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Current", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMetrics
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthMetrics
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Current = append(m.Current, &RdmaEntry{})
if err := m.Current[len(m.Current)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Limit", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMetrics
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthMetrics
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Limit = append(m.Limit, &RdmaEntry{})
if err := m.Limit[len(m.Limit)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipMetrics(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthMetrics
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *RdmaEntry) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMetrics
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: RdmaEntry: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: RdmaEntry: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Device", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMetrics
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
stringLen |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
intStringLen := int(stringLen)
if intStringLen < 0 {
return ErrInvalidLengthMetrics
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.Device = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field HcaHandles", wireType)
}
m.HcaHandles = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMetrics
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.HcaHandles |= (uint32(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 3:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field HcaObjects", wireType)
}
m.HcaObjects = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowMetrics
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.HcaObjects |= (uint32(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
default:
iNdEx = preIndex
skippy, err := skipMetrics(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthMetrics
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipMetrics(dAtA []byte) (n int, err error) { func skipMetrics(dAtA []byte) (n int, err error) {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0

View File

@ -10,6 +10,7 @@ message Metrics {
CPUStat cpu = 3 [(gogoproto.customname) = "CPU"]; CPUStat cpu = 3 [(gogoproto.customname) = "CPU"];
MemoryStat memory = 4; MemoryStat memory = 4;
BlkIOStat blkio = 5; BlkIOStat blkio = 5;
RdmaStat rdma = 6;
} }
message HugetlbStat { message HugetlbStat {
@ -109,3 +110,14 @@ message BlkIOEntry {
uint64 minor = 4; uint64 minor = 4;
uint64 value = 5; uint64 value = 5;
} }
message RdmaStat {
repeated RdmaEntry current = 1;
repeated RdmaEntry limit = 2;
}
message RdmaEntry {
string device = 1;
uint32 hca_handles = 2;
uint32 hca_objects = 3;
}

153
vendor/github.com/containerd/cgroups/rdma.go generated vendored Normal file
View File

@ -0,0 +1,153 @@
/*
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 cgroups
import (
"io/ioutil"
"math"
"os"
"path/filepath"
"strconv"
"strings"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
type rdmaController struct {
root string
}
func (p *rdmaController) Name() Name {
return Rdma
}
func (p *rdmaController) Path(path string) string {
return filepath.Join(p.root, path)
}
func NewRdma(root string) *rdmaController {
return &rdmaController{
root: filepath.Join(root, string(Rdma)),
}
}
func createCmdString(device string, limits *specs.LinuxRdma) string {
var cmdString string
cmdString = device
if limits.HcaHandles != nil {
cmdString = cmdString + " " + "hca_handle=" + strconv.FormatUint(uint64(*limits.HcaHandles), 10)
}
if limits.HcaObjects != nil {
cmdString = cmdString + " " + "hca_object=" + strconv.FormatUint(uint64(*limits.HcaObjects), 10)
}
return cmdString
}
func (p *rdmaController) Create(path string, resources *specs.LinuxResources) error {
if err := os.MkdirAll(p.Path(path), defaultDirPerm); err != nil {
return err
}
for device, limit := range resources.Rdma {
if device != "" && (limit.HcaHandles != nil || limit.HcaObjects != nil) {
return ioutil.WriteFile(
filepath.Join(p.Path(path), "rdma.max"),
[]byte(createCmdString(device, &limit)),
defaultFilePerm,
)
}
}
return nil
}
func (p *rdmaController) Update(path string, resources *specs.LinuxResources) error {
return p.Create(path, resources)
}
func parseRdmaKV(raw string, entry *RdmaEntry) {
var value uint64
var err error
parts := strings.Split(raw, "=")
switch len(parts) {
case 2:
if parts[1] == "max" {
value = math.MaxUint32
} else {
value, err = parseUint(parts[1], 10, 32)
if err != nil {
return
}
}
if parts[0] == "hca_handle" {
entry.HcaHandles = uint32(value)
} else if parts[0] == "hca_object" {
entry.HcaObjects = uint32(value)
}
}
}
func toRdmaEntry(strEntries []string) []*RdmaEntry {
var rdmaEntries []*RdmaEntry
for i := range strEntries {
parts := strings.Fields(strEntries[i])
switch len(parts) {
case 3:
entry := new(RdmaEntry)
entry.Device = parts[0]
parseRdmaKV(parts[1], entry)
parseRdmaKV(parts[2], entry)
rdmaEntries = append(rdmaEntries, entry)
default:
continue
}
}
return rdmaEntries
}
func (p *rdmaController) Stat(path string, stats *Metrics) error {
currentData, err := ioutil.ReadFile(filepath.Join(p.Path(path), "rdma.current"))
if err != nil {
return err
}
currentPerDevices := strings.Split(string(currentData), "\n")
maxData, err := ioutil.ReadFile(filepath.Join(p.Path(path), "rdma.max"))
if err != nil {
return err
}
maxPerDevices := strings.Split(string(maxData), "\n")
// If device got removed between reading two files, ignore returning
// stats.
if len(currentPerDevices) != len(maxPerDevices) {
return nil
}
currentEntries := toRdmaEntry(currentPerDevices)
maxEntries := toRdmaEntry(maxPerDevices)
stats.Rdma = &RdmaStat{
Current: currentEntries,
Limit: maxEntries,
}
return nil
}

View File

@ -38,6 +38,7 @@ const (
Cpuacct Name = "cpuacct" Cpuacct Name = "cpuacct"
Memory Name = "memory" Memory Name = "memory"
Blkio Name = "blkio" Blkio Name = "blkio"
Rdma Name = "rdma"
) )
// Subsystems returns a complete list of the default cgroups // Subsystems returns a complete list of the default cgroups
@ -55,6 +56,7 @@ func Subsystems() []Name {
Cpuacct, Cpuacct,
Memory, Memory,
Blkio, Blkio,
Rdma,
} }
if !isUserNS { if !isUserNS {
n = append(n, Devices) n = append(n, Devices)

View File

@ -80,6 +80,7 @@ func defaults(root string) ([]Subsystem, error) {
NewCpuacct(root), NewCpuacct(root),
NewMemory(root), NewMemory(root),
NewBlkio(root), NewBlkio(root),
NewRdma(root),
} }
// only add the devices cgroup if we are not in a user namespace // only add the devices cgroup if we are not in a user namespace
// because modifications are not allowed // because modifications are not allowed

View File

@ -166,7 +166,7 @@ If you have [criu](https://criu.org/Main_Page) installed on your machine you can
```go ```go
// checkpoint the task then push it to a registry // checkpoint the task then push it to a registry
checkpoint, err := task.Checkpoint(context, containerd.WithExit) checkpoint, err := task.Checkpoint(context)
err := client.Push(context, "myregistry/checkpoints/redis:master", checkpoint) err := client.Push(context, "myregistry/checkpoints/redis:master", checkpoint)
@ -184,6 +184,28 @@ defer task.Delete(context)
err := task.Start(context) err := task.Start(context)
``` ```
### Snapshot Plugins
In addition to the built-in Snapshot plugins in containerd, additional external
plugins can be configured using GRPC. An external plugin is made available using
the configured name and appears as a plugin alongside the built-in ones.
To add an external snapshot plugin, add the plugin to containerd's config file
(by default at `/etc/containerd/config.toml`). The string following
`proxy_plugin.` will be used as the name of the snapshotter and the address
should refer to a socket with a GRPC listener serving containerd's Snapshot
GRPC API. Remember to restart containerd for any configuration changes to take
effect.
```
[proxy_plugins]
[proxy_plugins.customsnapshot]
type = "snapshot"
address = "/var/run/mysnapshotter.sock"
```
See [PLUGINS.md](PLUGINS.md) for how to create plugins
### Releases and API Stability ### Releases and API Stability
Please see [RELEASES.md](RELEASES.md) for details on versioning and stability Please see [RELEASES.md](RELEASES.md) for details on versioning and stability

View File

@ -18,6 +18,7 @@
UpdateContainerRequest UpdateContainerRequest
UpdateContainerResponse UpdateContainerResponse
DeleteContainerRequest DeleteContainerRequest
ListContainerMessage
*/ */
package containers package containers
@ -218,6 +219,14 @@ func (m *DeleteContainerRequest) Reset() { *m = DeleteContain
func (*DeleteContainerRequest) ProtoMessage() {} func (*DeleteContainerRequest) ProtoMessage() {}
func (*DeleteContainerRequest) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{9} } func (*DeleteContainerRequest) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{9} }
type ListContainerMessage struct {
Container *Container `protobuf:"bytes,1,opt,name=container" json:"container,omitempty"`
}
func (m *ListContainerMessage) Reset() { *m = ListContainerMessage{} }
func (*ListContainerMessage) ProtoMessage() {}
func (*ListContainerMessage) Descriptor() ([]byte, []int) { return fileDescriptorContainers, []int{10} }
func init() { func init() {
proto.RegisterType((*Container)(nil), "containerd.services.containers.v1.Container") proto.RegisterType((*Container)(nil), "containerd.services.containers.v1.Container")
proto.RegisterType((*Container_Runtime)(nil), "containerd.services.containers.v1.Container.Runtime") proto.RegisterType((*Container_Runtime)(nil), "containerd.services.containers.v1.Container.Runtime")
@ -230,6 +239,7 @@ func init() {
proto.RegisterType((*UpdateContainerRequest)(nil), "containerd.services.containers.v1.UpdateContainerRequest") proto.RegisterType((*UpdateContainerRequest)(nil), "containerd.services.containers.v1.UpdateContainerRequest")
proto.RegisterType((*UpdateContainerResponse)(nil), "containerd.services.containers.v1.UpdateContainerResponse") proto.RegisterType((*UpdateContainerResponse)(nil), "containerd.services.containers.v1.UpdateContainerResponse")
proto.RegisterType((*DeleteContainerRequest)(nil), "containerd.services.containers.v1.DeleteContainerRequest") proto.RegisterType((*DeleteContainerRequest)(nil), "containerd.services.containers.v1.DeleteContainerRequest")
proto.RegisterType((*ListContainerMessage)(nil), "containerd.services.containers.v1.ListContainerMessage")
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -245,6 +255,7 @@ const _ = grpc.SupportPackageIsVersion4
type ContainersClient interface { type ContainersClient interface {
Get(ctx context.Context, in *GetContainerRequest, opts ...grpc.CallOption) (*GetContainerResponse, error) Get(ctx context.Context, in *GetContainerRequest, opts ...grpc.CallOption) (*GetContainerResponse, error)
List(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (*ListContainersResponse, error) List(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (*ListContainersResponse, error)
ListStream(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (Containers_ListStreamClient, error)
Create(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error) Create(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error)
Update(ctx context.Context, in *UpdateContainerRequest, opts ...grpc.CallOption) (*UpdateContainerResponse, error) Update(ctx context.Context, in *UpdateContainerRequest, opts ...grpc.CallOption) (*UpdateContainerResponse, error)
Delete(ctx context.Context, in *DeleteContainerRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error) Delete(ctx context.Context, in *DeleteContainerRequest, opts ...grpc.CallOption) (*google_protobuf2.Empty, error)
@ -276,6 +287,38 @@ func (c *containersClient) List(ctx context.Context, in *ListContainersRequest,
return out, nil return out, nil
} }
func (c *containersClient) ListStream(ctx context.Context, in *ListContainersRequest, opts ...grpc.CallOption) (Containers_ListStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_Containers_serviceDesc.Streams[0], c.cc, "/containerd.services.containers.v1.Containers/ListStream", opts...)
if err != nil {
return nil, err
}
x := &containersListStreamClient{stream}
if err := x.ClientStream.SendMsg(in); err != nil {
return nil, err
}
if err := x.ClientStream.CloseSend(); err != nil {
return nil, err
}
return x, nil
}
type Containers_ListStreamClient interface {
Recv() (*ListContainerMessage, error)
grpc.ClientStream
}
type containersListStreamClient struct {
grpc.ClientStream
}
func (x *containersListStreamClient) Recv() (*ListContainerMessage, error) {
m := new(ListContainerMessage)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
func (c *containersClient) Create(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error) { func (c *containersClient) Create(ctx context.Context, in *CreateContainerRequest, opts ...grpc.CallOption) (*CreateContainerResponse, error) {
out := new(CreateContainerResponse) out := new(CreateContainerResponse)
err := grpc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Create", in, out, c.cc, opts...) err := grpc.Invoke(ctx, "/containerd.services.containers.v1.Containers/Create", in, out, c.cc, opts...)
@ -308,6 +351,7 @@ func (c *containersClient) Delete(ctx context.Context, in *DeleteContainerReques
type ContainersServer interface { type ContainersServer interface {
Get(context.Context, *GetContainerRequest) (*GetContainerResponse, error) Get(context.Context, *GetContainerRequest) (*GetContainerResponse, error)
List(context.Context, *ListContainersRequest) (*ListContainersResponse, error) List(context.Context, *ListContainersRequest) (*ListContainersResponse, error)
ListStream(*ListContainersRequest, Containers_ListStreamServer) error
Create(context.Context, *CreateContainerRequest) (*CreateContainerResponse, error) Create(context.Context, *CreateContainerRequest) (*CreateContainerResponse, error)
Update(context.Context, *UpdateContainerRequest) (*UpdateContainerResponse, error) Update(context.Context, *UpdateContainerRequest) (*UpdateContainerResponse, error)
Delete(context.Context, *DeleteContainerRequest) (*google_protobuf2.Empty, error) Delete(context.Context, *DeleteContainerRequest) (*google_protobuf2.Empty, error)
@ -353,6 +397,27 @@ func _Containers_List_Handler(srv interface{}, ctx context.Context, dec func(int
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Containers_ListStream_Handler(srv interface{}, stream grpc.ServerStream) error {
m := new(ListContainersRequest)
if err := stream.RecvMsg(m); err != nil {
return err
}
return srv.(ContainersServer).ListStream(m, &containersListStreamServer{stream})
}
type Containers_ListStreamServer interface {
Send(*ListContainerMessage) error
grpc.ServerStream
}
type containersListStreamServer struct {
grpc.ServerStream
}
func (x *containersListStreamServer) Send(m *ListContainerMessage) error {
return x.ServerStream.SendMsg(m)
}
func _Containers_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { func _Containers_Create_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(CreateContainerRequest) in := new(CreateContainerRequest)
if err := dec(in); err != nil { if err := dec(in); err != nil {
@ -432,7 +497,13 @@ var _Containers_serviceDesc = grpc.ServiceDesc{
Handler: _Containers_Delete_Handler, Handler: _Containers_Delete_Handler,
}, },
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{
{
StreamName: "ListStream",
Handler: _Containers_ListStream_Handler,
ServerStreams: true,
},
},
Metadata: "github.com/containerd/containerd/api/services/containers/v1/containers.proto", Metadata: "github.com/containerd/containerd/api/services/containers/v1/containers.proto",
} }
@ -842,6 +913,34 @@ func (m *DeleteContainerRequest) MarshalTo(dAtA []byte) (int, error) {
return i, nil return i, nil
} }
func (m *ListContainerMessage) Marshal() (dAtA []byte, err error) {
size := m.Size()
dAtA = make([]byte, size)
n, err := m.MarshalTo(dAtA)
if err != nil {
return nil, err
}
return dAtA[:n], nil
}
func (m *ListContainerMessage) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.Container != nil {
dAtA[i] = 0xa
i++
i = encodeVarintContainers(dAtA, i, uint64(m.Container.Size()))
n13, err := m.Container.MarshalTo(dAtA[i:])
if err != nil {
return 0, err
}
i += n13
}
return i, nil
}
func encodeVarintContainers(dAtA []byte, offset int, v uint64) int { func encodeVarintContainers(dAtA []byte, offset int, v uint64) int {
for v >= 1<<7 { for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80) dAtA[offset] = uint8(v&0x7f | 0x80)
@ -1004,6 +1103,16 @@ func (m *DeleteContainerRequest) Size() (n int) {
return n return n
} }
func (m *ListContainerMessage) Size() (n int) {
var l int
_ = l
if m.Container != nil {
l = m.Container.Size()
n += 1 + l + sovContainers(uint64(l))
}
return n
}
func sovContainers(x uint64) (n int) { func sovContainers(x uint64) (n int) {
for { for {
n++ n++
@ -1158,6 +1267,16 @@ func (this *DeleteContainerRequest) String() string {
}, "") }, "")
return s return s
} }
func (this *ListContainerMessage) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&ListContainerMessage{`,
`Container:` + strings.Replace(fmt.Sprintf("%v", this.Container), "Container", "Container", 1) + `,`,
`}`,
}, "")
return s
}
func valueToStringContainers(v interface{}) string { func valueToStringContainers(v interface{}) string {
rv := reflect.ValueOf(v) rv := reflect.ValueOf(v)
if rv.IsNil() { if rv.IsNil() {
@ -2562,6 +2681,89 @@ func (m *DeleteContainerRequest) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *ListContainerMessage) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowContainers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
wire |= (uint64(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
fieldNum := int32(wire >> 3)
wireType := int(wire & 0x7)
if wireType == 4 {
return fmt.Errorf("proto: ListContainerMessage: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: ListContainerMessage: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field Container", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowContainers
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthContainers
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if m.Container == nil {
m.Container = &Container{}
}
if err := m.Container.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipContainers(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthContainers
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipContainers(dAtA []byte) (n int, err error) { func skipContainers(dAtA []byte) (n int, err error) {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
@ -2672,54 +2874,57 @@ func init() {
} }
var fileDescriptorContainers = []byte{ var fileDescriptorContainers = []byte{
// 776 bytes of a gzipped FileDescriptorProto // 820 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x72, 0xd2, 0x50, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcb, 0x6e, 0x13, 0x49,
0x14, 0x26, 0x81, 0x86, 0x72, 0x70, 0x46, 0xe7, 0x8a, 0x18, 0xe3, 0x0c, 0x50, 0x56, 0x8c, 0xa3, 0x14, 0x75, 0xdb, 0x4e, 0x3b, 0xbe, 0x1e, 0x69, 0x46, 0x35, 0x1e, 0x4f, 0x4f, 0x8f, 0x64, 0x3b,
0xc1, 0xa2, 0xa3, 0xfd, 0x71, 0x53, 0xfa, 0x37, 0x8e, 0xad, 0xd3, 0x89, 0x3a, 0xe3, 0xe8, 0xa2, 0x5e, 0x59, 0xa3, 0xa1, 0x9d, 0x18, 0x44, 0x5e, 0x6c, 0xe2, 0xbc, 0x04, 0x24, 0x28, 0xea, 0x80,
0x06, 0xb8, 0xa5, 0x91, 0xfc, 0x99, 0x7b, 0x61, 0x64, 0x5c, 0xe8, 0x23, 0xb8, 0xf3, 0x11, 0x7c, 0x84, 0x60, 0x11, 0xda, 0x76, 0xc5, 0x69, 0xdc, 0x2f, 0xba, 0xca, 0x16, 0x16, 0x8b, 0xc0, 0x1f,
0x95, 0x2e, 0x5d, 0xba, 0xaa, 0x2d, 0x4f, 0xe2, 0xe4, 0x26, 0x21, 0x29, 0x04, 0x85, 0x2a, 0xbb, 0xb0, 0xe3, 0x13, 0xf8, 0x95, 0x2c, 0x59, 0xb2, 0x0a, 0xc4, 0xe2, 0x43, 0x50, 0x57, 0x57, 0xbb,
0x7b, 0xb8, 0xe7, 0xfb, 0xce, 0xc7, 0x77, 0xce, 0xb9, 0x00, 0x7b, 0x6d, 0x8d, 0x1e, 0x77, 0x1b, 0x3b, 0x7e, 0x80, 0x9d, 0x90, 0x5d, 0x5d, 0xd7, 0x3d, 0xf7, 0x9e, 0x3a, 0xb7, 0x4e, 0xb9, 0x61,
0x72, 0xd3, 0x32, 0xaa, 0x4d, 0xcb, 0xa4, 0xaa, 0x66, 0x62, 0xa7, 0x15, 0x3d, 0xaa, 0xb6, 0x56, 0xaf, 0xa5, 0xd3, 0x93, 0x4e, 0x5d, 0x69, 0xd8, 0x66, 0xa5, 0x61, 0x5b, 0x54, 0xd3, 0x2d, 0xec,
0x25, 0xd8, 0xe9, 0x69, 0x4d, 0x4c, 0xc2, 0xcf, 0x49, 0xb5, 0xb7, 0x1c, 0x89, 0x64, 0xdb, 0xb1, 0x36, 0xa3, 0x4b, 0xcd, 0xd1, 0x2b, 0x04, 0xbb, 0x5d, 0xbd, 0x81, 0x49, 0xf8, 0x3b, 0xa9, 0x74,
0xa8, 0x85, 0x96, 0x42, 0x9c, 0x1c, 0x60, 0xe4, 0x48, 0x56, 0x6f, 0x59, 0xca, 0xb5, 0xad, 0xb6, 0x97, 0x22, 0x91, 0xe2, 0xb8, 0x36, 0xb5, 0xd1, 0x42, 0x88, 0x53, 0x02, 0x8c, 0x12, 0xc9, 0xea,
0xc5, 0xb2, 0xab, 0xee, 0xc9, 0x03, 0x4a, 0xb7, 0xda, 0x96, 0xd5, 0xd6, 0x71, 0x95, 0x45, 0x8d, 0x2e, 0xc9, 0xd9, 0x96, 0xdd, 0xb2, 0x59, 0x76, 0xc5, 0x5b, 0xf9, 0x40, 0xf9, 0x9f, 0x96, 0x6d,
0xee, 0x51, 0x55, 0x35, 0xfb, 0xfe, 0xd5, 0xed, 0xd1, 0x2b, 0x6c, 0xd8, 0x34, 0xb8, 0x2c, 0x8d, 0xb7, 0x0c, 0x5c, 0x61, 0x51, 0xbd, 0x73, 0x5c, 0xd1, 0xac, 0x1e, 0xdf, 0xfa, 0x77, 0x78, 0x0b,
0x5e, 0x1e, 0x69, 0x58, 0x6f, 0x1d, 0x1a, 0x2a, 0xe9, 0xf8, 0x19, 0xc5, 0xd1, 0x0c, 0xaa, 0x19, 0x9b, 0x0e, 0x0d, 0x36, 0x8b, 0xc3, 0x9b, 0xc7, 0x3a, 0x36, 0x9a, 0x47, 0xa6, 0x46, 0xda, 0x3c,
0x98, 0x50, 0xd5, 0xb0, 0xbd, 0x84, 0xf2, 0x37, 0x01, 0x32, 0x9b, 0x81, 0x44, 0x94, 0x07, 0x5e, 0xa3, 0x30, 0x9c, 0x41, 0x75, 0x13, 0x13, 0xaa, 0x99, 0x8e, 0x9f, 0x50, 0xfa, 0x20, 0x42, 0x7a,
0x6b, 0x89, 0x5c, 0x89, 0xab, 0x64, 0xea, 0xc2, 0xe0, 0xb4, 0xc8, 0x3f, 0xdd, 0x52, 0x78, 0xad, 0x33, 0xa0, 0x88, 0x72, 0x10, 0xd7, 0x9b, 0x92, 0x50, 0x14, 0xca, 0xe9, 0x9a, 0xd8, 0x3f, 0x2f,
0x85, 0x0e, 0x40, 0xd0, 0xd5, 0x06, 0xd6, 0x89, 0xc8, 0x97, 0x92, 0x95, 0x6c, 0x6d, 0x45, 0xfe, 0xc4, 0xef, 0x6f, 0xa9, 0x71, 0xbd, 0x89, 0x0e, 0x40, 0x34, 0xb4, 0x3a, 0x36, 0x88, 0x14, 0x2f,
0xeb, 0x57, 0x95, 0x87, 0xac, 0xf2, 0x1e, 0x83, 0x6e, 0x9b, 0xd4, 0xe9, 0x2b, 0x3e, 0x0f, 0xca, 0x26, 0xca, 0x99, 0xea, 0x8a, 0xf2, 0xd3, 0xa3, 0x2a, 0x83, 0xaa, 0xca, 0x1e, 0x83, 0x6e, 0x5b,
0xc1, 0x82, 0x66, 0xa8, 0x6d, 0x2c, 0x26, 0xdd, 0x62, 0x8a, 0x17, 0xa0, 0xe7, 0x90, 0x76, 0xba, 0xd4, 0xed, 0xa9, 0xbc, 0x0e, 0xca, 0xc2, 0x9c, 0x6e, 0x6a, 0x2d, 0x2c, 0x25, 0xbc, 0x66, 0xaa,
0xa6, 0xab, 0x51, 0x4c, 0x95, 0xb8, 0x4a, 0xb6, 0xf6, 0x70, 0xa6, 0x42, 0x8a, 0x87, 0x55, 0x02, 0x1f, 0xa0, 0x47, 0x90, 0x72, 0x3b, 0x96, 0xc7, 0x51, 0x4a, 0x16, 0x85, 0x72, 0xa6, 0x7a, 0x67,
0x12, 0x54, 0x81, 0x14, 0xb1, 0x71, 0x53, 0x5c, 0x60, 0x64, 0x39, 0xd9, 0x73, 0x43, 0x0e, 0xdc, 0xa6, 0x46, 0xaa, 0x8f, 0x55, 0x83, 0x22, 0xa8, 0x0c, 0x49, 0xe2, 0xe0, 0x86, 0x34, 0xc7, 0x8a,
0x90, 0x37, 0xcc, 0xbe, 0xc2, 0x32, 0x50, 0x09, 0xb2, 0xc4, 0x54, 0x6d, 0x72, 0x6c, 0x51, 0x8a, 0x65, 0x15, 0x5f, 0x0d, 0x25, 0x50, 0x43, 0xd9, 0xb0, 0x7a, 0x2a, 0xcb, 0x40, 0x45, 0xc8, 0x10,
0x1d, 0x51, 0x60, 0xaa, 0xa2, 0x1f, 0xa1, 0x25, 0xb8, 0x12, 0x84, 0x87, 0x1d, 0xdc, 0x17, 0xd3, 0x4b, 0x73, 0xc8, 0x89, 0x4d, 0x29, 0x76, 0x25, 0x91, 0xb1, 0x8a, 0xfe, 0x84, 0x16, 0xe0, 0xb7,
0x17, 0x53, 0x9e, 0xe1, 0x3e, 0xda, 0x04, 0x68, 0x3a, 0x58, 0xa5, 0xb8, 0x75, 0xa8, 0x52, 0x71, 0x20, 0x3c, 0x6a, 0xe3, 0x9e, 0x94, 0xba, 0x9c, 0xf2, 0x10, 0xf7, 0xd0, 0x26, 0x40, 0xc3, 0xc5,
0x91, 0x15, 0x95, 0xc6, 0x8a, 0xbe, 0x0c, 0x5a, 0x50, 0x5f, 0x3c, 0x39, 0x2d, 0x26, 0xbe, 0xfe, 0x1a, 0xc5, 0xcd, 0x23, 0x8d, 0x4a, 0xf3, 0xac, 0xa9, 0x3c, 0xd2, 0xf4, 0x71, 0x30, 0x82, 0xda,
0x2a, 0x72, 0x4a, 0xc6, 0xc7, 0x6d, 0x50, 0x97, 0xa4, 0x6b, 0xb7, 0x02, 0x92, 0xcc, 0x2c, 0x24, 0xfc, 0xd9, 0x79, 0x21, 0xf6, 0xfe, 0x4b, 0x41, 0x50, 0xd3, 0x1c, 0xb7, 0x41, 0xbd, 0x22, 0x1d,
0x3e, 0x6e, 0x83, 0xa2, 0x06, 0x00, 0xfe, 0x48, 0xb1, 0x49, 0x34, 0xcb, 0x24, 0x22, 0xb0, 0xa6, 0xa7, 0x19, 0x14, 0x49, 0xcf, 0x52, 0x84, 0xe3, 0x36, 0x28, 0xaa, 0x03, 0xe0, 0xd7, 0x14, 0x5b,
0x3d, 0x99, 0xc9, 0xcb, 0xed, 0x21, 0x9c, 0x35, 0xae, 0x9e, 0x72, 0xcb, 0x28, 0x11, 0x56, 0x69, 0x44, 0xb7, 0x2d, 0x22, 0x01, 0x1b, 0xda, 0xbd, 0x99, 0xb4, 0xdc, 0x1e, 0xc0, 0xd9, 0xe0, 0x6a,
0x15, 0xb2, 0x91, 0xce, 0xa2, 0x6b, 0x90, 0x74, 0x6d, 0x61, 0xc3, 0xa3, 0xb8, 0x47, 0xb7, 0xc7, 0x49, 0xaf, 0x8d, 0x1a, 0xa9, 0x2a, 0xaf, 0x42, 0x26, 0x32, 0x59, 0xf4, 0x07, 0x24, 0x3c, 0x59,
0x3d, 0x55, 0xef, 0x62, 0x91, 0xf7, 0x7a, 0xcc, 0x82, 0x35, 0x7e, 0x85, 0x93, 0xf6, 0x21, 0xed, 0xd8, 0xe5, 0x51, 0xbd, 0xa5, 0x37, 0xe3, 0xae, 0x66, 0x74, 0xb0, 0x14, 0xf7, 0x67, 0xcc, 0x82,
0xf7, 0x0a, 0x21, 0x48, 0x99, 0xaa, 0x81, 0x7d, 0x1c, 0x3b, 0x23, 0x19, 0xd2, 0x96, 0x4d, 0x99, 0xb5, 0xf8, 0x8a, 0x20, 0xef, 0x43, 0x8a, 0xcf, 0x0a, 0x21, 0x48, 0x5a, 0x9a, 0x89, 0x39, 0x8e,
0x74, 0xfe, 0x0f, 0x9d, 0x0b, 0x92, 0xa4, 0x17, 0x70, 0x75, 0x44, 0x6e, 0x8c, 0x9a, 0x3b, 0x51, 0xad, 0x91, 0x02, 0x29, 0xdb, 0xa1, 0x8c, 0x7a, 0xfc, 0x07, 0x93, 0x0b, 0x92, 0xe4, 0x43, 0xf8,
0x35, 0x93, 0x28, 0x43, 0x8d, 0xe5, 0x7b, 0x70, 0x7d, 0x17, 0xd3, 0xa1, 0x21, 0x0a, 0xfe, 0xd0, 0x7d, 0x88, 0xee, 0x18, 0x36, 0xff, 0x45, 0xd9, 0x4c, 0x2a, 0x19, 0x72, 0x2c, 0xdd, 0x82, 0x3f,
0xc5, 0x84, 0x4e, 0x5a, 0x91, 0xf2, 0x31, 0xe4, 0x2e, 0xa6, 0x13, 0xdb, 0x32, 0x09, 0x46, 0x07, 0x77, 0x31, 0x1d, 0x08, 0xa2, 0xe2, 0x57, 0x1d, 0x4c, 0xe8, 0x24, 0x8b, 0x94, 0x4e, 0x20, 0x7b,
0x90, 0x19, 0x5a, 0xcc, 0x60, 0xd9, 0xda, 0xdd, 0x59, 0x1a, 0xe1, 0x1b, 0x1f, 0x92, 0x94, 0x97, 0x39, 0x9d, 0x38, 0xb6, 0x45, 0x30, 0x3a, 0x80, 0xf4, 0x40, 0x62, 0x06, 0xcb, 0x54, 0xff, 0x9f,
0xe1, 0xc6, 0x9e, 0x46, 0xc2, 0x52, 0x24, 0x90, 0x26, 0x42, 0xfa, 0x48, 0xd3, 0x29, 0x76, 0x88, 0x65, 0x10, 0x5c, 0xf8, 0xb0, 0x48, 0x69, 0x09, 0xfe, 0xda, 0xd3, 0x49, 0xd8, 0x8a, 0x04, 0xd4,
0xc8, 0x95, 0x92, 0x95, 0x8c, 0x12, 0x84, 0x65, 0x1d, 0xf2, 0xa3, 0x10, 0x5f, 0x9e, 0x02, 0x10, 0x24, 0x48, 0x1d, 0xeb, 0x06, 0xc5, 0x2e, 0x91, 0x84, 0x62, 0xa2, 0x9c, 0x56, 0x83, 0xb0, 0x64,
0x16, 0x66, 0xb0, 0xcb, 0xe9, 0x8b, 0xb0, 0x94, 0xdf, 0x43, 0x7e, 0x93, 0x8d, 0xf3, 0x98, 0x79, 0x40, 0x6e, 0x18, 0xc2, 0xe9, 0xa9, 0x00, 0x61, 0x63, 0x06, 0xbb, 0x1a, 0xbf, 0x48, 0x95, 0xd2,
0xff, 0xdf, 0x8c, 0x0e, 0xdc, 0x1c, 0xab, 0x35, 0x37, 0xe7, 0xbf, 0x73, 0x90, 0x7f, 0xc5, 0x76, 0x4b, 0xc8, 0x6d, 0xb2, 0xeb, 0x3c, 0x22, 0xde, 0xaf, 0x17, 0xa3, 0x0d, 0x7f, 0x8f, 0xf4, 0xba,
0x6c, 0xfe, 0xdf, 0x0c, 0xad, 0x43, 0xd6, 0xdb, 0x67, 0xf6, 0x9e, 0xfb, 0x53, 0x3b, 0xfe, 0x10, 0x31, 0xe5, 0x3f, 0x0a, 0x90, 0x7b, 0xc2, 0x3c, 0x76, 0xf3, 0x27, 0x43, 0xeb, 0x90, 0xf1, 0xfd,
0xec, 0xb8, 0x4f, 0xfe, 0xbe, 0x4a, 0x3a, 0x8a, 0xff, 0x6c, 0xb8, 0x67, 0xd7, 0x96, 0x31, 0xa1, 0xcc, 0xde, 0x73, 0x7e, 0x6b, 0x47, 0x1f, 0x82, 0x1d, 0xef, 0xc9, 0xdf, 0xd7, 0x48, 0x5b, 0xe5,
0x73, 0xb3, 0xe5, 0x3e, 0xe4, 0xb7, 0xb0, 0x8e, 0x63, 0x5c, 0x99, 0xb0, 0x2c, 0xb5, 0xb3, 0x14, 0xcf, 0x86, 0xb7, 0xf6, 0x64, 0x19, 0x21, 0x7a, 0x63, 0xb2, 0x2c, 0x42, 0x6e, 0x0b, 0x1b, 0x78,
0x40, 0x38, 0x8c, 0xa8, 0x07, 0xc9, 0x5d, 0x4c, 0xd1, 0xa3, 0x29, 0x64, 0xc4, 0xac, 0xa4, 0xf4, 0x8c, 0x2a, 0x93, 0xcc, 0x52, 0x87, 0xec, 0xa5, 0xfb, 0xb8, 0x8f, 0x09, 0xf1, 0xde, 0xff, 0x07,
0x78, 0x66, 0x9c, 0x6f, 0xc5, 0x27, 0x48, 0xb9, 0x6b, 0x81, 0xa6, 0xf9, 0x39, 0x8b, 0x5d, 0x39, 0xd7, 0xe4, 0x16, 0x61, 0x55, 0xfd, 0x36, 0x07, 0x10, 0x5e, 0x78, 0xd4, 0x85, 0xc4, 0x2e, 0xa6,
0x69, 0xf5, 0x12, 0x48, 0xbf, 0xf8, 0x67, 0x10, 0xbc, 0xc9, 0x45, 0xd3, 0x90, 0xc4, 0x2f, 0x94, 0xe8, 0xee, 0x14, 0xe5, 0xc6, 0xd8, 0x5e, 0x5e, 0x9e, 0x19, 0xc7, 0xe5, 0x7e, 0x03, 0x49, 0xef,
0xb4, 0x76, 0x19, 0x68, 0x28, 0xc0, 0x9b, 0x91, 0xa9, 0x04, 0xc4, 0xcf, 0xfd, 0x54, 0x02, 0x26, 0xa8, 0x68, 0x9a, 0xbf, 0xcc, 0xb1, 0xb6, 0x96, 0x57, 0xaf, 0x80, 0xe4, 0xcd, 0xdf, 0x09, 0x00,
0x4d, 0xe2, 0x5b, 0x10, 0xbc, 0xb9, 0x99, 0x4a, 0x40, 0xfc, 0x88, 0x49, 0xf9, 0xb1, 0x8d, 0xd8, 0xde, 0xd6, 0x21, 0x75, 0xb1, 0x66, 0x5e, 0x83, 0xc3, 0xf2, 0xac, 0x48, 0x3e, 0xd1, 0x45, 0x01,
0x76, 0xff, 0x21, 0xd5, 0xdf, 0x9d, 0x9c, 0x17, 0x12, 0x3f, 0xcf, 0x0b, 0x89, 0x2f, 0x83, 0x02, 0x9d, 0x82, 0xe8, 0x3b, 0x14, 0x4d, 0x73, 0x90, 0xf1, 0x0f, 0x87, 0xbc, 0x76, 0x15, 0x28, 0x17,
0x77, 0x32, 0x28, 0x70, 0x3f, 0x06, 0x05, 0xee, 0x6c, 0x50, 0xe0, 0xde, 0xec, 0xfc, 0xc3, 0x9f, 0xe1, 0x14, 0x44, 0xdf, 0x0b, 0x53, 0x11, 0x18, 0xef, 0xef, 0xa9, 0x08, 0x4c, 0x72, 0xdc, 0x73,
0xbe, 0xf5, 0x30, 0x7a, 0x9d, 0x68, 0x08, 0xac, 0xe6, 0x83, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff, 0x10, 0x7d, 0x7f, 0x4c, 0x45, 0x60, 0xbc, 0x95, 0xe4, 0xdc, 0x88, 0xf3, 0xb7, 0xbd, 0x2f, 0xc1,
0x95, 0x94, 0x84, 0xf2, 0x47, 0x0a, 0x00, 0x00, 0xda, 0x8b, 0xb3, 0x8b, 0x7c, 0xec, 0xf3, 0x45, 0x3e, 0xf6, 0xb6, 0x9f, 0x17, 0xce, 0xfa, 0x79,
0xe1, 0x53, 0x3f, 0x2f, 0x7c, 0xed, 0xe7, 0x85, 0x67, 0x3b, 0xd7, 0xf8, 0xb8, 0x5d, 0x0f, 0xa3,
0xa7, 0xb1, 0xba, 0xc8, 0x7a, 0xde, 0xfe, 0x1e, 0x00, 0x00, 0xff, 0xff, 0xd0, 0xae, 0xca, 0xcb,
0x2f, 0x0b, 0x00, 0x00,
} }

View File

@ -29,6 +29,7 @@ option go_package = "github.com/containerd/containerd/api/services/containers/v1
service Containers { service Containers {
rpc Get(GetContainerRequest) returns (GetContainerResponse); rpc Get(GetContainerRequest) returns (GetContainerResponse);
rpc List(ListContainersRequest) returns (ListContainersResponse); rpc List(ListContainersRequest) returns (ListContainersResponse);
rpc ListStream(ListContainersRequest) returns (stream ListContainerMessage);
rpc Create(CreateContainerRequest) returns (CreateContainerResponse); rpc Create(CreateContainerRequest) returns (CreateContainerResponse);
rpc Update(UpdateContainerRequest) returns (UpdateContainerResponse); rpc Update(UpdateContainerRequest) returns (UpdateContainerResponse);
rpc Delete(DeleteContainerRequest) returns (google.protobuf.Empty); rpc Delete(DeleteContainerRequest) returns (google.protobuf.Empty);
@ -156,3 +157,7 @@ message UpdateContainerResponse {
message DeleteContainerRequest { message DeleteContainerRequest {
string id = 1; string id = 1;
} }
message ListContainerMessage {
Container container = 1;
}

View File

@ -82,6 +82,11 @@ func (*CreateResponse) Descriptor() ([]byte, []int) { return fileDescriptorLease
type DeleteRequest struct { type DeleteRequest struct {
ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
// Sync indicates that the delete and cleanup should be done
// synchronously before returning to the caller
//
// Default is false
Sync bool `protobuf:"varint,2,opt,name=sync,proto3" json:"sync,omitempty"`
} }
func (m *DeleteRequest) Reset() { *m = DeleteRequest{} } func (m *DeleteRequest) Reset() { *m = DeleteRequest{} }
@ -404,6 +409,16 @@ func (m *DeleteRequest) MarshalTo(dAtA []byte) (int, error) {
i = encodeVarintLeases(dAtA, i, uint64(len(m.ID))) i = encodeVarintLeases(dAtA, i, uint64(len(m.ID)))
i += copy(dAtA[i:], m.ID) i += copy(dAtA[i:], m.ID)
} }
if m.Sync {
dAtA[i] = 0x10
i++
if m.Sync {
dAtA[i] = 1
} else {
dAtA[i] = 0
}
i++
}
return i, nil return i, nil
} }
@ -534,6 +549,9 @@ func (m *DeleteRequest) Size() (n int) {
if l > 0 { if l > 0 {
n += 1 + l + sovLeases(uint64(l)) n += 1 + l + sovLeases(uint64(l))
} }
if m.Sync {
n += 2
}
return n return n
} }
@ -633,6 +651,7 @@ func (this *DeleteRequest) String() string {
} }
s := strings.Join([]string{`&DeleteRequest{`, s := strings.Join([]string{`&DeleteRequest{`,
`ID:` + fmt.Sprintf("%v", this.ID) + `,`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`,
`Sync:` + fmt.Sprintf("%v", this.Sync) + `,`,
`}`, `}`,
}, "") }, "")
return s return s
@ -1230,6 +1249,26 @@ func (m *DeleteRequest) Unmarshal(dAtA []byte) error {
} }
m.ID = string(dAtA[iNdEx:postIndex]) m.ID = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex iNdEx = postIndex
case 2:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field Sync", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowLeases
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
v |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
m.Sync = bool(v != 0)
default: default:
iNdEx = preIndex iNdEx = preIndex
skippy, err := skipLeases(dAtA[iNdEx:]) skippy, err := skipLeases(dAtA[iNdEx:])
@ -1521,37 +1560,38 @@ func init() {
} }
var fileDescriptorLeases = []byte{ var fileDescriptorLeases = []byte{
// 504 bytes of a gzipped FileDescriptorProto // 515 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0xdf, 0x8a, 0xd3, 0x40, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x94, 0xdf, 0x8a, 0xd3, 0x40,
0x14, 0xc6, 0x3b, 0xa9, 0x8d, 0xf6, 0xc4, 0x15, 0x19, 0x96, 0x25, 0x44, 0x4c, 0x4b, 0x10, 0xb6, 0x14, 0xc6, 0x3b, 0xe9, 0x36, 0x6e, 0x4f, 0x5d, 0x91, 0x61, 0x59, 0x4a, 0xc4, 0xb4, 0x04, 0xc1,
0xf8, 0x67, 0xe2, 0xd6, 0x9b, 0x75, 0x15, 0xc1, 0x6e, 0x17, 0x14, 0x82, 0x48, 0xf0, 0x62, 0xf1, 0xe2, 0x9f, 0x89, 0x5b, 0x6f, 0xd6, 0x5d, 0x11, 0xec, 0x76, 0x41, 0x21, 0x88, 0x04, 0x2f, 0x16,
0x66, 0x49, 0xdb, 0xb3, 0x31, 0x98, 0x36, 0x31, 0x33, 0x2d, 0xf4, 0xce, 0x47, 0xf0, 0x11, 0x7c, 0x6f, 0x96, 0x34, 0x3d, 0x1b, 0x83, 0x69, 0x12, 0x33, 0xd3, 0x42, 0xef, 0x7c, 0x04, 0x1f, 0xc1,
0x08, 0x1f, 0xa2, 0x97, 0x5e, 0x7a, 0xb5, 0xba, 0xb9, 0xf3, 0x2d, 0x24, 0x33, 0x09, 0xbb, 0x5b, 0x87, 0xf0, 0x21, 0x7a, 0xe9, 0xa5, 0x57, 0xab, 0x9b, 0x3b, 0xdf, 0x42, 0x32, 0x93, 0xb0, 0x7f,
0xd1, 0x56, 0xd9, 0xbb, 0x33, 0x99, 0xef, 0x3b, 0xe7, 0x77, 0x3e, 0x98, 0x40, 0x3f, 0x8c, 0xc4, 0x44, 0x5b, 0x65, 0xef, 0xce, 0xcc, 0x7c, 0xdf, 0x99, 0xdf, 0xf9, 0xc2, 0x04, 0x86, 0x41, 0x28,
0xbb, 0xe9, 0x80, 0x0d, 0x93, 0xb1, 0x3b, 0x4c, 0x26, 0x22, 0x88, 0x26, 0x98, 0x8d, 0xce, 0x97, 0xde, 0x4d, 0x47, 0xcc, 0x4f, 0x26, 0xb6, 0x9f, 0xc4, 0xc2, 0x0b, 0x63, 0xcc, 0xc6, 0xe7, 0x4b,
0x41, 0x1a, 0xb9, 0x1c, 0xb3, 0x59, 0x34, 0x44, 0xee, 0xc6, 0x18, 0x70, 0xe4, 0xee, 0x6c, 0xa7, 0x2f, 0x0d, 0x6d, 0x8e, 0xd9, 0x2c, 0xf4, 0x91, 0xdb, 0x11, 0x7a, 0x1c, 0xb9, 0x3d, 0xdb, 0x2e,
0xac, 0x58, 0x9a, 0x25, 0x22, 0xa1, 0xb7, 0xcf, 0xf4, 0xac, 0xd2, 0xb2, 0x52, 0x31, 0xdb, 0xb1, 0x2b, 0x96, 0x66, 0x89, 0x48, 0xe8, 0xed, 0x33, 0x3d, 0xab, 0xb4, 0xac, 0x54, 0xcc, 0xb6, 0x8d,
0x36, 0xc3, 0x24, 0x4c, 0xa4, 0xd2, 0x2d, 0x2a, 0x65, 0xb2, 0x6e, 0x85, 0x49, 0x12, 0xc6, 0xe8, 0xcd, 0x20, 0x09, 0x12, 0xa9, 0xb4, 0x8b, 0x4a, 0x99, 0x8c, 0x5b, 0x41, 0x92, 0x04, 0x11, 0xda,
0xca, 0xd3, 0x60, 0x7a, 0xec, 0xe2, 0x38, 0x15, 0xf3, 0xf2, 0xb2, 0xb5, 0x7c, 0x29, 0xa2, 0x31, 0x72, 0x35, 0x9a, 0x1e, 0xdb, 0x38, 0x49, 0xc5, 0xbc, 0x3c, 0xec, 0x5c, 0x3e, 0x14, 0xe1, 0x04,
0x72, 0x11, 0x8c, 0x53, 0x25, 0x70, 0x7e, 0x12, 0x68, 0x78, 0xc5, 0x04, 0xba, 0x05, 0x5a, 0x34, 0xb9, 0xf0, 0x26, 0xa9, 0x12, 0x58, 0x3f, 0x09, 0x34, 0x9c, 0xe2, 0x06, 0xba, 0x05, 0x5a, 0x38,
0x32, 0x49, 0x9b, 0x74, 0x9a, 0x3d, 0x3d, 0x3f, 0x69, 0x69, 0x2f, 0xfb, 0xbe, 0x16, 0x8d, 0xe8, 0x6e, 0x93, 0x2e, 0xe9, 0x35, 0x07, 0x7a, 0x7e, 0xd2, 0xd1, 0x5e, 0x0e, 0x5d, 0x2d, 0x1c, 0xd3,
0x3e, 0xc0, 0x30, 0xc3, 0x40, 0xe0, 0xe8, 0x28, 0x10, 0xa6, 0xd6, 0x26, 0x1d, 0xa3, 0x6b, 0x31, 0x7d, 0x00, 0x3f, 0x43, 0x4f, 0xe0, 0xf8, 0xc8, 0x13, 0x6d, 0xad, 0x4b, 0x7a, 0xad, 0xbe, 0xc1,
0xd5, 0x97, 0x55, 0x7d, 0xd9, 0x9b, 0xaa, 0x6f, 0xef, 0xda, 0xe2, 0xa4, 0x55, 0xfb, 0xf4, 0xbd, 0x54, 0x5f, 0x56, 0xf5, 0x65, 0x6f, 0xaa, 0xbe, 0x83, 0xf5, 0xc5, 0x49, 0xa7, 0xf6, 0xe9, 0x7b,
0x45, 0xfc, 0x66, 0xe9, 0x7b, 0x2e, 0xe8, 0x0b, 0xd0, 0xe3, 0x60, 0x80, 0x31, 0x37, 0xeb, 0xed, 0x87, 0xb8, 0xcd, 0xd2, 0xf7, 0x5c, 0xd0, 0x17, 0xa0, 0x47, 0xde, 0x08, 0x23, 0xde, 0xae, 0x77,
0x7a, 0xc7, 0xe8, 0x3e, 0x64, 0x7f, 0x5d, 0x95, 0x49, 0x24, 0xe6, 0x49, 0xcb, 0xc1, 0x44, 0x64, 0xeb, 0xbd, 0x56, 0xff, 0x11, 0xfb, 0xeb, 0xa8, 0x4c, 0x22, 0x31, 0x47, 0x5a, 0x0e, 0x62, 0x91,
0x73, 0xbf, 0xf4, 0x5b, 0x8f, 0xc1, 0x38, 0xf7, 0x99, 0xde, 0x84, 0xfa, 0x7b, 0x9c, 0x2b, 0x6c, 0xcd, 0xdd, 0xd2, 0x6f, 0x3c, 0x81, 0xd6, 0xb9, 0x6d, 0x7a, 0x13, 0xea, 0xef, 0x71, 0xae, 0xb0,
0xbf, 0x28, 0xe9, 0x26, 0x34, 0x66, 0x41, 0x3c, 0x45, 0x89, 0xda, 0xf4, 0xd5, 0x61, 0x4f, 0xdb, 0xdd, 0xa2, 0xa4, 0x9b, 0xd0, 0x98, 0x79, 0xd1, 0x14, 0x25, 0x6a, 0xd3, 0x55, 0x8b, 0x5d, 0x6d,
0x25, 0xce, 0x17, 0x02, 0x1b, 0xfb, 0x12, 0xc9, 0xc7, 0x0f, 0x53, 0xe4, 0xe2, 0x8f, 0x3b, 0xbf, 0x87, 0x58, 0x5f, 0x08, 0x6c, 0xec, 0x4b, 0x24, 0x17, 0x3f, 0x4c, 0x91, 0x8b, 0x3f, 0xce, 0xfc,
0x5e, 0xc2, 0xdd, 0x5d, 0x81, 0x7b, 0xa1, 0xeb, 0x65, 0x63, 0x7b, 0x70, 0xa3, 0xea, 0xcf, 0xd3, 0xfa, 0x12, 0xee, 0xce, 0x12, 0xdc, 0x0b, 0x5d, 0xaf, 0x1a, 0xdb, 0x81, 0x1b, 0x55, 0x7f, 0x9e,
0x64, 0xc2, 0x91, 0xee, 0x41, 0x43, 0xce, 0x96, 0x7e, 0xa3, 0x7b, 0x67, 0x9d, 0x30, 0x7d, 0x65, 0x26, 0x31, 0x47, 0xba, 0x0b, 0x0d, 0x79, 0xb7, 0xf4, 0xb7, 0xfa, 0x77, 0x56, 0x09, 0xd3, 0x55,
0x71, 0xb6, 0x61, 0xa3, 0x8f, 0x31, 0xae, 0xcc, 0xc0, 0xd9, 0x06, 0xc3, 0x8b, 0xb8, 0xa8, 0x64, 0x16, 0x6b, 0x0f, 0x36, 0x86, 0x18, 0xe1, 0xf2, 0x0c, 0x28, 0xac, 0xf1, 0x79, 0xec, 0x4b, 0x9e,
0x26, 0x5c, 0x3d, 0x8e, 0x62, 0x81, 0x19, 0x37, 0x49, 0xbb, 0xde, 0x69, 0xfa, 0xd5, 0xd1, 0xf1, 0x75, 0x57, 0xd6, 0xd6, 0x5d, 0x68, 0x39, 0x21, 0x17, 0x95, 0xb5, 0x0d, 0xd7, 0x8e, 0xc3, 0x48,
0xe0, 0xba, 0x12, 0x96, 0x74, 0x4f, 0x41, 0x57, 0xb3, 0xa5, 0x70, 0x5d, 0xbc, 0xd2, 0xd3, 0xfd, 0x60, 0xc6, 0xdb, 0xa4, 0x5b, 0xef, 0x35, 0xdd, 0x6a, 0x69, 0x39, 0x70, 0x5d, 0x09, 0x4b, 0xe2,
0xac, 0x81, 0x2e, 0xbf, 0x70, 0x8a, 0xa0, 0xab, 0xc5, 0xe9, 0xfd, 0x7f, 0xc9, 0xdf, 0x7a, 0xb0, 0xa7, 0xa0, 0x2b, 0x1e, 0x29, 0x5c, 0x15, 0xb9, 0xf4, 0xf4, 0x3f, 0x6b, 0xa0, 0xcb, 0x1d, 0x4e,
0xa6, 0xba, 0xe4, 0x7d, 0x05, 0xba, 0x4a, 0x64, 0xe5, 0x98, 0x0b, 0xc1, 0x59, 0x5b, 0xbf, 0x3d, 0x11, 0x74, 0x15, 0x06, 0x7d, 0xf0, 0x2f, 0xdf, 0xc4, 0x78, 0xb8, 0xa2, 0xba, 0xe4, 0x7d, 0x05,
0x82, 0x83, 0xe2, 0xe5, 0xd1, 0x23, 0xb8, 0x52, 0xe4, 0x41, 0xef, 0xae, 0xda, 0xfb, 0x2c, 0x5d, 0xba, 0x4a, 0x69, 0xe9, 0x35, 0x17, 0xc2, 0x34, 0xb6, 0x7e, 0x7b, 0x18, 0x07, 0xc5, 0x6b, 0xa4,
0xeb, 0xde, 0x5a, 0x5a, 0x05, 0xdc, 0x3b, 0x5c, 0x9c, 0xda, 0xb5, 0x6f, 0xa7, 0x76, 0xed, 0x63, 0x47, 0xb0, 0x56, 0xe4, 0x41, 0xef, 0x2d, 0x9b, 0xfb, 0x2c, 0x5d, 0xe3, 0xfe, 0x4a, 0x5a, 0x05,
0x6e, 0x93, 0x45, 0x6e, 0x93, 0xaf, 0xb9, 0x4d, 0x7e, 0xe4, 0x36, 0x79, 0xfb, 0xec, 0x3f, 0x7f, 0x3c, 0x38, 0x5c, 0x9c, 0x9a, 0xb5, 0x6f, 0xa7, 0x66, 0xed, 0x63, 0x6e, 0x92, 0x45, 0x6e, 0x92,
0x43, 0x4f, 0x54, 0x75, 0x58, 0x1b, 0xe8, 0x72, 0x99, 0x47, 0xbf, 0x02, 0x00, 0x00, 0xff, 0xff, 0xaf, 0xb9, 0x49, 0x7e, 0xe4, 0x26, 0x79, 0xfb, 0xec, 0x3f, 0x7f, 0x4d, 0x7b, 0xaa, 0x3a, 0xac,
0xad, 0x77, 0xda, 0x73, 0xd1, 0x04, 0x00, 0x00, 0x8d, 0x74, 0x39, 0xcc, 0xe3, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x14, 0x74, 0xdd, 0x12, 0xe5,
0x04, 0x00, 0x00,
} }

View File

@ -47,6 +47,12 @@ message CreateResponse {
message DeleteRequest { message DeleteRequest {
string id = 1; string id = 1;
// Sync indicates that the delete and cleanup should be done
// synchronously before returning to the caller
//
// Default is false
bool sync = 2;
} }
message ListRequest { message ListRequest {

View File

@ -43,8 +43,16 @@ var (
} }
) )
// DecompressReadCloser include the stream after decompress and the compress method detected.
type DecompressReadCloser interface {
io.ReadCloser
// GetCompression returns the compress method which is used before decompressing
GetCompression() Compression
}
type readCloserWrapper struct { type readCloserWrapper struct {
io.Reader io.Reader
compression Compression
closer func() error closer func() error
} }
@ -55,6 +63,10 @@ func (r *readCloserWrapper) Close() error {
return nil return nil
} }
func (r *readCloserWrapper) GetCompression() Compression {
return r.compression
}
type writeCloserWrapper struct { type writeCloserWrapper struct {
io.Writer io.Writer
closer func() error closer func() error
@ -84,7 +96,7 @@ func DetectCompression(source []byte) Compression {
} }
// DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive. // DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive.
func DecompressStream(archive io.Reader) (io.ReadCloser, error) { func DecompressStream(archive io.Reader) (DecompressReadCloser, error) {
buf := bufioReader32KPool.Get().(*bufio.Reader) buf := bufioReader32KPool.Get().(*bufio.Reader)
buf.Reset(archive) buf.Reset(archive)
bs, err := buf.Peek(10) bs, err := buf.Peek(10)
@ -105,14 +117,14 @@ func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
} }
switch compression := DetectCompression(bs); compression { switch compression := DetectCompression(bs); compression {
case Uncompressed: case Uncompressed:
readBufWrapper := &readCloserWrapper{buf, closer} readBufWrapper := &readCloserWrapper{buf, compression, closer}
return readBufWrapper, nil return readBufWrapper, nil
case Gzip: case Gzip:
gzReader, err := gzip.NewReader(buf) gzReader, err := gzip.NewReader(buf)
if err != nil { if err != nil {
return nil, err return nil, err
} }
readBufWrapper := &readCloserWrapper{gzReader, closer} readBufWrapper := &readCloserWrapper{gzReader, compression, 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())

View File

@ -114,6 +114,9 @@ func Apply(ctx context.Context, root string, r io.Reader, opts ...ApplyOpt) (int
return 0, errors.Wrap(err, "failed to apply option") return 0, errors.Wrap(err, "failed to apply option")
} }
} }
if options.Filter == nil {
options.Filter = all
}
return apply(ctx, root, tar.NewReader(r), options) return apply(ctx, root, tar.NewReader(r), options)
} }
@ -155,6 +158,14 @@ func applyNaive(ctx context.Context, root string, tr *tar.Reader, options ApplyO
// Normalize name, for safety and for a simple is-root check // Normalize name, for safety and for a simple is-root check
hdr.Name = filepath.Clean(hdr.Name) hdr.Name = filepath.Clean(hdr.Name)
accept, err := options.Filter(hdr)
if err != nil {
return 0, err
}
if !accept {
continue
}
if skipFile(hdr) { if skipFile(hdr) {
log.G(ctx).Warnf("file %q ignored: archive may not be supported on system", hdr.Name) log.G(ctx).Warnf("file %q ignored: archive may not be supported on system", hdr.Name)
continue continue
@ -366,10 +377,11 @@ func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header
} }
case tar.TypeLink: case tar.TypeLink:
targetPath, err := fs.RootPath(extractDir, hdr.Linkname) targetPath, err := hardlinkRootPath(extractDir, hdr.Linkname)
if err != nil { if err != nil {
return err return err
} }
if err := os.Link(targetPath, path); err != nil { if err := os.Link(targetPath, path); err != nil {
return err return err
} }
@ -648,3 +660,27 @@ func copyBuffered(ctx context.Context, dst io.Writer, src io.Reader) (written in
return written, err return written, err
} }
// hardlinkRootPath returns target linkname, evaluating and bounding any
// symlink to the parent directory.
//
// NOTE: Allow hardlink to the softlink, not the real one. For example,
//
// touch /tmp/zzz
// ln -s /tmp/zzz /tmp/xxx
// ln /tmp/xxx /tmp/yyy
//
// /tmp/yyy should be softlink which be same of /tmp/xxx, not /tmp/zzz.
func hardlinkRootPath(root, linkname string) (string, error) {
ppath, base := filepath.Split(linkname)
ppath, err := fs.RootPath(root, ppath)
if err != nil {
return "", err
}
targetPath := filepath.Join(ppath, base)
if !strings.HasPrefix(targetPath, root) {
targetPath = root
}
return targetPath, nil
}

View File

@ -16,5 +16,23 @@
package archive package archive
import "archive/tar"
// ApplyOpt allows setting mutable archive apply properties on creation // ApplyOpt allows setting mutable archive apply properties on creation
type ApplyOpt func(options *ApplyOptions) error type ApplyOpt func(options *ApplyOptions) error
// Filter specific files from the archive
type Filter func(*tar.Header) (bool, error)
// all allows all files
func all(_ *tar.Header) (bool, error) {
return true, nil
}
// WithFilter uses the filter to select which files are to be extracted.
func WithFilter(f Filter) ApplyOpt {
return func(options *ApplyOptions) error {
options.Filter = f
return nil
}
}

View File

@ -20,4 +20,5 @@ package archive
// ApplyOptions provides additional options for an Apply operation // ApplyOptions provides additional options for an Apply operation
type ApplyOptions struct { type ApplyOptions struct {
Filter Filter // Filter tar headers
} }

View File

@ -22,6 +22,7 @@ package archive
type ApplyOptions struct { type ApplyOptions struct {
ParentLayerPaths []string // Parent layer paths used for Windows layer apply ParentLayerPaths []string // Parent layer paths used for Windows layer apply
IsWindowsContainerLayer bool // True if the tar stream to be applied is a Windows Container Layer IsWindowsContainerLayer bool // True if the tar stream to be applied is a Windows Container Layer
Filter Filter // Filter tar headers
} }
// WithParentLayers adds parent layers to the apply process this is required // WithParentLayers adds parent layers to the apply process this is required

View File

@ -20,7 +20,6 @@ import (
"fmt" "fmt"
"io" "io"
"net" "net"
"sync"
winio "github.com/Microsoft/go-winio" winio "github.com/Microsoft/go-winio"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
@ -41,7 +40,6 @@ func NewFIFOSetInDir(_, id string, terminal bool) (*FIFOSet, error) {
func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) { func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) {
var ( var (
wg sync.WaitGroup
set []io.Closer set []io.Closer
) )
@ -85,9 +83,7 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) {
}(l) }(l)
set = append(set, l) set = append(set, l)
wg.Add(1)
go func() { go func() {
defer wg.Done()
c, err := l.Accept() c, err := l.Accept()
if err != nil { if err != nil {
log.L.WithError(err).Errorf("failed to accept stdout connection on %s", fifos.Stdout) log.L.WithError(err).Errorf("failed to accept stdout connection on %s", fifos.Stdout)
@ -115,9 +111,7 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) {
}(l) }(l)
set = append(set, l) set = append(set, l)
wg.Add(1)
go func() { go func() {
defer wg.Done()
c, err := l.Accept() c, err := l.Accept()
if err != nil { if err != nil {
log.L.WithError(err).Errorf("failed to accept stderr connection on %s", fifos.Stderr) log.L.WithError(err).Errorf("failed to accept stderr connection on %s", fifos.Stderr)

View File

@ -43,8 +43,11 @@ import (
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/leases"
leasesproxy "github.com/containerd/containerd/leases/proxy"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/pkg/dialer" "github.com/containerd/containerd/pkg/dialer"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker" "github.com/containerd/containerd/remotes/docker"
@ -79,8 +82,12 @@ func New(address string, opts ...ClientOpt) (*Client, error) {
return nil, err return nil, err
} }
} }
rt := fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS)
if copts.defaultRuntime != "" {
rt = copts.defaultRuntime
}
c := &Client{ c := &Client{
runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS), runtime: rt,
} }
if copts.services != nil { if copts.services != nil {
c.services = *copts.services c.services = *copts.services
@ -284,7 +291,31 @@ func defaultRemoteContext() *RemoteContext {
} }
} }
// Fetch downloads the provided content into containerd's content store
// and returns a non-platform specific image reference
func (c *Client) Fetch(ctx context.Context, ref string, opts ...RemoteOpt) (images.Image, error) {
fetchCtx := defaultRemoteContext()
for _, o := range opts {
if err := o(c, fetchCtx); err != nil {
return images.Image{}, err
}
}
if fetchCtx.Unpack {
return images.Image{}, errors.New("unpack on fetch not supported, try pull")
}
ctx, done, err := c.WithLease(ctx)
if err != nil {
return images.Image{}, err
}
defer done(ctx)
return c.fetch(ctx, fetchCtx, ref)
}
// Pull downloads the provided content into containerd's content store // Pull downloads the provided content into containerd's content store
// and returns a platform specific image object
func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image, error) { func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image, error) {
pullCtx := defaultRemoteContext() pullCtx := defaultRemoteContext()
for _, o := range opts { for _, o := range opts {
@ -292,7 +323,12 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image
return nil, err return nil, err
} }
} }
store := c.ContentStore()
if len(pullCtx.Platforms) > 1 {
return nil, errors.New("cannot pull multiplatform image locally, try Fetch")
} else if len(pullCtx.Platforms) == 0 {
pullCtx.Platforms = []string{platforms.Default()}
}
ctx, done, err := c.WithLease(ctx) ctx, done, err := c.WithLease(ctx)
if err != nil { if err != nil {
@ -300,82 +336,92 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image
} }
defer done(ctx) defer done(ctx)
name, desc, err := pullCtx.Resolver.Resolve(ctx, ref) img, err := c.fetch(ctx, pullCtx, ref)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to resolve reference %q", ref) return nil, err
} }
fetcher, err := pullCtx.Resolver.Fetcher(ctx, name) i := NewImageWithPlatform(c, img, pullCtx.Platforms[0])
if pullCtx.Unpack {
if err := i.Unpack(ctx, pullCtx.Snapshotter); err != nil {
return nil, errors.Wrapf(err, "failed to unpack image on snapshotter %s", pullCtx.Snapshotter)
}
}
return i, nil
}
func (c *Client) fetch(ctx context.Context, rCtx *RemoteContext, ref string) (images.Image, error) {
store := c.ContentStore()
name, desc, err := rCtx.Resolver.Resolve(ctx, ref)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to get fetcher for %q", name) return images.Image{}, errors.Wrapf(err, "failed to resolve reference %q", ref)
}
fetcher, err := rCtx.Resolver.Fetcher(ctx, name)
if err != nil {
return images.Image{}, errors.Wrapf(err, "failed to get fetcher for %q", name)
} }
var ( var (
schema1Converter *schema1.Converter schema1Converter *schema1.Converter
handler images.Handler handler images.Handler
) )
if desc.MediaType == images.MediaTypeDockerSchema1Manifest && pullCtx.ConvertSchema1 { if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 {
schema1Converter = schema1.NewConverter(store, fetcher) schema1Converter = schema1.NewConverter(store, fetcher)
handler = images.Handlers(append(pullCtx.BaseHandlers, schema1Converter)...) handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...)
} else { } else {
// Get all the children for a descriptor // Get all the children for a descriptor
childrenHandler := images.ChildrenHandler(store) childrenHandler := images.ChildrenHandler(store)
// Set any children labels for that content // Set any children labels for that content
childrenHandler = images.SetChildrenLabels(store, childrenHandler) childrenHandler = images.SetChildrenLabels(store, childrenHandler)
// Filter children by platforms // Filter children by platforms
childrenHandler = images.FilterPlatforms(childrenHandler, pullCtx.Platforms...) childrenHandler = images.FilterPlatforms(childrenHandler, rCtx.Platforms...)
handler = images.Handlers(append(pullCtx.BaseHandlers, handler = images.Handlers(append(rCtx.BaseHandlers,
remotes.FetchHandler(store, fetcher), remotes.FetchHandler(store, fetcher),
childrenHandler, childrenHandler,
)...) )...)
} }
if err := images.Dispatch(ctx, handler, desc); err != nil { if err := images.Dispatch(ctx, handler, desc); err != nil {
return nil, err return images.Image{}, err
} }
if schema1Converter != nil { if schema1Converter != nil {
desc, err = schema1Converter.Convert(ctx) desc, err = schema1Converter.Convert(ctx)
if err != nil { if err != nil {
return nil, err return images.Image{}, err
} }
} }
img := &image{ img := images.Image{
client: c,
i: images.Image{
Name: name, Name: name,
Target: desc, Target: desc,
Labels: pullCtx.Labels, Labels: rCtx.Labels,
},
}
if pullCtx.Unpack {
if err := img.Unpack(ctx, pullCtx.Snapshotter); err != nil {
return nil, errors.Wrapf(err, "failed to unpack image on snapshotter %s", pullCtx.Snapshotter)
}
} }
is := c.ImageService() is := c.ImageService()
for { for {
if created, err := is.Create(ctx, img.i); err != nil { if created, err := is.Create(ctx, img); err != nil {
if !errdefs.IsAlreadyExists(err) { if !errdefs.IsAlreadyExists(err) {
return nil, err return images.Image{}, err
} }
updated, err := is.Update(ctx, img.i) updated, err := is.Update(ctx, img)
if err != nil { if err != nil {
// if image was removed, try create again // if image was removed, try create again
if errdefs.IsNotFound(err) { if errdefs.IsNotFound(err) {
continue continue
} }
return nil, err return images.Image{}, err
} }
img.i = updated img = updated
} else { } else {
img.i = created img = created
} }
return img, nil return img, nil
} }
} }
@ -403,10 +449,7 @@ func (c *Client) GetImage(ctx context.Context, ref string) (Image, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &image{ return NewImage(c, i), nil
client: c,
i: i,
}, nil
} }
// ListImages returns all existing images // ListImages returns all existing images
@ -417,10 +460,7 @@ func (c *Client) ListImages(ctx context.Context, filters ...string) ([]Image, er
} }
images := make([]Image, len(imgs)) images := make([]Image, len(imgs))
for i, img := range imgs { for i, img := range imgs {
images[i] = &image{ images[i] = NewImage(c, img)
client: c,
i: img,
}
} }
return images, nil return images, nil
} }
@ -451,6 +491,8 @@ func (c *Client) NamespaceService() namespaces.Store {
if c.namespaceStore != nil { if c.namespaceStore != nil {
return c.namespaceStore return c.namespaceStore
} }
c.connMu.Lock()
defer c.connMu.Unlock()
return NewNamespaceStoreFromClient(namespacesapi.NewNamespacesClient(c.conn)) return NewNamespaceStoreFromClient(namespacesapi.NewNamespacesClient(c.conn))
} }
@ -459,6 +501,8 @@ func (c *Client) ContainerService() containers.Store {
if c.containerStore != nil { if c.containerStore != nil {
return c.containerStore return c.containerStore
} }
c.connMu.Lock()
defer c.connMu.Unlock()
return NewRemoteContainerStore(containersapi.NewContainersClient(c.conn)) return NewRemoteContainerStore(containersapi.NewContainersClient(c.conn))
} }
@ -467,6 +511,8 @@ func (c *Client) ContentStore() content.Store {
if c.contentStore != nil { if c.contentStore != nil {
return c.contentStore return c.contentStore
} }
c.connMu.Lock()
defer c.connMu.Unlock()
return contentproxy.NewContentStore(contentapi.NewContentClient(c.conn)) return contentproxy.NewContentStore(contentapi.NewContentClient(c.conn))
} }
@ -475,6 +521,8 @@ func (c *Client) SnapshotService(snapshotterName string) snapshots.Snapshotter {
if c.snapshotters != nil { if c.snapshotters != nil {
return c.snapshotters[snapshotterName] return c.snapshotters[snapshotterName]
} }
c.connMu.Lock()
defer c.connMu.Unlock()
return snproxy.NewSnapshotter(snapshotsapi.NewSnapshotsClient(c.conn), snapshotterName) return snproxy.NewSnapshotter(snapshotsapi.NewSnapshotsClient(c.conn), snapshotterName)
} }
@ -483,6 +531,8 @@ func (c *Client) TaskService() tasks.TasksClient {
if c.taskService != nil { if c.taskService != nil {
return c.taskService return c.taskService
} }
c.connMu.Lock()
defer c.connMu.Unlock()
return tasks.NewTasksClient(c.conn) return tasks.NewTasksClient(c.conn)
} }
@ -491,6 +541,8 @@ func (c *Client) ImageService() images.Store {
if c.imageStore != nil { if c.imageStore != nil {
return c.imageStore return c.imageStore
} }
c.connMu.Lock()
defer c.connMu.Unlock()
return NewImageStoreFromClient(imagesapi.NewImagesClient(c.conn)) return NewImageStoreFromClient(imagesapi.NewImagesClient(c.conn))
} }
@ -499,24 +551,32 @@ func (c *Client) DiffService() DiffService {
if c.diffService != nil { if c.diffService != nil {
return c.diffService return c.diffService
} }
c.connMu.Lock()
defer c.connMu.Unlock()
return NewDiffServiceFromClient(diffapi.NewDiffClient(c.conn)) return NewDiffServiceFromClient(diffapi.NewDiffClient(c.conn))
} }
// IntrospectionService returns the underlying Introspection Client // IntrospectionService returns the underlying Introspection Client
func (c *Client) IntrospectionService() introspectionapi.IntrospectionClient { func (c *Client) IntrospectionService() introspectionapi.IntrospectionClient {
c.connMu.Lock()
defer c.connMu.Unlock()
return introspectionapi.NewIntrospectionClient(c.conn) return introspectionapi.NewIntrospectionClient(c.conn)
} }
// LeasesService returns the underlying Leases Client // LeasesService returns the underlying Leases Client
func (c *Client) LeasesService() leasesapi.LeasesClient { func (c *Client) LeasesService() leases.Manager {
if c.leasesService != nil { if c.leasesService != nil {
return c.leasesService return c.leasesService
} }
return leasesapi.NewLeasesClient(c.conn) c.connMu.Lock()
defer c.connMu.Unlock()
return leasesproxy.NewLeaseManager(leasesapi.NewLeasesClient(c.conn))
} }
// HealthService returns the underlying GRPC HealthClient // HealthService returns the underlying GRPC HealthClient
func (c *Client) HealthService() grpc_health_v1.HealthClient { func (c *Client) HealthService() grpc_health_v1.HealthClient {
c.connMu.Lock()
defer c.connMu.Unlock()
return grpc_health_v1.NewHealthClient(c.conn) return grpc_health_v1.NewHealthClient(c.conn)
} }
@ -525,11 +585,15 @@ func (c *Client) EventService() EventService {
if c.eventService != nil { if c.eventService != nil {
return c.eventService return c.eventService
} }
c.connMu.Lock()
defer c.connMu.Unlock()
return NewEventServiceFromClient(eventsapi.NewEventsClient(c.conn)) return NewEventServiceFromClient(eventsapi.NewEventsClient(c.conn))
} }
// VersionService returns the underlying VersionClient // VersionService returns the underlying VersionClient
func (c *Client) VersionService() versionservice.VersionClient { func (c *Client) VersionService() versionservice.VersionClient {
c.connMu.Lock()
defer c.connMu.Unlock()
return versionservice.NewVersionClient(c.conn) return versionservice.NewVersionClient(c.conn)
} }

View File

@ -18,12 +18,14 @@ package containerd
import ( import (
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
type clientOpts struct { type clientOpts struct {
defaultns string defaultns string
defaultRuntime string
services *services services *services
dialOptions []grpc.DialOption dialOptions []grpc.DialOption
} }
@ -42,6 +44,14 @@ func WithDefaultNamespace(ns string) ClientOpt {
} }
} }
// WithDefaultRuntime sets the default runtime on the client
func WithDefaultRuntime(rt string) ClientOpt {
return func(c *clientOpts) error {
c.defaultRuntime = rt
return nil
}
}
// WithDialOpts allows grpc.DialOptions to be set on the connection // WithDialOpts allows grpc.DialOptions to be set on the connection
func WithDialOpts(opts []grpc.DialOption) ClientOpt { func WithDialOpts(opts []grpc.DialOption) ClientOpt {
return func(c *clientOpts) error { return func(c *clientOpts) error {
@ -67,6 +77,9 @@ type RemoteOpt func(*Client, *RemoteContext) error
// WithPlatform allows the caller to specify a platform to retrieve // WithPlatform allows the caller to specify a platform to retrieve
// content for // content for
func WithPlatform(platform string) RemoteOpt { func WithPlatform(platform string) RemoteOpt {
if platform == "" {
platform = platforms.Default()
}
return func(_ *Client, c *RemoteContext) error { return func(_ *Client, c *RemoteContext) error {
for _, p := range c.Platforms { for _, p := range c.Platforms {
if p == platform { if p == platform {

View File

@ -17,6 +17,7 @@
package command package command
import ( import (
gocontext "context"
"io" "io"
"os" "os"
@ -48,7 +49,7 @@ var configCommand = cli.Command{
config := &Config{ config := &Config{
Config: defaultConfig(), Config: defaultConfig(),
} }
plugins, err := server.LoadPlugins(config.Config) plugins, err := server.LoadPlugins(gocontext.Background(), config.Config)
if err != nil { if err != nil {
return err return err
} }

View File

@ -48,6 +48,11 @@ high performance container runtime
` `
func init() { func init() {
logrus.SetFormatter(&logrus.TextFormatter{
TimestampFormat: log.RFC3339NanoFixed,
FullTimestamp: true,
})
// Discard grpc logs so that they don't mess with our stdio // Discard grpc logs so that they don't mess with our stdio
grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard)) grpclog.SetLoggerV2(grpclog.NewLoggerV2(ioutil.Discard, ioutil.Discard, ioutil.Discard))
@ -116,8 +121,12 @@ func App() *cli.App {
return errors.Wrap(err, "creating temp mount location") return errors.Wrap(err, "creating temp mount location")
} }
// unmount all temp mounts on boot for the server // unmount all temp mounts on boot for the server
if err := mount.CleanupTempMounts(0); err != nil { warnings, err := mount.CleanupTempMounts(0)
return errors.Wrap(err, "unmounting temp mounts") if err != nil {
log.G(ctx).WithError(err).Error("unmounting temp mounts")
}
for _, w := range warnings {
log.G(ctx).WithError(w).Warn("cleanup temp mount")
} }
address := config.GRPC.Address address := config.GRPC.Address
if address == "" { if address == "" {

View File

@ -37,8 +37,12 @@ var ociHook = cli.Command{
if err != nil { if err != nil {
return err return err
} }
spec, err := loadSpec(state.Bundle)
if err != nil {
return err
}
var ( var (
ctx = newTemplateContext(state) ctx = newTemplateContext(state, spec)
args = []string(context.Args()) args = []string(context.Args())
env = os.Environ() env = os.Environ()
) )
@ -52,6 +56,25 @@ var ociHook = cli.Command{
}, },
} }
type hookSpec struct {
Root struct {
Path string `json:"path"`
} `json:"root"`
}
func loadSpec(bundle string) (*hookSpec, error) {
f, err := os.Open(filepath.Join(bundle, "config.json"))
if err != nil {
return nil, err
}
defer f.Close()
var s hookSpec
if err := json.NewDecoder(f).Decode(&s); err != nil {
return nil, err
}
return &s, nil
}
func loadHookState(r io.Reader) (*specs.State, error) { func loadHookState(r io.Reader) (*specs.State, error) {
var s specs.State var s specs.State
if err := json.NewDecoder(r).Decode(&s); err != nil { if err := json.NewDecoder(r).Decode(&s); err != nil {
@ -60,9 +83,10 @@ func loadHookState(r io.Reader) (*specs.State, error) {
return &s, nil return &s, nil
} }
func newTemplateContext(state *specs.State) *templateContext { func newTemplateContext(state *specs.State, spec *hookSpec) *templateContext {
t := &templateContext{ t := &templateContext{
state: state, state: state,
root: spec.Root.Path,
} }
t.funcs = template.FuncMap{ t.funcs = template.FuncMap{
"id": t.id, "id": t.id,
@ -77,6 +101,7 @@ func newTemplateContext(state *specs.State) *templateContext {
type templateContext struct { type templateContext struct {
state *specs.State state *specs.State
root string
funcs template.FuncMap funcs template.FuncMap
} }
@ -89,7 +114,10 @@ func (t *templateContext) bundle() string {
} }
func (t *templateContext) rootfs() string { func (t *templateContext) rootfs() string {
return filepath.Join(t.state.Bundle, "rootfs") if filepath.IsAbs(t.root) {
return t.root
}
return filepath.Join(t.state.Bundle, t.root)
} }
func (t *templateContext) pid() int { func (t *templateContext) pid() int {

View File

@ -24,6 +24,8 @@ import (
"github.com/containerd/containerd/cmd/ctr/commands/content" "github.com/containerd/containerd/cmd/ctr/commands/content"
"github.com/containerd/containerd/cmd/ctr/commands/events" "github.com/containerd/containerd/cmd/ctr/commands/events"
"github.com/containerd/containerd/cmd/ctr/commands/images" "github.com/containerd/containerd/cmd/ctr/commands/images"
"github.com/containerd/containerd/cmd/ctr/commands/install"
"github.com/containerd/containerd/cmd/ctr/commands/leases"
namespacesCmd "github.com/containerd/containerd/cmd/ctr/commands/namespaces" namespacesCmd "github.com/containerd/containerd/cmd/ctr/commands/namespaces"
"github.com/containerd/containerd/cmd/ctr/commands/plugins" "github.com/containerd/containerd/cmd/ctr/commands/plugins"
"github.com/containerd/containerd/cmd/ctr/commands/pprof" "github.com/containerd/containerd/cmd/ctr/commands/pprof"
@ -96,11 +98,13 @@ containerd CLI
content.Command, content.Command,
events.Command, events.Command,
images.Command, images.Command,
leases.Command,
namespacesCmd.Command, namespacesCmd.Command,
pprof.Command, pprof.Command,
run.Command, run.Command,
snapshots.Command, snapshots.Command,
tasks.Command, tasks.Command,
install.Command,
}, extraCmds...) }, extraCmds...)
app.Before = func(context *cli.Context) error { app.Before = func(context *cli.Context) error {
if context.GlobalBool("debug") { if context.GlobalBool("debug") {

View File

@ -46,8 +46,8 @@ func AppContext(context *cli.Context) (gocontext.Context, gocontext.CancelFunc)
} }
// NewClient returns a new containerd client // NewClient returns a new containerd client
func NewClient(context *cli.Context) (*containerd.Client, gocontext.Context, gocontext.CancelFunc, error) { func NewClient(context *cli.Context, opts ...containerd.ClientOpt) (*containerd.Client, gocontext.Context, gocontext.CancelFunc, error) {
client, err := containerd.New(context.GlobalString("address")) client, err := containerd.New(context.GlobalString("address"), opts...)
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }

View File

@ -105,7 +105,7 @@ var (
}, },
cli.StringFlag{ cli.StringFlag{
Name: "runtime", Name: "runtime",
Usage: "runtime name (io.containerd.runtime.v1.linux, io.containerd.runtime.v1.windows, io.containerd.runtime.v1.com.vmware.linux)", Usage: "runtime name",
Value: fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS), Value: fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS),
}, },
cli.BoolFlag{ cli.BoolFlag{

View File

@ -32,6 +32,7 @@ import (
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/pkg/progress" "github.com/containerd/containerd/pkg/progress"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/remotes" "github.com/containerd/containerd/remotes"
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"
@ -56,7 +57,16 @@ not use this implementation as a guide. The end goal should be having metadata,
content and snapshots ready for a direct use via the 'ctr run'. content and snapshots ready for a direct use via the 'ctr run'.
Most of this is experimental and there are few leaps to make this work.`, Most of this is experimental and there are few leaps to make this work.`,
Flags: append(commands.RegistryFlags, commands.LabelFlag), Flags: append(commands.RegistryFlags, commands.LabelFlag,
cli.StringSliceFlag{
Name: "platform",
Usage: "Pull content from a specific platform",
},
cli.BoolFlag{
Name: "all-platforms",
Usage: "pull content from all platforms",
},
),
Action: func(clicontext *cli.Context) error { Action: func(clicontext *cli.Context) error {
var ( var (
ref = clicontext.Args().First() ref = clicontext.Args().First()
@ -73,10 +83,10 @@ Most of this is experimental and there are few leaps to make this work.`,
} }
// Fetch loads all resources into the content store and returns the image // Fetch loads all resources into the content store and returns the image
func Fetch(ctx context.Context, client *containerd.Client, ref string, cliContext *cli.Context) (containerd.Image, error) { func Fetch(ctx context.Context, client *containerd.Client, ref string, cliContext *cli.Context) (images.Image, error) {
resolver, err := commands.GetResolver(ctx, cliContext) resolver, err := commands.GetResolver(ctx, cliContext)
if err != nil { if err != nil {
return nil, err return images.Image{}, err
} }
ongoing := newJobs(ref) ongoing := newJobs(ref)
@ -109,15 +119,19 @@ func Fetch(ctx context.Context, client *containerd.Client, ref string, cliContex
} }
if !cliContext.Bool("all-platforms") { if !cliContext.Bool("all-platforms") {
for _, platform := range cliContext.StringSlice("platform") { p := cliContext.StringSlice("platform")
if len(p) == 0 {
p = append(p, platforms.Default())
}
for _, platform := range p {
opts = append(opts, containerd.WithPlatform(platform)) opts = append(opts, containerd.WithPlatform(platform))
} }
} }
img, err := client.Pull(pctx, ref, opts...) img, err := client.Fetch(pctx, ref, opts...)
stopProgress() stopProgress()
if err != nil { if err != nil {
return nil, err return images.Image{}, err
} }
<-progress <-progress

View File

@ -19,10 +19,13 @@ package images
import ( import (
"fmt" "fmt"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/cmd/ctr/commands/content" "github.com/containerd/containerd/cmd/ctr/commands/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
"github.com/pkg/errors"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -43,7 +46,7 @@ command. As part of this process, we do the following:
cli.StringSliceFlag{ cli.StringSliceFlag{
Name: "platform", Name: "platform",
Usage: "Pull content from a specific platform", Usage: "Pull content from a specific platform",
Value: &cli.StringSlice{platforms.Default()}, Value: &cli.StringSlice{},
}, },
cli.BoolFlag{ cli.BoolFlag{
Name: "all-platforms", Name: "all-platforms",
@ -78,11 +81,34 @@ command. As part of this process, we do the following:
log.G(ctx).WithField("image", ref).Debug("unpacking") log.G(ctx).WithField("image", ref).Debug("unpacking")
// TODO: Show unpack status // TODO: Show unpack status
fmt.Printf("unpacking %s...\n", img.Target().Digest)
err = img.Unpack(ctx, context.String("snapshotter")) var p []string
if err == nil { if context.Bool("all-platforms") {
fmt.Println("done") all, err := images.Platforms(ctx, client.ContentStore(), img.Target)
if err != nil {
return errors.Wrap(err, "unable to resolve image platforms")
} }
p = make([]string, len(all))
for i := range all {
p[i] = platforms.Format(all[i])
}
} else {
p = context.StringSlice("platform")
}
if len(p) == 0 {
p = append(p, platforms.Default())
}
for _, platform := range p {
fmt.Printf("unpacking %s %s...\n", platform, img.Target.Digest)
i := containerd.NewImageWithPlatform(client, img, platform)
err = i.Unpack(ctx, context.String("snapshotter"))
if err != nil {
return err return err
}
}
fmt.Println("done")
return nil
}, },
} }

View File

@ -0,0 +1,61 @@
/*
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 install
import (
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/urfave/cli"
)
// Command to install binary packages
var Command = cli.Command{
Name: "install",
Usage: "install a new package",
ArgsUsage: "<ref>",
Description: "install a new package",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "libs,l",
Usage: "install libs from the image",
},
cli.BoolFlag{
Name: "replace,r",
Usage: "replace any binaries or libs in the opt directory",
},
},
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
ref := context.Args().First()
image, err := client.GetImage(ctx, ref)
if err != nil {
return err
}
var opts []containerd.InstallOpts
if context.Bool("libs") {
opts = append(opts, containerd.WithInstallLibs)
}
if context.Bool("replace") {
opts = append(opts, containerd.WithInstallReplace)
}
return client.Install(ctx, image, opts...)
},
}

View File

@ -0,0 +1,202 @@
/*
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 leases
import (
"fmt"
"os"
"sort"
"strings"
"text/tabwriter"
"time"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/leases"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
// Command is the cli command for managing content
var Command = cli.Command{
Name: "leases",
Usage: "manage leases",
Subcommands: cli.Commands{
listCommand,
createCommand,
deleteCommand,
},
}
var listCommand = cli.Command{
Name: "list",
Aliases: []string{"ls"},
Usage: "list all active leases",
ArgsUsage: "[flags] <filter>",
Description: "list active leases by containerd",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "quiet, q",
Usage: "print only the blob digest",
},
},
Action: func(context *cli.Context) error {
var (
filters = context.Args()
quiet = context.Bool("quiet")
)
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
ls := client.LeasesService()
leaseList, err := ls.List(ctx, filters...)
if err != nil {
return errors.Wrap(err, "failed to list leases")
}
if quiet {
for _, l := range leaseList {
fmt.Println(l.ID)
}
return nil
}
tw := tabwriter.NewWriter(os.Stdout, 1, 8, 1, ' ', 0)
fmt.Fprintln(tw, "ID\tCREATED AT\tLABELS\t")
for _, l := range leaseList {
labels := "-"
if len(l.Labels) > 0 {
var pairs []string
for k, v := range l.Labels {
pairs = append(pairs, fmt.Sprintf("%v=%v", k, v))
}
sort.Strings(pairs)
labels = strings.Join(pairs, ",")
}
fmt.Fprintf(tw, "%v\t%v\t%s\t\n",
l.ID,
l.CreatedAt.Local().Format(time.RFC3339),
labels)
}
return tw.Flush()
},
}
var createCommand = cli.Command{
Name: "create",
Usage: "create lease",
ArgsUsage: "[flags] <label>=<value> ...",
Description: "create a new lease",
Flags: []cli.Flag{
cli.StringFlag{
Name: "id",
Usage: "set the id for the lease, will be generated by default",
},
cli.DurationFlag{
Name: "expires, x",
Usage: "expiration of lease (0 value will not expire)",
Value: 24 * time.Hour,
},
},
Action: func(context *cli.Context) error {
var labelstr = context.Args()
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
ls := client.LeasesService()
opts := []leases.Opt{}
if len(labelstr) > 0 {
labels := map[string]string{}
for _, lstr := range labelstr {
l := strings.SplitN(lstr, "=", 2)
if len(l) == 1 {
labels[l[0]] = ""
} else {
labels[l[0]] = l[1]
}
}
opts = append(opts, leases.WithLabels(labels))
}
if id := context.String("id"); id != "" {
opts = append(opts, leases.WithID(id))
}
if exp := context.Duration("expires"); exp > 0 {
opts = append(opts, leases.WithExpiration(exp))
}
l, err := ls.Create(ctx, opts...)
if err != nil {
return err
}
fmt.Println(l.ID)
return nil
},
}
var deleteCommand = cli.Command{
Name: "delete",
Aliases: []string{"rm"},
Usage: "delete a lease",
ArgsUsage: "[flags] <lease id> ...",
Description: "delete a lease",
Flags: []cli.Flag{
cli.BoolFlag{
Name: "sync",
Usage: "Synchronously remove leases and all unreferenced resources",
},
},
Action: func(context *cli.Context) error {
var lids = context.Args()
if len(lids) == 0 {
return cli.ShowSubcommandHelp(context)
}
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
ls := client.LeasesService()
sync := context.Bool("sync")
for i, lid := range lids {
var opts []leases.DeleteOpt
if sync && i == len(lids)-1 {
opts = append(opts, leases.SynchronousDelete)
}
lease := leases.Lease{
ID: lid,
}
if err := ls.Delete(ctx, lease, opts...); err != nil {
return err
}
fmt.Println(lid)
}
return nil
},
}

View File

@ -19,9 +19,7 @@ package run
import ( import (
gocontext "context" gocontext "context"
"encoding/csv" "encoding/csv"
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"strings" "strings"
"github.com/containerd/console" "github.com/containerd/console"
@ -37,17 +35,6 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
func loadSpec(path string, s *specs.Spec) error {
raw, err := ioutil.ReadFile(path)
if err != nil {
return errors.New("cannot load spec config file")
}
if err := json.Unmarshal(raw, s); err != nil {
return errors.Errorf("decoding spec config file failed, current supported OCI runtime-spec : v%s", specs.Version)
}
return nil
}
func withMounts(context *cli.Context) oci.SpecOpts { func withMounts(context *cli.Context) oci.SpecOpts {
return func(ctx gocontext.Context, client oci.Client, container *containers.Container, s *specs.Spec) error { return func(ctx gocontext.Context, client oci.Client, container *containers.Container, s *specs.Spec) error {
mounts := make([]specs.Mount, 0) mounts := make([]specs.Mount, 0)

View File

@ -44,7 +44,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
if err != nil { if err != nil {
return nil, err return nil, err
} }
return client.NewContainer(ctx, id, containerd.WithCheckpoint(im, id)) return client.NewContainer(ctx, id, containerd.WithCheckpoint(im, id), containerd.WithRuntime(context.String("runtime"), nil))
} }
var ( var (
@ -52,6 +52,13 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
cOpts []containerd.NewContainerOpts cOpts []containerd.NewContainerOpts
spec containerd.NewContainerOpts spec containerd.NewContainerOpts
) )
if context.IsSet("config") {
opts = append(opts, oci.WithSpecFromFile(context.String("config")))
} else {
opts = append(opts, oci.WithDefaultSpec())
}
opts = append(opts, oci.WithEnv(context.StringSlice("env"))) opts = append(opts, oci.WithEnv(context.StringSlice("env")))
opts = append(opts, withMounts(context)) opts = append(opts, withMounts(context))
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
@ -117,15 +124,10 @@ 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("config") {
var s specs.Spec var s specs.Spec
if err := loadSpec(context.String("config"), &s); err != nil {
return nil, err
}
spec = containerd.WithSpec(&s, opts...) spec = containerd.WithSpec(&s, opts...)
} else {
spec = containerd.WithNewSpec(opts...)
}
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 rootfs snapshot for resolving /etc/passwd.

View File

@ -63,6 +63,13 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
cOpts []containerd.NewContainerOpts cOpts []containerd.NewContainerOpts
spec containerd.NewContainerOpts spec containerd.NewContainerOpts
) )
if context.IsSet("config") {
opts = append(opts, oci.WithSpecFromFile(context.String("config")))
} else {
opts = append(opts, oci.WithDefaultSpec())
}
opts = append(opts, oci.WithImageConfig(image)) opts = append(opts, oci.WithImageConfig(image))
opts = append(opts, oci.WithEnv(context.StringSlice("env"))) opts = append(opts, oci.WithEnv(context.StringSlice("env")))
opts = append(opts, withMounts(context)) opts = append(opts, withMounts(context))
@ -74,15 +81,8 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
opts = append(opts, oci.WithProcessCwd(cwd)) opts = append(opts, oci.WithProcessCwd(cwd))
} }
if context.IsSet("config") {
var s specs.Spec var s specs.Spec
if err := loadSpec(context.String("config"), &s); err != nil {
return nil, err
}
spec = containerd.WithSpec(&s, opts...) spec = containerd.WithSpec(&s, opts...)
} else {
spec = containerd.WithNewSpec(opts...)
}
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
cOpts = append(cOpts, containerd.WithImage(image)) cOpts = append(cOpts, containerd.WithImage(image))

View File

@ -26,7 +26,7 @@ import (
"github.com/containerd/console" "github.com/containerd/console"
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
shim "github.com/containerd/containerd/runtime/shim/v1" "github.com/containerd/containerd/runtime/v2/task"
"github.com/containerd/ttrpc" "github.com/containerd/ttrpc"
"github.com/containerd/typeurl" "github.com/containerd/typeurl"
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
@ -36,8 +36,6 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
var empty = &ptypes.Empty{}
var fifoFlags = []cli.Flag{ var fifoFlags = []cli.Flag{
cli.StringFlag{ cli.StringFlag{
Name: "stdin", Name: "stdin",
@ -57,7 +55,7 @@ var fifoFlags = []cli.Flag{
}, },
} }
// Command is the cli command for interacting with a shim // Command is the cli command for interacting with a task
var Command = cli.Command{ var Command = cli.Command{
Name: "shim", Name: "shim",
Usage: "interact with a shim directly", Usage: "interact with a shim directly",
@ -77,13 +75,13 @@ var Command = cli.Command{
var startCommand = cli.Command{ var startCommand = cli.Command{
Name: "start", Name: "start",
Usage: "start a container with a shim", Usage: "start a container with a task",
Action: func(context *cli.Context) error { Action: func(context *cli.Context) error {
service, err := getShimService(context) service, err := getTaskService(context)
if err != nil { if err != nil {
return err return err
} }
_, err = service.Start(gocontext.Background(), &shim.StartRequest{ _, err = service.Start(gocontext.Background(), &task.StartRequest{
ID: context.Args().First(), ID: context.Args().First(),
}) })
return err return err
@ -92,13 +90,15 @@ var startCommand = cli.Command{
var deleteCommand = cli.Command{ var deleteCommand = cli.Command{
Name: "delete", Name: "delete",
Usage: "delete a container with a shim", Usage: "delete a container with a task",
Action: func(context *cli.Context) error { Action: func(context *cli.Context) error {
service, err := getShimService(context) service, err := getTaskService(context)
if err != nil { if err != nil {
return err return err
} }
r, err := service.Delete(gocontext.Background(), empty) r, err := service.Delete(gocontext.Background(), &task.DeleteRequest{
ID: context.Args().First(),
})
if err != nil { if err != nil {
return err return err
} }
@ -109,13 +109,13 @@ var deleteCommand = cli.Command{
var stateCommand = cli.Command{ var stateCommand = cli.Command{
Name: "state", Name: "state",
Usage: "get the state of all the processes of the shim", Usage: "get the state of all the processes of the task",
Action: func(context *cli.Context) error { Action: func(context *cli.Context) error {
service, err := getShimService(context) service, err := getTaskService(context)
if err != nil { if err != nil {
return err return err
} }
r, err := service.State(gocontext.Background(), &shim.StateRequest{ r, err := service.State(gocontext.Background(), &task.StateRequest{
ID: context.Args().First(), ID: context.Args().First(),
}) })
if err != nil { if err != nil {
@ -128,7 +128,7 @@ var stateCommand = cli.Command{
var execCommand = cli.Command{ var execCommand = cli.Command{
Name: "exec", Name: "exec",
Usage: "exec a new process in the shim's container", Usage: "exec a new process in the task's container",
Flags: append(fifoFlags, Flags: append(fifoFlags,
cli.BoolFlag{ cli.BoolFlag{
Name: "attach,a", Name: "attach,a",
@ -149,7 +149,7 @@ var execCommand = cli.Command{
}, },
), ),
Action: func(context *cli.Context) error { Action: func(context *cli.Context) error {
service, err := getShimService(context) service, err := getTaskService(context)
if err != nil { if err != nil {
return err return err
} }
@ -178,7 +178,7 @@ var execCommand = cli.Command{
return err return err
} }
rq := &shim.ExecProcessRequest{ rq := &task.ExecProcessRequest{
ID: id, ID: id,
Spec: &ptypes.Any{ Spec: &ptypes.Any{
TypeUrl: url, TypeUrl: url,
@ -192,7 +192,7 @@ var execCommand = cli.Command{
if _, err := service.Exec(ctx, rq); err != nil { if _, err := service.Exec(ctx, rq); err != nil {
return err return err
} }
r, err := service.Start(ctx, &shim.StartRequest{ r, err := service.Start(ctx, &task.StartRequest{
ID: id, ID: id,
}) })
if err != nil { if err != nil {
@ -211,7 +211,7 @@ var execCommand = cli.Command{
if err != nil { if err != nil {
return err return err
} }
if _, err := service.ResizePty(ctx, &shim.ResizePtyRequest{ if _, err := service.ResizePty(ctx, &task.ResizePtyRequest{
ID: id, ID: id,
Width: uint32(size.Width), Width: uint32(size.Width),
Height: uint32(size.Height), Height: uint32(size.Height),
@ -225,7 +225,7 @@ var execCommand = cli.Command{
}, },
} }
func getShimService(context *cli.Context) (shim.ShimService, error) { func getTaskService(context *cli.Context) (task.TaskService, error) {
bindSocket := context.GlobalString("socket") bindSocket := context.GlobalString("socket")
if bindSocket == "" { if bindSocket == "" {
return nil, errors.New("socket path must be specified") return nil, errors.New("socket path must be specified")
@ -241,5 +241,5 @@ func getShimService(context *cli.Context) (shim.ShimService, error) {
// TODO(stevvooe): This actually leaks the connection. We were leaking it // TODO(stevvooe): This actually leaks the connection. We were leaking it
// before, so may not be a huge deal. // before, so may not be a huge deal.
return shim.NewShimClient(client), nil return task.NewTaskClient(client), nil
} }

View File

@ -18,9 +18,12 @@ package tasks
import ( import (
"fmt" "fmt"
"runtime"
"github.com/containerd/containerd" "github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -34,13 +37,18 @@ var checkpointCommand = cli.Command{
Name: "exit", Name: "exit",
Usage: "stop the container after the checkpoint", Usage: "stop the container after the checkpoint",
}, },
cli.StringFlag{
Name: "runtime",
Usage: "runtime name",
Value: fmt.Sprintf("io.containerd.runtime.v1.%s", runtime.GOOS),
},
}, },
Action: func(context *cli.Context) error { Action: func(context *cli.Context) error {
id := context.Args().First() id := context.Args().First()
if id == "" { if id == "" {
return errors.New("container id must be provided") return errors.New("container id must be provided")
} }
client, ctx, cancel, err := commands.NewClient(context) client, ctx, cancel, err := commands.NewClient(context, containerd.WithDefaultRuntime(context.String("runtime")))
if err != nil { if err != nil {
return err return err
} }
@ -55,7 +63,7 @@ var checkpointCommand = cli.Command{
} }
var opts []containerd.CheckpointTaskOpts var opts []containerd.CheckpointTaskOpts
if context.Bool("exit") { if context.Bool("exit") {
opts = append(opts, containerd.WithExit) opts = append(opts, withExit(context.String("runtime")))
} }
checkpoint, err := task.Checkpoint(ctx, opts...) checkpoint, err := task.Checkpoint(ctx, opts...)
if err != nil { if err != nil {
@ -65,3 +73,29 @@ var checkpointCommand = cli.Command{
return nil return nil
}, },
} }
func withExit(rt string) containerd.CheckpointTaskOpts {
return func(r *containerd.CheckpointTaskInfo) error {
switch rt {
case "io.containerd.runc.v1":
if r.Options == nil {
r.Options = &options.CheckpointOptions{
Exit: true,
}
} else {
opts, _ := r.Options.(*options.CheckpointOptions)
opts.Exit = true
}
default:
if r.Options == nil {
r.Options = &runctypes.CheckpointOptions{
Exit: true,
}
} else {
opts, _ := r.Options.(*runctypes.CheckpointOptions)
opts.Exit = true
}
}
return nil
}
}

View File

@ -0,0 +1,108 @@
// +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 tasks
import (
"encoding/json"
"errors"
"fmt"
"os"
"text/tabwriter"
"github.com/containerd/cgroups"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/typeurl"
"github.com/urfave/cli"
)
func init() {
// metricsCommand is only added on Linux as github.com/containerd/cgroups
// does not compile on darwin or windows
Command.Subcommands = append(Command.Subcommands, metricsCommand)
}
const (
formatFlag = "format"
formatTable = "table"
formatJSON = "json"
)
var metricsCommand = cli.Command{
Name: "metrics",
Usage: "get a single data point of metrics for a task with the built-in Linux runtime",
ArgsUsage: "CONTAINER",
Aliases: []string{"metric"},
Flags: []cli.Flag{
cli.StringFlag{
Name: formatFlag,
Usage: `"table" or "json"`,
Value: formatTable,
},
},
Action: func(context *cli.Context) error {
client, ctx, cancel, err := commands.NewClient(context)
if err != nil {
return err
}
defer cancel()
container, err := client.LoadContainer(ctx, context.Args().First())
if err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
return err
}
metric, err := task.Metrics(ctx)
if err != nil {
return nil
}
anydata, err := typeurl.UnmarshalAny(metric.Data)
if err != nil {
return err
}
data, ok := anydata.(*cgroups.Metrics)
if !ok {
return errors.New("cannot convert metric data to cgroups.Metrics")
}
switch context.String(formatFlag) {
case formatTable:
w := tabwriter.NewWriter(os.Stdout, 1, 8, 4, ' ', 0)
fmt.Fprintf(w, "ID\tTIMESTAMP\t\n")
fmt.Fprintf(w, "%s\t%s\t\n\n", metric.ID, metric.Timestamp)
fmt.Fprintf(w, "METRIC\tVALUE\t\n")
fmt.Fprintf(w, "memory.usage_in_bytes\t%d\t\n", data.Memory.Usage.Usage)
fmt.Fprintf(w, "memory.stat.cache\t%d\t\n", data.Memory.TotalCache)
fmt.Fprintf(w, "cpuacct.usage\t%d\t\n", data.CPU.Usage.Total)
fmt.Fprintf(w, "cpuacct.usage_percpu\t%v\t\n", data.CPU.Usage.PerCPU)
return w.Flush()
case formatJSON:
marshaledJSON, err := json.MarshalIndent(data, "", " ")
if err != nil {
return err
}
fmt.Println(string(marshaledJSON))
return nil
default:
return errors.New("format must be table or json")
}
},
}

View File

@ -59,15 +59,16 @@ func HandleConsoleResize(ctx gocontext.Context, task resizer, con console.Consol
// NewTask creates a new task // NewTask creates a new task
func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, tty, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) { func NewTask(ctx gocontext.Context, client *containerd.Client, container containerd.Container, _ string, tty, nullIO bool, ioOpts []cio.Opt, opts ...containerd.NewTaskOpts) (containerd.Task, error) {
ioCreator := cio.NewCreator(append([]cio.Opt{cio.WithStdio}, ioOpts...)...) var ioCreator cio.Creator
if tty { if tty {
ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio, cio.WithTerminal}, ioOpts...)...)
}
if nullIO { if nullIO {
if tty {
return nil, errors.New("tty and null-io cannot be used together") return nil, errors.New("tty and null-io cannot be used together")
} }
ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio, cio.WithTerminal}, ioOpts...)...)
} else if nullIO {
ioCreator = cio.NullIO ioCreator = cio.NullIO
} else {
ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio}, ioOpts...)...)
} }
return container.NewTask(ctx, ioCreator) return container.NewTask(ctx, ioCreator)
} }

View File

@ -173,10 +173,7 @@ func (c *container) Image(ctx context.Context) (Image, error) {
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to get image %s for container", r.Image) return nil, errors.Wrapf(err, "failed to get image %s for container", r.Image)
} }
return &image{ return NewImage(c.client, i), nil
client: c.client,
i: i,
}, nil
} }
func (c *container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...NewTaskOpts) (_ Task, err error) { func (c *container) NewTask(ctx context.Context, ioCreate cio.Creator, opts ...NewTaskOpts) (_ Task, err error) {

View File

@ -197,11 +197,10 @@ func WithNewSpec(opts ...oci.SpecOpts) NewContainerOpts {
// WithSpec sets the provided spec on the container // WithSpec sets the provided spec on the container
func WithSpec(s *oci.Spec, opts ...oci.SpecOpts) NewContainerOpts { func WithSpec(s *oci.Spec, opts ...oci.SpecOpts) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error { return func(ctx context.Context, client *Client, c *containers.Container) error {
for _, o := range opts { if err := oci.ApplyOpts(ctx, client, c, s, opts...); err != nil {
if err := o(ctx, client, c, s); err != nil {
return err return err
} }
}
var err error var err error
c.Spec, err = typeurl.MarshalAny(s) c.Spec, err = typeurl.MarshalAny(s)
return err return err

View File

@ -18,11 +18,15 @@ package containerd
import ( import (
"context" "context"
"errors"
"io"
containersapi "github.com/containerd/containerd/api/services/containers/v1" containersapi "github.com/containerd/containerd/api/services/containers/v1"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
) )
type remoteContainers struct { type remoteContainers struct {
@ -50,15 +54,56 @@ func (r *remoteContainers) Get(ctx context.Context, id string) (containers.Conta
} }
func (r *remoteContainers) List(ctx context.Context, filters ...string) ([]containers.Container, error) { func (r *remoteContainers) List(ctx context.Context, filters ...string) ([]containers.Container, error) {
containers, err := r.stream(ctx, filters...)
if err != nil {
if err == errStreamNotAvailable {
return r.list(ctx, filters...)
}
return nil, err
}
return containers, nil
}
func (r *remoteContainers) list(ctx context.Context, filters ...string) ([]containers.Container, error) {
resp, err := r.client.List(ctx, &containersapi.ListContainersRequest{ resp, err := r.client.List(ctx, &containersapi.ListContainersRequest{
Filters: filters, Filters: filters,
}) })
if err != nil { if err != nil {
return nil, errdefs.FromGRPC(err) return nil, errdefs.FromGRPC(err)
} }
return containersFromProto(resp.Containers), nil return containersFromProto(resp.Containers), nil
}
var errStreamNotAvailable = errors.New("streaming api not available")
func (r *remoteContainers) stream(ctx context.Context, filters ...string) ([]containers.Container, error) {
session, err := r.client.ListStream(ctx, &containersapi.ListContainersRequest{
Filters: filters,
})
if err != nil {
return nil, errdefs.FromGRPC(err)
}
var containers []containers.Container
for {
c, err := session.Recv()
if err != nil {
if err == io.EOF {
return containers, nil
}
if s, ok := status.FromError(err); ok {
if s.Code() == codes.Unimplemented {
return nil, errStreamNotAvailable
}
}
return nil, errdefs.FromGRPC(err)
}
select {
case <-ctx.Done():
return containers, ctx.Err()
default:
containers = append(containers, containerFromProto(c.Container))
}
}
} }
func (r *remoteContainers) Create(ctx context.Context, container containers.Container) (containers.Container, error) { func (r *remoteContainers) Create(ctx context.Context, container containers.Container) (containers.Container, error) {

View File

@ -322,6 +322,40 @@ func (s *store) ListStatuses(ctx context.Context, fs ...string) ([]content.Statu
return active, nil return active, nil
} }
// WalkStatusRefs is used to walk all status references
// Failed status reads will be logged and ignored, if
// this function is called while references are being altered,
// these error messages may be produced.
func (s *store) WalkStatusRefs(ctx context.Context, fn func(string) error) error {
fp, err := os.Open(filepath.Join(s.root, "ingest"))
if err != nil {
return err
}
defer fp.Close()
fis, err := fp.Readdir(-1)
if err != nil {
return err
}
for _, fi := range fis {
rf := filepath.Join(s.root, "ingest", fi.Name(), "ref")
ref, err := readFileString(rf)
if err != nil {
log.G(ctx).WithError(err).WithField("path", rf).Error("failed to read ingest ref")
continue
}
if err := fn(ref); err != nil {
return err
}
}
return nil
}
// status works like stat above except uses the path to the ingest. // status works like stat above except uses the path to the ingest.
func (s *store) status(ingestPath string) (content.Status, error) { func (s *store) status(ingestPath string) (content.Status, error) {
dp := filepath.Join(ingestPath, "data") dp := filepath.Join(ingestPath, "data")

View File

@ -34,23 +34,32 @@ const nvidiaCLI = "nvidia-container-cli"
// Capability specifies capabilities for the gpu inside the container // Capability specifies capabilities for the gpu inside the container
// Detailed explaination of options can be found: // Detailed explaination of options can be found:
// https://github.com/nvidia/nvidia-container-runtime#supported-driver-capabilities // https://github.com/nvidia/nvidia-container-runtime#supported-driver-capabilities
type Capability int type Capability string
const ( const (
// Compute capability // Compute capability
Compute Capability = iota + 1 Compute Capability = "compute"
// Compat32 capability // Compat32 capability
Compat32 Compat32 Capability = "compat32"
// Graphics capability // Graphics capability
Graphics Graphics Capability = "graphics"
// Utility capability // Utility capability
Utility Utility Capability = "utility"
// Video capability // Video capability
Video Video Capability = "video"
// Display capability // Display capability
Display Display Capability = "display"
) )
var allCaps = []Capability{
Compute,
Compat32,
Graphics,
Utility,
Video,
Display,
}
// WithGPUs adds NVIDIA gpu support to a container // WithGPUs adds NVIDIA gpu support to a container
func WithGPUs(opts ...Opts) oci.SpecOpts { func WithGPUs(opts ...Opts) oci.SpecOpts {
return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error { return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error {
@ -60,10 +69,13 @@ func WithGPUs(opts ...Opts) oci.SpecOpts {
return err return err
} }
} }
if c.OCIHookPath == "" {
path, err := exec.LookPath("containerd") path, err := exec.LookPath("containerd")
if err != nil { if err != nil {
return err return err
} }
c.OCIHookPath = path
}
nvidiaPath, err := exec.LookPath(nvidiaCLI) nvidiaPath, err := exec.LookPath(nvidiaCLI)
if err != nil { if err != nil {
return err return err
@ -72,7 +84,7 @@ func WithGPUs(opts ...Opts) oci.SpecOpts {
s.Hooks = &specs.Hooks{} s.Hooks = &specs.Hooks{}
} }
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{ s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
Path: path, Path: c.OCIHookPath,
Args: append([]string{ Args: append([]string{
"containerd", "containerd",
"oci-hook", "oci-hook",
@ -86,13 +98,13 @@ func WithGPUs(opts ...Opts) oci.SpecOpts {
} }
type config struct { type config struct {
Devices []int Devices []string
DeviceUUID string
Capabilities []Capability Capabilities []Capability
LoadKmods bool LoadKmods bool
LDCache string LDCache string
LDConfig string LDConfig string
Requirements []string Requirements []string
OCIHookPath string
} }
func (c *config) args() []string { func (c *config) args() []string {
@ -108,13 +120,10 @@ func (c *config) args() []string {
"configure", "configure",
) )
if len(c.Devices) > 0 { if len(c.Devices) > 0 {
args = append(args, fmt.Sprintf("--device=%s", strings.Join(toStrings(c.Devices), ","))) args = append(args, fmt.Sprintf("--device=%s", strings.Join(c.Devices, ",")))
}
if c.DeviceUUID != "" {
args = append(args, fmt.Sprintf("--device=%s", c.DeviceUUID))
} }
for _, c := range c.Capabilities { for _, c := range c.Capabilities {
args = append(args, fmt.Sprintf("--%s", capFlags[c])) args = append(args, fmt.Sprintf("--%s", c))
} }
if c.LDConfig != "" { if c.LDConfig != "" {
args = append(args, fmt.Sprintf("--ldconfig=%s", c.LDConfig)) args = append(args, fmt.Sprintf("--ldconfig=%s", c.LDConfig))
@ -126,56 +135,47 @@ func (c *config) args() []string {
return args return args
} }
var capFlags = map[Capability]string{
Compute: "compute",
Compat32: "compat32",
Graphics: "graphics",
Utility: "utility",
Video: "video",
Display: "display",
}
func toStrings(ints []int) []string {
var s []string
for _, i := range ints {
s = append(s, strconv.Itoa(i))
}
return s
}
// Opts are options for configuring gpu support // Opts are options for configuring gpu support
type Opts func(*config) error type Opts func(*config) error
// WithDevices adds the provided device indexes to the container // WithDevices adds the provided device indexes to the container
func WithDevices(ids ...int) Opts { func WithDevices(ids ...int) Opts {
return func(c *config) error { return func(c *config) error {
c.Devices = ids for _, i := range ids {
c.Devices = append(c.Devices, strconv.Itoa(i))
}
return nil return nil
} }
} }
// WithDeviceUUID adds the specific device UUID to the container // WithDeviceUUIDs adds the specific device UUID to the container
func WithDeviceUUID(guid string) Opts { func WithDeviceUUIDs(uuids ...string) Opts {
return func(c *config) error { return func(c *config) error {
c.DeviceUUID = guid c.Devices = append(c.Devices, uuids...)
return nil return nil
} }
} }
// WithAllDevices adds all gpus to the container // WithAllDevices adds all gpus to the container
func WithAllDevices(c *config) error { func WithAllDevices(c *config) error {
c.DeviceUUID = "all" c.Devices = []string{"all"}
return nil return nil
} }
// WithAllCapabilities adds all capabilities to the container for the gpus // WithAllCapabilities adds all capabilities to the container for the gpus
func WithAllCapabilities(c *config) error { func WithAllCapabilities(c *config) error {
for k := range capFlags { c.Capabilities = allCaps
c.Capabilities = append(c.Capabilities, k)
}
return nil return nil
} }
// WithCapabilities adds the specified capabilities to the container for the gpus
func WithCapabilities(caps ...Capability) Opts {
return func(c *config) error {
c.Capabilities = append(c.Capabilities, caps...)
return nil
}
}
// WithRequiredCUDAVersion sets the required cuda version // WithRequiredCUDAVersion sets the required cuda version
func WithRequiredCUDAVersion(major, minor int) Opts { func WithRequiredCUDAVersion(major, minor int) Opts {
return func(c *config) error { return func(c *config) error {
@ -183,3 +183,23 @@ func WithRequiredCUDAVersion(major, minor int) Opts {
return nil return nil
} }
} }
// WithOCIHookPath sets the hook path for the binary
func WithOCIHookPath(path string) Opts {
return func(c *config) error {
c.OCIHookPath = path
return nil
}
}
// WithLookupOCIHookPath sets the hook path for the binary via a binary name
func WithLookupOCIHookPath(name string) Opts {
return func(c *config) error {
path, err := exec.LookPath(name)
if err != nil {
return err
}
c.OCIHookPath = path
return nil
}
}

View File

@ -18,10 +18,10 @@ package walking
import ( import (
"context" "context"
"crypto/rand"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"io" "io"
"math/rand"
"time" "time"
"github.com/containerd/containerd/archive" "github.com/containerd/containerd/archive"

View File

@ -58,6 +58,16 @@ func NewImage(client *Client, i images.Image) Image {
return &image{ return &image{
client: client, client: client,
i: i, i: i,
platform: platforms.Default(),
}
}
// NewImageWithPlatform returns a client image object from the metadata image
func NewImageWithPlatform(client *Client, i images.Image, platform string) Image {
return &image{
client: client,
i: i,
platform: platform,
} }
} }
@ -65,6 +75,7 @@ type image struct {
client *Client client *Client
i images.Image i images.Image
platform string
} }
func (i *image) Name() string { func (i *image) Name() string {
@ -77,24 +88,24 @@ func (i *image) Target() ocispec.Descriptor {
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, platforms.Default()) return i.i.RootFS(ctx, provider, i.platform)
} }
func (i *image) Size(ctx context.Context) (int64, error) { func (i *image) Size(ctx context.Context) (int64, error) {
provider := i.client.ContentStore() provider := i.client.ContentStore()
return i.i.Size(ctx, provider, platforms.Default()) return i.i.Size(ctx, provider, i.platform)
} }
func (i *image) Config(ctx context.Context) (ocispec.Descriptor, error) { func (i *image) Config(ctx context.Context) (ocispec.Descriptor, error) {
provider := i.client.ContentStore() provider := i.client.ContentStore()
return i.i.Config(ctx, provider, platforms.Default()) return i.i.Config(ctx, provider, i.platform)
} }
func (i *image) IsUnpacked(ctx context.Context, snapshotterName string) (bool, error) { func (i *image) IsUnpacked(ctx context.Context, snapshotterName string) (bool, error) {
sn := i.client.SnapshotService(snapshotterName) sn := i.client.SnapshotService(snapshotterName)
cs := i.client.ContentStore() cs := i.client.ContentStore()
diffs, err := i.i.RootFS(ctx, cs, platforms.Default()) diffs, err := i.i.RootFS(ctx, cs, i.platform)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -117,7 +128,7 @@ func (i *image) Unpack(ctx context.Context, snapshotterName string) error {
} }
defer done(ctx) defer done(ctx)
layers, err := i.getLayers(ctx, platforms.Default()) layers, err := i.getLayers(ctx, i.platform)
if err != nil { if err != nil {
return err return err
} }
@ -154,7 +165,7 @@ func (i *image) Unpack(ctx context.Context, snapshotterName string) error {
} }
if unpacked { if unpacked {
desc, err := i.i.Config(ctx, cs, platforms.Default()) desc, err := i.i.Config(ctx, cs, i.platform)
if err != nil { if err != nil {
return err return err
} }

View File

@ -25,7 +25,6 @@ import (
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
ocispecs "github.com/opencontainers/image-spec/specs-go" ocispecs "github.com/opencontainers/image-spec/specs-go"
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"
@ -58,7 +57,7 @@ func (oe *V1Exporter) Export(ctx context.Context, store content.Provider, desc o
} }
handlers := images.Handlers( handlers := images.Handlers(
images.FilterPlatforms(images.ChildrenHandler(store), platforms.Default()), images.ChildrenHandler(store),
images.HandlerFunc(exportHandler), images.HandlerFunc(exportHandler),
) )

View File

@ -80,10 +80,7 @@ func (c *Client) Import(ctx context.Context, importer images.Importer, reader io
imgrec = updated imgrec = updated
} }
images = append(images, &image{ images = append(images, NewImage(c, imgrec))
client: c,
i: imgrec,
})
} }
return images, nil return images, nil
} }

91
vendor/github.com/containerd/containerd/install.go generated vendored Normal file
View File

@ -0,0 +1,91 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package containerd
import (
"archive/tar"
"context"
"os"
"path/filepath"
introspectionapi "github.com/containerd/containerd/api/services/introspection/v1"
"github.com/containerd/containerd/archive"
"github.com/containerd/containerd/archive/compression"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/pkg/errors"
)
// Install a binary image into the opt service
func (c *Client) Install(ctx context.Context, image Image, opts ...InstallOpts) error {
resp, err := c.IntrospectionService().Plugins(ctx, &introspectionapi.PluginsRequest{
Filters: []string{
"id==opt",
},
})
if err != nil {
return err
}
if len(resp.Plugins) != 1 {
return errors.New("opt service not enabled")
}
path := resp.Plugins[0].Exports["path"]
if path == "" {
return errors.New("opt path not exported")
}
var config InstallConfig
for _, o := range opts {
o(&config)
}
var (
cs = image.ContentStore()
platform = platforms.Default()
)
manifest, err := images.Manifest(ctx, cs, image.Target(), platform)
if err != nil {
return err
}
for _, layer := range manifest.Layers {
ra, err := cs.ReaderAt(ctx, layer)
if err != nil {
return err
}
cr := content.NewReader(ra)
r, err := compression.DecompressStream(cr)
if err != nil {
return err
}
defer r.Close()
if _, err := archive.Apply(ctx, path, r, archive.WithFilter(func(hdr *tar.Header) (bool, error) {
d := filepath.Dir(hdr.Name)
result := d == "bin"
if config.Libs {
result = result || d == "lib"
}
if result && !config.Replace {
if _, err := os.Lstat(filepath.Join(path, hdr.Name)); err == nil {
return false, errors.Errorf("cannot replace %s in %s", hdr.Name, path)
}
}
return result, nil
})); err != nil {
return err
}
}
return nil
}

View File

@ -0,0 +1,38 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package containerd
// InstallOpts configures binary installs
type InstallOpts func(*InstallConfig)
// InstallConfig sets the binary install configuration
type InstallConfig struct {
// Libs installs libs from the image
Libs bool
// Replace will overwrite existing binaries or libs in the opt directory
Replace bool
}
// WithInstallLibs installs libs from the image
func WithInstallLibs(c *InstallConfig) {
c.Libs = true
}
// WithInstallReplace will replace existing files
func WithInstallReplace(c *InstallConfig) {
c.Replace = true
}

View File

@ -20,89 +20,27 @@ import (
"context" "context"
"time" "time"
leasesapi "github.com/containerd/containerd/api/services/leases/v1"
"github.com/containerd/containerd/leases" "github.com/containerd/containerd/leases"
) )
// Lease is used to hold a reference to active resources which have not been
// referenced by a root resource. This is useful for preventing garbage
// collection of resources while they are actively being updated.
type Lease struct {
id string
createdAt time.Time
client *Client
}
// CreateLease creates a new lease
func (c *Client) CreateLease(ctx context.Context) (Lease, error) {
lapi := c.LeasesService()
resp, err := lapi.Create(ctx, &leasesapi.CreateRequest{})
if err != nil {
return Lease{}, err
}
return Lease{
id: resp.Lease.ID,
client: c,
}, nil
}
// ListLeases lists active leases
func (c *Client) ListLeases(ctx context.Context) ([]Lease, error) {
lapi := c.LeasesService()
resp, err := lapi.List(ctx, &leasesapi.ListRequest{})
if err != nil {
return nil, err
}
leases := make([]Lease, len(resp.Leases))
for i := range resp.Leases {
leases[i] = Lease{
id: resp.Leases[i].ID,
createdAt: resp.Leases[i].CreatedAt,
client: c,
}
}
return leases, nil
}
// WithLease attaches a lease on the context // WithLease attaches a lease on the context
func (c *Client) WithLease(ctx context.Context) (context.Context, func(context.Context) error, error) { func (c *Client) WithLease(ctx context.Context) (context.Context, func(context.Context) error, error) {
_, ok := leases.Lease(ctx) _, ok := leases.FromContext(ctx)
if ok { if ok {
return ctx, func(context.Context) error { return ctx, func(context.Context) error {
return nil return nil
}, nil }, nil
} }
l, err := c.CreateLease(ctx) ls := c.LeasesService()
l, err := ls.Create(ctx, leases.WithRandomID(), leases.WithExpiration(24*time.Hour))
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
ctx = leases.WithLease(ctx, l.ID()) ctx = leases.WithLease(ctx, l.ID)
return ctx, func(ctx context.Context) error { return ctx, func(ctx context.Context) error {
return l.Delete(ctx) return ls.Delete(ctx, l)
}, nil }, nil
} }
// ID returns the lease ID
func (l Lease) ID() string {
return l.id
}
// CreatedAt returns the time at which the lease was created
func (l Lease) CreatedAt() time.Time {
return l.createdAt
}
// Delete deletes the lease, removing the reference to all resources created
// during the lease.
func (l Lease) Delete(ctx context.Context) error {
lapi := l.client.LeasesService()
_, err := lapi.Delete(ctx, &leasesapi.DeleteRequest{
ID: l.id,
})
return err
}

View File

@ -29,8 +29,8 @@ func WithLease(ctx context.Context, lid string) context.Context {
return withGRPCLeaseHeader(ctx, lid) return withGRPCLeaseHeader(ctx, lid)
} }
// Lease returns the lease from the context. // FromContext returns the lease from the context.
func Lease(ctx context.Context) (string, bool) { func FromContext(ctx context.Context) (string, bool) {
lid, ok := ctx.Value(leaseKey{}).(string) lid, ok := ctx.Value(leaseKey{}).(string)
if !ok { if !ok {
return fromGRPCHeader(ctx) return fromGRPCHeader(ctx)

43
vendor/github.com/containerd/containerd/leases/id.go generated vendored Normal file
View File

@ -0,0 +1,43 @@
/*
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 leases
import (
"encoding/base64"
"fmt"
"math/rand"
"time"
)
// WithRandomID sets the lease ID to a random unique value
func WithRandomID() Opt {
return func(l *Lease) error {
t := time.Now()
var b [3]byte
rand.Read(b[:])
l.ID = fmt.Sprintf("%d-%s", t.Nanosecond(), base64.URLEncoding.EncodeToString(b[:]))
return nil
}
}
// WithID sets the ID for the lease
func WithID(id string) Opt {
return func(l *Lease) error {
l.ID = id
return nil
}
}

View File

@ -0,0 +1,76 @@
/*
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 leases
import (
"context"
"time"
)
// Opt is used to set options on a lease
type Opt func(*Lease) error
// DeleteOpt allows configuring a delete operation
type DeleteOpt func(context.Context, *DeleteOptions) error
// Manager is used to create, list, and remove leases
type Manager interface {
Create(context.Context, ...Opt) (Lease, error)
Delete(context.Context, Lease, ...DeleteOpt) error
List(context.Context, ...string) ([]Lease, error)
}
// Lease retains resources to prevent cleanup before
// the resources can be fully referenced.
type Lease struct {
ID string
CreatedAt time.Time
Labels map[string]string
}
// DeleteOptions provide options on image delete
type DeleteOptions struct {
Synchronous bool
}
// SynchronousDelete is used to indicate that a lease deletion and removal of
// any unreferenced resources should occur synchronously before returning the
// result.
func SynchronousDelete(ctx context.Context, o *DeleteOptions) error {
o.Synchronous = true
return nil
}
// WithLabels sets labels on a lease
func WithLabels(labels map[string]string) Opt {
return func(l *Lease) error {
l.Labels = labels
return nil
}
}
// WithExpiration sets an expiration on the lease
func WithExpiration(d time.Duration) Opt {
return func(l *Lease) error {
if l.Labels == nil {
l.Labels = map[string]string{}
}
l.Labels["containerd.io/gc.expire"] = time.Now().Add(d).Format(time.RFC3339)
return nil
}
}

View File

@ -0,0 +1,93 @@
/*
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 proxy
import (
"context"
leasesapi "github.com/containerd/containerd/api/services/leases/v1"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/leases"
)
type proxyManager struct {
client leasesapi.LeasesClient
}
// NewLeaseManager returns a lease manager which communicates
// through a grpc lease service.
func NewLeaseManager(client leasesapi.LeasesClient) leases.Manager {
return &proxyManager{
client: client,
}
}
func (pm *proxyManager) Create(ctx context.Context, opts ...leases.Opt) (leases.Lease, error) {
l := leases.Lease{}
for _, opt := range opts {
if err := opt(&l); err != nil {
return leases.Lease{}, err
}
}
resp, err := pm.client.Create(ctx, &leasesapi.CreateRequest{
ID: l.ID,
Labels: l.Labels,
})
if err != nil {
return leases.Lease{}, errdefs.FromGRPC(err)
}
return leases.Lease{
ID: resp.Lease.ID,
CreatedAt: resp.Lease.CreatedAt,
Labels: resp.Lease.Labels,
}, nil
}
func (pm *proxyManager) Delete(ctx context.Context, l leases.Lease, opts ...leases.DeleteOpt) error {
var do leases.DeleteOptions
for _, opt := range opts {
if err := opt(ctx, &do); err != nil {
return err
}
}
_, err := pm.client.Delete(ctx, &leasesapi.DeleteRequest{
ID: l.ID,
Sync: do.Synchronous,
})
return errdefs.FromGRPC(err)
}
func (pm *proxyManager) List(ctx context.Context, filters ...string) ([]leases.Lease, error) {
resp, err := pm.client.List(ctx, &leasesapi.ListRequest{
Filters: filters,
})
if err != nil {
return nil, errdefs.FromGRPC(err)
}
l := make([]leases.Lease, len(resp.Leases))
for i := range resp.Leases {
l[i] = leases.Lease{
ID: resp.Leases[i].ID,
CreatedAt: resp.Leases[i].CreatedAt,
Labels: resp.Leases[i].Labels,
}
}
return l, nil
}

View File

@ -42,6 +42,10 @@ type (
// and is usually used to trace detailed behavior of the program. // and is usually used to trace detailed behavior of the program.
const TraceLevel = logrus.Level(uint32(logrus.DebugLevel + 1)) const TraceLevel = logrus.Level(uint32(logrus.DebugLevel + 1))
// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
// ensure the formatted time is always the same number of characters.
const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
// ParseLevel takes a string level and returns the Logrus log level constant. // ParseLevel takes a string level and returns the Logrus log level constant.
// It supports trace level. // It supports trace level.
func ParseLevel(lvl string) (logrus.Level, error) { func ParseLevel(lvl string) (logrus.Level, error) {

View File

@ -23,6 +23,7 @@ import (
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
"github.com/containerd/containerd/filters" "github.com/containerd/containerd/filters"
"github.com/containerd/containerd/images" "github.com/containerd/containerd/images"
"github.com/containerd/containerd/leases"
) )
func adaptImage(o interface{}) filters.Adaptor { func adaptImage(o interface{}) filters.Adaptor {
@ -119,6 +120,23 @@ func adaptContentStatus(status content.Status) filters.Adaptor {
}) })
} }
func adaptLease(lease leases.Lease) filters.Adaptor {
return filters.AdapterFunc(func(fieldpath []string) (string, bool) {
if len(fieldpath) == 0 {
return "", false
}
switch fieldpath[0] {
case "id":
return lease.ID, len(lease.ID) > 0
case "labels":
return checkMap(fieldpath[1:], lease.Labels)
}
return "", false
})
}
func checkMap(fieldpath []string, m map[string]string) (string, bool) { func checkMap(fieldpath []string, m map[string]string) (string, bool) {
if len(m) == 0 { if len(m) == 0 {
return "", false return "", false

View File

@ -72,6 +72,7 @@ var (
bucketKeyCreatedAt = []byte("createdat") bucketKeyCreatedAt = []byte("createdat")
bucketKeyExpected = []byte("expected") bucketKeyExpected = []byte("expected")
bucketKeyRef = []byte("ref") bucketKeyRef = []byte("ref")
bucketKeyExpireAt = []byte("expireat")
deprecatedBucketKeyObjectIngest = []byte("ingest") // stores ingest links, deprecated in v1.2 deprecatedBucketKeyObjectIngest = []byte("ingest") // stores ingest links, deprecated in v1.2
) )

View File

@ -328,6 +328,10 @@ func (cs *contentStore) Abort(ctx context.Context, ref string) error {
return err return err
} }
if err := removeIngestLease(ctx, tx, ref); err != nil {
return err
}
// if not shared content, delete active ingest on backend // if not shared content, delete active ingest on backend
if expected == "" { if expected == "" {
return cs.Store.Abort(ctx, bref) return cs.Store.Abort(ctx, bref)
@ -395,6 +399,11 @@ func (cs *contentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (
return err return err
} }
leased, err := addIngestLease(ctx, tx, wOpts.Ref)
if err != nil {
return err
}
brefb := bkt.Get(bucketKeyRef) brefb := bkt.Get(bucketKeyRef)
if brefb == nil { if brefb == nil {
sid, err := bkt.NextSequence() sid, err := bkt.NextSequence()
@ -409,6 +418,18 @@ func (cs *contentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (
} else { } else {
bref = string(brefb) bref = string(brefb)
} }
if !leased {
// Add timestamp to allow aborting once stale
// When lease is set the ingest should be aborted
// after lease it belonged to is deleted.
// Expiration can be configurable in the future to
// give more control to the daemon, however leases
// already give users more control of expiration.
expireAt := time.Now().UTC().Add(24 * time.Hour)
if err := writeExpireAt(expireAt, bkt); err != nil {
return err
}
}
if shared { if shared {
if err := bkt.Put(bucketKeyExpected, []byte(wOpts.Desc.Digest)); err != nil { if err := bkt.Put(bucketKeyExpected, []byte(wOpts.Desc.Digest)); err != nil {
@ -543,6 +564,9 @@ func (nw *namespacedWriter) Commit(ctx context.Context, size int64, expected dig
if err != nil { if err != nil {
return err return err
} }
if err := removeIngestLease(ctx, tx, nw.ref); err != nil {
return err
}
return addContentLease(ctx, tx, dgst) return addContentLease(ctx, tx, dgst)
}) })
} }
@ -697,6 +721,30 @@ func writeInfo(info *content.Info, bkt *bolt.Bucket) error {
return bkt.Put(bucketKeySize, sizeEncoded) return bkt.Put(bucketKeySize, sizeEncoded)
} }
func readExpireAt(bkt *bolt.Bucket) (*time.Time, error) {
v := bkt.Get(bucketKeyExpireAt)
if v == nil {
return nil, nil
}
t := &time.Time{}
if err := t.UnmarshalBinary(v); err != nil {
return nil, err
}
return t, nil
}
func writeExpireAt(expire time.Time, bkt *bolt.Bucket) error {
expireAt, err := expire.MarshalBinary()
if err != nil {
return err
}
if err := bkt.Put(bucketKeyExpireAt, expireAt); err != nil {
return err
}
return nil
}
func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, err error) { func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, err error) {
cs.l.Lock() cs.l.Lock()
t1 := time.Now() t1 := time.Now()
@ -707,7 +755,8 @@ func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, er
cs.l.Unlock() cs.l.Unlock()
}() }()
seen := map[string]struct{}{} contentSeen := map[string]struct{}{}
ingestSeen := map[string]struct{}{}
if err := cs.db.View(func(tx *bolt.Tx) error { if err := cs.db.View(func(tx *bolt.Tx) error {
v1bkt := tx.Bucket(bucketKeyVersion) v1bkt := tx.Bucket(bucketKeyVersion)
if v1bkt == nil { if v1bkt == nil {
@ -730,7 +779,7 @@ func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, er
if bbkt != nil { if bbkt != nil {
if err := bbkt.ForEach(func(ck, cv []byte) error { if err := bbkt.ForEach(func(ck, cv []byte) error {
if cv == nil { if cv == nil {
seen[string(ck)] = struct{}{} contentSeen[string(ck)] = struct{}{}
} }
return nil return nil
}); err != nil { }); err != nil {
@ -742,9 +791,17 @@ func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, er
if ibkt != nil { if ibkt != nil {
if err := ibkt.ForEach(func(ref, v []byte) error { if err := ibkt.ForEach(func(ref, v []byte) error {
if v == nil { if v == nil {
expected := ibkt.Bucket(ref).Get(bucketKeyExpected) bkt := ibkt.Bucket(ref)
// expected here may be from a different namespace
// so much be explicitly retained from the ingest
// in case it was removed from the other namespace
expected := bkt.Get(bucketKeyExpected)
if len(expected) > 0 { if len(expected) > 0 {
seen[string(expected)] = struct{}{} contentSeen[string(expected)] = struct{}{}
}
bref := bkt.Get(bucketKeyRef)
if len(bref) > 0 {
ingestSeen[string(bref)] = struct{}{}
} }
} }
return nil return nil
@ -760,7 +817,7 @@ func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, er
} }
err = cs.Store.Walk(ctx, func(info content.Info) error { err = cs.Store.Walk(ctx, func(info content.Info) error {
if _, ok := seen[info.Digest.String()]; !ok { if _, ok := contentSeen[info.Digest.String()]; !ok {
if err := cs.Store.Delete(ctx, info.Digest); err != nil { if err := cs.Store.Delete(ctx, info.Digest); err != nil {
return err return err
} }
@ -768,5 +825,40 @@ func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, er
} }
return nil return nil
}) })
if err != nil {
return
}
// If the content store has implemented a more efficient walk function
// then use that else fallback to reading all statuses which may
// cause reading of unneeded metadata.
type statusWalker interface {
WalkStatusRefs(context.Context, func(string) error) error
}
if w, ok := cs.Store.(statusWalker); ok {
err = w.WalkStatusRefs(ctx, func(ref string) error {
if _, ok := ingestSeen[ref]; !ok {
if err := cs.Store.Abort(ctx, ref); err != nil {
return err
}
log.G(ctx).WithField("ref", ref).Debug("cleanup aborting ingest")
}
return nil
})
} else {
var statuses []content.Status
statuses, err = cs.Store.ListStatuses(ctx)
if err != nil {
return 0, err
}
for _, status := range statuses {
if _, ok := ingestSeen[status.Ref]; !ok {
if err = cs.Store.Abort(ctx, status.Ref); err != nil {
return
}
log.G(ctx).WithField("ref", status.Ref).Debug("cleanup aborting ingest")
}
}
}
return return
} }

View File

@ -275,7 +275,7 @@ func (m *DB) GarbageCollect(ctx context.Context) (gc.Stats, error) {
if idx := strings.IndexRune(n.Key, '/'); idx > 0 { if idx := strings.IndexRune(n.Key, '/'); idx > 0 {
m.dirtySS[n.Key[:idx]] = struct{}{} m.dirtySS[n.Key[:idx]] = struct{}{}
} }
} else if n.Type == ResourceContent { } else if n.Type == ResourceContent || n.Type == ResourceIngest {
m.dirtyCS = true m.dirtyCS = true
} }
return remove(ctx, tx, n) return remove(ctx, tx, n)

View File

@ -21,6 +21,7 @@ import (
"context" "context"
"fmt" "fmt"
"strings" "strings"
"time"
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/containerd/containerd/gc" "github.com/containerd/containerd/gc"
@ -39,12 +40,17 @@ const (
ResourceContainer ResourceContainer
// ResourceTask specifies a task resource // ResourceTask specifies a task resource
ResourceTask ResourceTask
// ResourceLease specifies a lease
ResourceLease
// ResourceIngest specifies a content ingest
ResourceIngest
) )
var ( var (
labelGCRoot = []byte("containerd.io/gc.root") labelGCRoot = []byte("containerd.io/gc.root")
labelGCSnapRef = []byte("containerd.io/gc.ref.snapshot.") labelGCSnapRef = []byte("containerd.io/gc.ref.snapshot.")
labelGCContentRef = []byte("containerd.io/gc.ref.content") labelGCContentRef = []byte("containerd.io/gc.ref.content")
labelGCExpire = []byte("containerd.io/gc.expire")
) )
func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error { func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
@ -53,6 +59,8 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
return nil return nil
} }
expThreshold := time.Now()
// iterate through each namespace // iterate through each namespace
v1c := v1bkt.Cursor() v1c := v1bkt.Cursor()
@ -71,6 +79,30 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
} }
libkt := lbkt.Bucket(k) libkt := lbkt.Bucket(k)
if lblbkt := libkt.Bucket(bucketKeyObjectLabels); lblbkt != nil {
if expV := lblbkt.Get(labelGCExpire); expV != nil {
exp, err := time.Parse(time.RFC3339, string(expV))
if err != nil {
// label not used, log and continue to use lease
log.G(ctx).WithError(err).WithField("lease", string(k)).Infof("ignoring invalid expiration value %q", string(expV))
} else if expThreshold.After(exp) {
// lease has expired, skip
return nil
}
}
}
select {
case nc <- gcnode(ResourceLease, ns, string(k)):
case <-ctx.Done():
return ctx.Err()
}
// Emit content and snapshots as roots instead of implementing
// in references. Since leases cannot be referenced there is
// no need to allow the lookup to be recursive, handling here
// therefore reduces the number of database seeks.
cbkt := libkt.Bucket(bucketKeyObjectContent) cbkt := libkt.Bucket(bucketKeyObjectContent)
if cbkt != nil { if cbkt != nil {
if err := cbkt.ForEach(func(k, v []byte) error { if err := cbkt.ForEach(func(k, v []byte) error {
@ -106,6 +138,20 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
} }
} }
ibkt := libkt.Bucket(bucketKeyObjectIngests)
if ibkt != nil {
if err := ibkt.ForEach(func(k, v []byte) error {
select {
case nc <- gcnode(ResourceIngest, ns, string(k)):
case <-ctx.Done():
return ctx.Err()
}
return nil
}); err != nil {
return err
}
}
return nil return nil
}); err != nil { }); err != nil {
return err return err
@ -141,8 +187,30 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
cbkt := nbkt.Bucket(bucketKeyObjectContent) cbkt := nbkt.Bucket(bucketKeyObjectContent)
if cbkt != nil { if cbkt != nil {
cbkt = cbkt.Bucket(bucketKeyObjectBlob) ibkt := cbkt.Bucket(bucketKeyObjectIngests)
if ibkt != nil {
if err := ibkt.ForEach(func(k, v []byte) error {
if v != nil {
return nil
} }
ea, err := readExpireAt(ibkt.Bucket(k))
if err != nil {
return err
}
if ea == nil || expThreshold.After(*ea) {
return nil
}
select {
case nc <- gcnode(ResourceIngest, ns, string(k)):
case <-ctx.Done():
return ctx.Err()
}
return nil
}); err != nil {
return err
}
}
cbkt = cbkt.Bucket(bucketKeyObjectBlob)
if cbkt != nil { if cbkt != nil {
if err := cbkt.ForEach(func(k, v []byte) error { if err := cbkt.ForEach(func(k, v []byte) error {
if v != nil { if v != nil {
@ -153,6 +221,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
return err return err
} }
} }
}
cbkt = nbkt.Bucket(bucketKeyObjectContainers) cbkt = nbkt.Bucket(bucketKeyObjectContainers)
if cbkt != nil { if cbkt != nil {
@ -240,6 +309,19 @@ func references(ctx context.Context, tx *bolt.Tx, node gc.Node, fn func(gc.Node)
} }
return sendSnapshotRefs(node.Namespace, bkt, fn) return sendSnapshotRefs(node.Namespace, bkt, fn)
} else if node.Type == ResourceIngest {
// Send expected value
bkt := getBucket(tx, bucketKeyVersion, []byte(node.Namespace), bucketKeyObjectContent, bucketKeyObjectIngests, []byte(node.Key))
if bkt == nil {
// Node may be created from dead edge
return nil
}
// Load expected
expected := bkt.Get(bucketKeyExpected)
if len(expected) > 0 {
fn(gcnode(ResourceContent, node.Namespace, string(expected)))
}
return nil
} }
return nil return nil
@ -261,6 +343,18 @@ func scanAll(ctx context.Context, tx *bolt.Tx, fn func(ctx context.Context, n gc
nbkt := v1bkt.Bucket(k) nbkt := v1bkt.Bucket(k)
ns := string(k) ns := string(k)
lbkt := nbkt.Bucket(bucketKeyObjectLeases)
if lbkt != nil {
if err := lbkt.ForEach(func(k, v []byte) error {
if v != nil {
return nil
}
return fn(ctx, gcnode(ResourceLease, ns, string(k)))
}); err != nil {
return err
}
}
sbkt := nbkt.Bucket(bucketKeyObjectSnapshots) sbkt := nbkt.Bucket(bucketKeyObjectSnapshots)
if sbkt != nil { if sbkt != nil {
if err := sbkt.ForEach(func(sk, sv []byte) error { if err := sbkt.ForEach(func(sk, sv []byte) error {
@ -282,8 +376,20 @@ func scanAll(ctx context.Context, tx *bolt.Tx, fn func(ctx context.Context, n gc
cbkt := nbkt.Bucket(bucketKeyObjectContent) cbkt := nbkt.Bucket(bucketKeyObjectContent)
if cbkt != nil { if cbkt != nil {
cbkt = cbkt.Bucket(bucketKeyObjectBlob) ibkt := cbkt.Bucket(bucketKeyObjectIngests)
if ibkt != nil {
if err := ibkt.ForEach(func(k, v []byte) error {
if v != nil {
return nil
} }
node := gcnode(ResourceIngest, ns, string(k))
return fn(ctx, node)
}); err != nil {
return err
}
}
cbkt = cbkt.Bucket(bucketKeyObjectBlob)
if cbkt != nil { if cbkt != nil {
if err := cbkt.ForEach(func(k, v []byte) error { if err := cbkt.ForEach(func(k, v []byte) error {
if v != nil { if v != nil {
@ -296,6 +402,7 @@ func scanAll(ctx context.Context, tx *bolt.Tx, fn func(ctx context.Context, n gc
} }
} }
} }
}
return nil return nil
} }
@ -334,6 +441,20 @@ func remove(ctx context.Context, tx *bolt.Tx, node gc.Node) error {
return ssbkt.DeleteBucket([]byte(parts[1])) return ssbkt.DeleteBucket([]byte(parts[1]))
} }
} }
case ResourceLease:
lbkt := nsbkt.Bucket(bucketKeyObjectLeases)
if lbkt != nil {
return lbkt.DeleteBucket([]byte(node.Key))
}
case ResourceIngest:
ibkt := nsbkt.Bucket(bucketKeyObjectContent)
if ibkt != nil {
ibkt = ibkt.Bucket(bucketKeyObjectIngests)
}
if ibkt != nil {
log.G(ctx).WithField("ref", node.Key).Debug("remove ingest")
return ibkt.DeleteBucket([]byte(node.Key))
}
} }
return nil return nil

View File

@ -22,6 +22,7 @@ import (
"github.com/boltdb/bolt" "github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/filters"
"github.com/containerd/containerd/leases" "github.com/containerd/containerd/leases"
"github.com/containerd/containerd/metadata/boltutil" "github.com/containerd/containerd/metadata/boltutil"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
@ -29,17 +30,6 @@ import (
"github.com/pkg/errors" "github.com/pkg/errors"
) )
// Lease retains resources to prevent garbage collection before
// the resources can be fully referenced.
type Lease struct {
ID string
CreatedAt time.Time
Labels map[string]string
Content []string
Snapshots map[string][]string
}
// LeaseManager manages the create/delete lifecyle of leases // LeaseManager manages the create/delete lifecyle of leases
// and also returns existing leases // and also returns existing leases
type LeaseManager struct { type LeaseManager struct {
@ -55,49 +45,56 @@ func NewLeaseManager(tx *bolt.Tx) *LeaseManager {
} }
// Create creates a new lease using the provided lease // Create creates a new lease using the provided lease
func (lm *LeaseManager) Create(ctx context.Context, lid string, labels map[string]string) (Lease, error) { func (lm *LeaseManager) Create(ctx context.Context, opts ...leases.Opt) (leases.Lease, error) {
var l leases.Lease
for _, opt := range opts {
if err := opt(&l); err != nil {
return leases.Lease{}, err
}
}
if l.ID == "" {
return leases.Lease{}, errors.New("lease id must be provided")
}
namespace, err := namespaces.NamespaceRequired(ctx) namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil { if err != nil {
return Lease{}, err return leases.Lease{}, err
} }
topbkt, err := createBucketIfNotExists(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) topbkt, err := createBucketIfNotExists(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
if err != nil { if err != nil {
return Lease{}, err return leases.Lease{}, err
} }
txbkt, err := topbkt.CreateBucket([]byte(lid)) txbkt, err := topbkt.CreateBucket([]byte(l.ID))
if err != nil { if err != nil {
if err == bolt.ErrBucketExists { if err == bolt.ErrBucketExists {
err = errdefs.ErrAlreadyExists err = errdefs.ErrAlreadyExists
} }
return Lease{}, errors.Wrapf(err, "lease %q", lid) return leases.Lease{}, errors.Wrapf(err, "lease %q", l.ID)
} }
t := time.Now().UTC() t := time.Now().UTC()
createdAt, err := t.MarshalBinary() createdAt, err := t.MarshalBinary()
if err != nil { if err != nil {
return Lease{}, err return leases.Lease{}, err
} }
if err := txbkt.Put(bucketKeyCreatedAt, createdAt); err != nil { if err := txbkt.Put(bucketKeyCreatedAt, createdAt); err != nil {
return Lease{}, err return leases.Lease{}, err
} }
if labels != nil { if l.Labels != nil {
if err := boltutil.WriteLabels(txbkt, labels); err != nil { if err := boltutil.WriteLabels(txbkt, l.Labels); err != nil {
return Lease{}, err return leases.Lease{}, err
} }
} }
l.CreatedAt = t
return Lease{ return l, nil
ID: lid,
CreatedAt: t,
Labels: labels,
}, nil
} }
// Delete delets the lease with the provided lease ID // Delete delets the lease with the provided lease ID
func (lm *LeaseManager) Delete(ctx context.Context, lid string) error { func (lm *LeaseManager) Delete(ctx context.Context, lease leases.Lease, _ ...leases.DeleteOpt) error {
namespace, err := namespaces.NamespaceRequired(ctx) namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil { if err != nil {
return err return err
@ -105,26 +102,34 @@ func (lm *LeaseManager) Delete(ctx context.Context, lid string) error {
topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
if topbkt == nil { if topbkt == nil {
return nil return errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID)
}
if err := topbkt.DeleteBucket([]byte(lease.ID)); err != nil {
if err == bolt.ErrBucketNotFound {
err = errors.Wrapf(errdefs.ErrNotFound, "lease %q", lease.ID)
} }
if err := topbkt.DeleteBucket([]byte(lid)); err != nil && err != bolt.ErrBucketNotFound {
return err return err
} }
return nil return nil
} }
// List lists all active leases // List lists all active leases
func (lm *LeaseManager) List(ctx context.Context, includeResources bool, filter ...string) ([]Lease, error) { func (lm *LeaseManager) List(ctx context.Context, fs ...string) ([]leases.Lease, error) {
namespace, err := namespaces.NamespaceRequired(ctx) namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var leases []Lease filter, err := filters.ParseAll(fs...)
if err != nil {
return nil, errors.Wrapf(errdefs.ErrInvalidArgument, err.Error())
}
var ll []leases.Lease
topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases) topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
if topbkt == nil { if topbkt == nil {
return leases, nil return ll, nil
} }
if err := topbkt.ForEach(func(k, v []byte) error { if err := topbkt.ForEach(func(k, v []byte) error {
@ -133,7 +138,7 @@ func (lm *LeaseManager) List(ctx context.Context, includeResources bool, filter
} }
txbkt := topbkt.Bucket(k) txbkt := topbkt.Bucket(k)
l := Lease{ l := leases.Lease{
ID: string(k), ID: string(k),
} }
@ -150,21 +155,20 @@ func (lm *LeaseManager) List(ctx context.Context, includeResources bool, filter
} }
l.Labels = labels l.Labels = labels
// TODO: Read Snapshots if filter.Match(adaptLease(l)) {
// TODO: Read Content ll = append(ll, l)
}
leases = append(leases, l)
return nil return nil
}); err != nil { }); err != nil {
return nil, err return nil, err
} }
return leases, nil return ll, nil
} }
func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error { func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error {
lid, ok := leases.Lease(ctx) lid, ok := leases.FromContext(ctx)
if !ok { if !ok {
return nil return nil
} }
@ -193,7 +197,7 @@ func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string)
} }
func removeSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error { func removeSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error {
lid, ok := leases.Lease(ctx) lid, ok := leases.FromContext(ctx)
if !ok { if !ok {
return nil return nil
} }
@ -213,7 +217,7 @@ func removeSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key stri
} }
func addContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error { func addContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error {
lid, ok := leases.Lease(ctx) lid, ok := leases.FromContext(ctx)
if !ok { if !ok {
return nil return nil
} }
@ -237,7 +241,7 @@ func addContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error
} }
func removeContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error { func removeContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) error {
lid, ok := leases.Lease(ctx) lid, ok := leases.FromContext(ctx)
if !ok { if !ok {
return nil return nil
} }
@ -255,3 +259,51 @@ func removeContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) er
return bkt.Delete([]byte(dgst.String())) return bkt.Delete([]byte(dgst.String()))
} }
func addIngestLease(ctx context.Context, tx *bolt.Tx, ref string) (bool, error) {
lid, ok := leases.FromContext(ctx)
if !ok {
return false, nil
}
namespace, ok := namespaces.Namespace(ctx)
if !ok {
panic("namespace must already be required")
}
bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid))
if bkt == nil {
return false, errors.Wrap(errdefs.ErrNotFound, "lease does not exist")
}
bkt, err := bkt.CreateBucketIfNotExists(bucketKeyObjectIngests)
if err != nil {
return false, err
}
if err := bkt.Put([]byte(ref), nil); err != nil {
return false, err
}
return true, nil
}
func removeIngestLease(ctx context.Context, tx *bolt.Tx, ref string) error {
lid, ok := leases.FromContext(ctx)
if !ok {
return nil
}
namespace, ok := namespaces.Namespace(ctx)
if !ok {
panic("namespace must already be checked")
}
bkt := getBucket(tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases, []byte(lid), bucketKeyObjectIngests)
if bkt == nil {
// Key does not exist so we return nil
return nil
}
return bkt.Delete([]byte(ref))
}

View File

@ -23,13 +23,14 @@ import (
"github.com/containerd/cgroups" "github.com/containerd/cgroups"
eventstypes "github.com/containerd/containerd/api/events" eventstypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/linux" "github.com/containerd/containerd/runtime/v1/linux"
metrics "github.com/docker/go-metrics" metrics "github.com/docker/go-metrics"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
@ -80,16 +81,21 @@ type cgroupsMonitor struct {
} }
func (m *cgroupsMonitor) Monitor(c runtime.Task) error { func (m *cgroupsMonitor) Monitor(c runtime.Task) error {
info := c.Info() if err := m.collector.Add(c); err != nil {
t := c.(*linux.Task) return err
}
t, ok := c.(*linux.Task)
if !ok {
return nil
}
cg, err := t.Cgroup() cg, err := t.Cgroup()
if err != nil { if err != nil {
if errdefs.IsNotFound(err) {
return nil
}
return err return err
} }
if err := m.collector.Add(info.ID, info.Namespace, cg); err != nil { err = m.oom.Add(c.ID(), c.Namespace(), cg, m.trigger)
return err
}
err = m.oom.Add(info.ID, info.Namespace, cg, m.trigger)
if err == cgroups.ErrMemoryNotSupported { if err == cgroups.ErrMemoryNotSupported {
logrus.WithError(err).Warn("OOM monitoring failed") logrus.WithError(err).Warn("OOM monitoring failed")
return nil return nil
@ -98,17 +104,7 @@ func (m *cgroupsMonitor) Monitor(c runtime.Task) error {
} }
func (m *cgroupsMonitor) Stop(c runtime.Task) error { func (m *cgroupsMonitor) Stop(c runtime.Task) error {
info := c.Info() m.collector.Remove(c)
t := c.(*linux.Task)
cgroup, err := t.Cgroup()
if err != nil {
log.G(m.context).WithError(err).Warnf("unable to retrieve cgroup on stop")
} else {
m.collector.collect(info.ID, info.Namespace, cgroup, m.collector.storedMetrics, false, nil)
}
m.collector.Remove(info.ID, info.Namespace)
return nil return nil
} }

View File

@ -19,12 +19,16 @@
package cgroups package cgroups
import ( import (
"context"
"errors" "errors"
"fmt" "fmt"
"sync" "sync"
"github.com/containerd/cgroups" "github.com/containerd/cgroups"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/runtime"
"github.com/containerd/typeurl"
metrics "github.com/docker/go-metrics" metrics "github.com/docker/go-metrics"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
@ -49,7 +53,7 @@ func newCollector(ns *metrics.Namespace) *collector {
// add machine cpus and memory info // add machine cpus and memory info
c := &collector{ c := &collector{
ns: ns, ns: ns,
cgroups: make(map[string]*task), tasks: make(map[string]runtime.Task),
} }
c.metrics = append(c.metrics, pidMetrics...) c.metrics = append(c.metrics, pidMetrics...)
c.metrics = append(c.metrics, cpuMetrics...) c.metrics = append(c.metrics, cpuMetrics...)
@ -61,12 +65,6 @@ func newCollector(ns *metrics.Namespace) *collector {
return c return c
} }
type task struct {
id string
namespace string
cgroup cgroups.Cgroup
}
func taskID(id, namespace string) string { func taskID(id, namespace string) string {
return fmt.Sprintf("%s-%s", id, namespace) return fmt.Sprintf("%s-%s", id, namespace)
} }
@ -76,7 +74,7 @@ func taskID(id, namespace string) string {
type collector struct { type collector struct {
mu sync.RWMutex mu sync.RWMutex
cgroups map[string]*task tasks map[string]runtime.Task
ns *metrics.Namespace ns *metrics.Namespace
metrics []*metric metrics []*metric
storedMetrics chan prometheus.Metric storedMetrics chan prometheus.Metric
@ -91,9 +89,9 @@ func (c *collector) Describe(ch chan<- *prometheus.Desc) {
func (c *collector) Collect(ch chan<- prometheus.Metric) { func (c *collector) Collect(ch chan<- prometheus.Metric) {
c.mu.RLock() c.mu.RLock()
wg := &sync.WaitGroup{} wg := &sync.WaitGroup{}
for _, t := range c.cgroups { for _, t := range c.tasks {
wg.Add(1) wg.Add(1)
go c.collect(t.id, t.namespace, t.cgroup, ch, true, wg) go c.collect(t, ch, true, wg)
} }
storedLoop: storedLoop:
for { for {
@ -109,45 +107,52 @@ storedLoop:
wg.Wait() wg.Wait()
} }
func (c *collector) collect(id, namespace string, cg cgroups.Cgroup, ch chan<- prometheus.Metric, block bool, wg *sync.WaitGroup) { func (c *collector) collect(t runtime.Task, ch chan<- prometheus.Metric, block bool, wg *sync.WaitGroup) {
if wg != nil { if wg != nil {
defer wg.Done() defer wg.Done()
} }
ctx := namespaces.WithNamespace(context.Background(), t.Namespace())
stats, err := cg.Stat(cgroups.IgnoreNotExist) stats, err := t.Stats(ctx)
if err != nil { if err != nil {
log.L.WithError(err).Errorf("stat cgroup %s", id) log.L.WithError(err).Errorf("stat task %s", t.ID())
return
}
data, err := typeurl.UnmarshalAny(stats)
if err != nil {
log.L.WithError(err).Errorf("unmarshal stats for %s", t.ID())
return
}
s, ok := data.(*cgroups.Metrics)
if !ok {
log.L.WithError(err).Errorf("invalid metric type for %s", t.ID())
return return
} }
for _, m := range c.metrics { for _, m := range c.metrics {
m.collect(id, namespace, stats, c.ns, ch, block) m.collect(t.ID(), t.Namespace(), s, c.ns, ch, block)
} }
} }
// Add adds the provided cgroup and id so that metrics are collected and exported // Add adds the provided cgroup and id so that metrics are collected and exported
func (c *collector) Add(id, namespace string, cg cgroups.Cgroup) error { func (c *collector) Add(t runtime.Task) error {
if c.ns == nil { if c.ns == nil {
return nil return nil
} }
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
if _, ok := c.cgroups[taskID(id, namespace)]; ok { id := taskID(t.ID(), t.Namespace())
if _, ok := c.tasks[id]; ok {
return ErrAlreadyCollected return ErrAlreadyCollected
} }
c.cgroups[taskID(id, namespace)] = &task{ c.tasks[id] = t
id: id,
namespace: namespace,
cgroup: cg,
}
return nil return nil
} }
// Remove removes the provided cgroup by id from the collector // Remove removes the provided cgroup by id from the collector
func (c *collector) Remove(id, namespace string) { func (c *collector) Remove(t runtime.Task) {
if c.ns == nil { if c.ns == nil {
return return
} }
c.mu.Lock() c.mu.Lock()
defer c.mu.Unlock() defer c.mu.Unlock()
delete(c.cgroups, taskID(id, namespace)) delete(c.tasks, taskID(t.ID(), t.Namespace()))
} }

View File

@ -116,7 +116,8 @@ func (o *oomCollector) start() {
if err == unix.EINTR { if err == unix.EINTR {
continue continue
} }
logrus.WithField("error", err).Fatal("cgroups: epoll wait") logrus.WithError(err).Error("cgroups: epoll wait failed, OOM notifications disabled")
return
} }
for i := 0; i < n; i++ { for i := 0; i < n; i++ {
o.process(uintptr(events[i].Fd), events[i].Events) o.process(uintptr(events[i].Fd), events[i].Events)

View File

@ -39,10 +39,10 @@ func SetTempMountLocation(root string) error {
} }
// CleanupTempMounts all temp mounts and remove the directories // CleanupTempMounts all temp mounts and remove the directories
func CleanupTempMounts(flags int) error { func CleanupTempMounts(flags int) (warnings []error, err error) {
mounts, err := Self() mounts, err := Self()
if err != nil { if err != nil {
return err return nil, err
} }
var toUnmount []string var toUnmount []string
for _, m := range mounts { for _, m := range mounts {
@ -53,11 +53,12 @@ func CleanupTempMounts(flags int) error {
sort.Sort(sort.Reverse(sort.StringSlice(toUnmount))) sort.Sort(sort.Reverse(sort.StringSlice(toUnmount)))
for _, path := range toUnmount { for _, path := range toUnmount {
if err := UnmountAll(path, flags); err != nil { if err := UnmountAll(path, flags); err != nil {
return err warnings = append(warnings, err)
continue
} }
if err := os.Remove(path); err != nil { if err := os.Remove(path); err != nil {
return err warnings = append(warnings, err)
} }
} }
return nil return warnings, nil
} }

View File

@ -24,6 +24,6 @@ func SetTempMountLocation(root string) error {
} }
// CleanupTempMounts all temp mounts and remove the directories // CleanupTempMounts all temp mounts and remove the directories
func CleanupTempMounts(flags int) error { func CleanupTempMounts(flags int) ([]error, error) {
return nil return nil, nil
} }

View File

@ -34,10 +34,23 @@ func GenerateSpec(ctx context.Context, client Client, c *containers.Container, o
if err != nil { if err != nil {
return nil, err return nil, err
} }
return s, ApplyOpts(ctx, client, c, s, opts...)
}
// ApplyOpts applys the options to the given spec, injecting data from the
// context, client and container instance.
func ApplyOpts(ctx context.Context, client Client, c *containers.Container, s *Spec, opts ...SpecOpts) error {
for _, o := range opts { for _, o := range opts {
if err := o(ctx, client, c, s); err != nil { if err := o(ctx, client, c, s); err != nil {
return nil, err return err
} }
} }
return s, nil
return nil
}
func createDefaultSpec(ctx context.Context, id string) (*Spec, error) {
var s Spec
return &s, populateDefaultSpec(ctx, &s, id)
} }

View File

@ -18,10 +18,13 @@ package oci
import ( import (
"context" "context"
"encoding/json"
"io/ioutil"
"strings" "strings"
"github.com/containerd/containerd/containers" "github.com/containerd/containerd/containers"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
) )
// SpecOpts sets spec specific information to a newly generated OCI spec // SpecOpts sets spec specific information to a newly generated OCI spec
@ -46,6 +49,38 @@ func setProcess(s *Spec) {
} }
} }
// WithDefaultSpec returns a SpecOpts that will populate the spec with default
// values.
//
// Use as the first option to clear the spec, then apply options afterwards.
func WithDefaultSpec() SpecOpts {
return func(ctx context.Context, _ Client, c *containers.Container, s *Spec) error {
return populateDefaultSpec(ctx, s, c.ID)
}
}
// WithSpecFromBytes loads the the spec from the provided byte slice.
func WithSpecFromBytes(p []byte) SpecOpts {
return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
*s = Spec{} // make sure spec is cleared.
if err := json.Unmarshal(p, s); err != nil {
return errors.Wrapf(err, "decoding spec config file failed, current supported OCI runtime-spec : v%s", specs.Version)
}
return nil
}
}
// WithSpecFromFile loads the specification from the provided filename.
func WithSpecFromFile(filename string) SpecOpts {
return func(ctx context.Context, c Client, container *containers.Container, s *Spec) error {
p, err := ioutil.ReadFile(filename)
if err != nil {
return errors.Wrap(err, "cannot load spec config file")
}
return WithSpecFromBytes(p)(ctx, c, container, s)
}
}
// WithProcessArgs replaces the args on the generated spec // WithProcessArgs replaces the args on the generated spec
func WithProcessArgs(args ...string) SpecOpts { func WithProcessArgs(args ...string) SpecOpts {
return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error { return func(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {

View File

@ -106,6 +106,12 @@ func WithLinuxNamespace(ns specs.LinuxNamespace) SpecOpts {
// 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)
}
// WithImageConfigArgs configures the spec to from the configuration of an Image with additional args that
// replaces the CMD of the image
func WithImageConfigArgs(image Image, args []string) SpecOpts {
return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error { return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
ic, err := image.Config(ctx) ic, err := image.Config(ctx)
if err != nil { if err != nil {
@ -133,6 +139,9 @@ func WithImageConfig(image Image) SpecOpts {
setProcess(s) setProcess(s)
s.Process.Env = append(s.Process.Env, config.Env...) s.Process.Env = append(s.Process.Env, config.Env...)
cmd := config.Cmd cmd := config.Cmd
if len(args) > 0 {
cmd = args
}
s.Process.Args = append(config.Entrypoint, cmd...) s.Process.Args = append(config.Entrypoint, cmd...)
cwd := config.WorkingDir cwd := config.WorkingDir
if cwd == "" { if cwd == "" {
@ -348,8 +357,8 @@ func WithUIDGID(uid, gid uint32) SpecOpts {
// WithUserID sets the correct UID and GID for the container based // WithUserID sets the correct UID and GID for the container based
// on the image's /etc/passwd contents. If /etc/passwd does not exist, // on the image's /etc/passwd contents. If /etc/passwd does not exist,
// or uid is not found in /etc/passwd, it sets gid to be the same with // or uid is not found in /etc/passwd, it sets the requested uid,
// uid, and not returns error. // additionally sets the gid to 0, and does not return an error.
func WithUserID(uid uint32) SpecOpts { func WithUserID(uid uint32) SpecOpts {
return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) { return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) {
setProcess(s) setProcess(s)
@ -362,7 +371,7 @@ func WithUserID(uid uint32) SpecOpts {
}) })
if err != nil { if err != nil {
if os.IsNotExist(err) || err == errNoUsersFound { if os.IsNotExist(err) || err == errNoUsersFound {
s.Process.User.UID, s.Process.User.GID = uid, uid s.Process.User.UID, s.Process.User.GID = uid, 0
return nil return nil
} }
return err return err
@ -388,7 +397,7 @@ func WithUserID(uid uint32) SpecOpts {
}) })
if err != nil { if err != nil {
if os.IsNotExist(err) || err == errNoUsersFound { if os.IsNotExist(err) || err == errNoUsersFound {
s.Process.User.UID, s.Process.User.GID = uid, uid s.Process.User.UID, s.Process.User.GID = uid, 0
return nil return nil
} }
return err return err

View File

@ -76,12 +76,13 @@ func defaultNamespaces() []specs.LinuxNamespace {
} }
} }
func createDefaultSpec(ctx context.Context, id string) (*Spec, error) { func populateDefaultSpec(ctx context.Context, s *Spec, id string) error {
ns, err := namespaces.NamespaceRequired(ctx) ns, err := namespaces.NamespaceRequired(ctx)
if err != nil { if err != nil {
return nil, err return err
} }
s := &Spec{
*s = Spec{
Version: specs.Version, Version: specs.Version,
Root: &specs.Root{ Root: &specs.Root{
Path: defaultRootfsPath, Path: defaultRootfsPath,
@ -183,5 +184,5 @@ func createDefaultSpec(ctx context.Context, id string) (*Spec, error) {
Namespaces: defaultNamespaces(), Namespaces: defaultNamespaces(),
}, },
} }
return s, nil return nil
} }

View File

@ -22,8 +22,8 @@ import (
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
) )
func createDefaultSpec(ctx context.Context, id string) (*Spec, error) { func populateDefaultSpec(ctx context.Context, s *Spec, id string) error {
return &Spec{ *s = Spec{
Version: specs.Version, Version: specs.Version,
Root: &specs.Root{}, Root: &specs.Root{},
Process: &specs.Process{ Process: &specs.Process{
@ -39,5 +39,6 @@ func createDefaultSpec(ctx context.Context, id string) (*Spec, error) {
AllowUnqualifiedDNSQuery: true, AllowUnqualifiedDNSQuery: true,
}, },
}, },
}, nil }
return nil
} }

View File

@ -58,6 +58,8 @@ const (
InternalPlugin Type = "io.containerd.internal.v1" InternalPlugin Type = "io.containerd.internal.v1"
// RuntimePlugin implements a runtime // RuntimePlugin implements a runtime
RuntimePlugin Type = "io.containerd.runtime.v1" RuntimePlugin Type = "io.containerd.runtime.v1"
// RuntimePluginV2 implements a runtime v2
RuntimePluginV2 Type = "io.containerd.runtime.v2"
// ServicePlugin implements a internal service // ServicePlugin implements a internal service
ServicePlugin Type = "io.containerd.service.v1" ServicePlugin Type = "io.containerd.service.v1"
// GRPCPlugin implements a grpc service // GRPCPlugin implements a grpc service

View File

@ -18,7 +18,6 @@ package schema1
import ( import (
"bytes" "bytes"
"compress/gzip"
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
@ -31,6 +30,7 @@ import (
"golang.org/x/sync/errgroup" "golang.org/x/sync/errgroup"
"github.com/containerd/containerd/archive/compression"
"github.com/containerd/containerd/content" "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"
@ -257,6 +257,7 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
var ( var (
ref = remotes.MakeRefKey(ctx, desc) ref = remotes.MakeRefKey(ctx, desc)
calc = newBlobStateCalculator() calc = newBlobStateCalculator()
compressMethod = compression.Gzip
) )
// size may be unknown, set to zero for content ingest // size may be unknown, set to zero for content ingest
@ -280,13 +281,14 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
} }
defer ra.Close() defer ra.Close()
gr, err := gzip.NewReader(content.NewReader(ra)) r, err := compression.DecompressStream(content.NewReader(ra))
if err != nil { if err != nil {
return err return err
} }
defer gr.Close()
_, err = io.Copy(calc, gr) compressMethod = r.GetCompression()
_, err = io.Copy(calc, r)
r.Close()
if err != nil { if err != nil {
return err return err
} }
@ -303,13 +305,14 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
pr, pw := io.Pipe() pr, pw := io.Pipe()
eg.Go(func() error { eg.Go(func() error {
gr, err := gzip.NewReader(pr) r, err := compression.DecompressStream(pr)
if err != nil { if err != nil {
return err return err
} }
defer gr.Close()
_, err = io.Copy(calc, gr) compressMethod = r.GetCompression()
_, err = io.Copy(calc, r)
r.Close()
pr.CloseWithError(err) pr.CloseWithError(err)
return err return err
}) })
@ -333,6 +336,11 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
desc.Size = info.Size desc.Size = info.Size
} }
if compressMethod == compression.Uncompressed {
log.G(ctx).WithField("id", desc.Digest).Debugf("changed media type for uncompressed schema1 layer blob")
desc.MediaType = images.MediaTypeDockerSchema2Layer
}
state := calc.State() state := calc.State()
c.mu.Lock() c.mu.Lock()
@ -342,6 +350,7 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
return nil return nil
} }
func (c *Converter) schema1ManifestHistory() ([]ocispec.History, []digest.Digest, error) { func (c *Converter) schema1ManifestHistory() ([]ocispec.History, []digest.Digest, error) {
if c.pulledManifest == nil { if c.pulledManifest == nil {
return nil, nil, errors.New("missing schema 1 manifest for conversion") return nil, nil, errors.New("missing schema 1 manifest for conversion")

View File

@ -18,9 +18,9 @@ package rootfs
import ( import (
"context" "context"
"crypto/rand"
"encoding/base64" "encoding/base64"
"fmt" "fmt"
"math/rand"
"time" "time"
"github.com/containerd/containerd/diff" "github.com/containerd/containerd/diff"

View File

@ -42,8 +42,12 @@ type CreateOpts struct {
IO IO IO IO
// Checkpoint digest to restore container state // Checkpoint digest to restore container state
Checkpoint string Checkpoint string
// Options for the runtime and container // RuntimeOptions for the runtime
Options *types.Any RuntimeOptions *types.Any
// TaskOptions received for the task
TaskOptions *types.Any
// Runtime to use
Runtime string
} }
// Exit information for a process // Exit information for a process
@ -64,7 +68,5 @@ type PlatformRuntime interface {
Get(context.Context, string) (Task, error) Get(context.Context, string) (Task, error)
// Tasks returns all the current tasks for the runtime. // Tasks returns all the current tasks for the runtime.
// Any container runs at most one task at a time. // Any container runs at most one task at a time.
Tasks(context.Context) ([]Task, error) Tasks(context.Context, bool) ([]Task, error)
// Delete removes the task in the runtime.
Delete(context.Context, Task) (*Exit, error)
} }

View File

@ -46,14 +46,16 @@ type Process interface {
Start(context.Context) error Start(context.Context) error
// Wait for the process to exit // Wait for the process to exit
Wait(context.Context) (*Exit, error) Wait(context.Context) (*Exit, error)
// Delete deletes the process
Delete(context.Context) (*Exit, error)
} }
// Task is the runtime object for an executing container // Task is the runtime object for an executing container
type Task interface { type Task interface {
Process Process
// Information of the container // Namespace that the task exists in
Info() TaskInfo Namespace() string
// Pause pauses the container process // Pause pauses the container process
Pause(context.Context) error Pause(context.Context) error
// Resume unpauses the container process // Resume unpauses the container process
@ -64,14 +66,12 @@ type Task interface {
Pids(context.Context) ([]ProcessInfo, error) Pids(context.Context) ([]ProcessInfo, error)
// Checkpoint checkpoints a container to an image with live system data // Checkpoint checkpoints a container to an image with live system data
Checkpoint(context.Context, string, *types.Any) error Checkpoint(context.Context, string, *types.Any) error
// DeleteProcess deletes a specific exec process via its id
DeleteProcess(context.Context, string) (*Exit, error)
// Update sets the provided resources to a running task // Update sets the provided resources to a running task
Update(context.Context, *types.Any) error Update(context.Context, *types.Any) error
// Process returns a process within the task for the provided id // Process returns a process within the task for the provided id
Process(context.Context, string) (Process, error) Process(context.Context, string) (Process, error)
// Metrics returns runtime specific metrics for a task // Stats returns runtime specific metrics for a task
Metrics(context.Context) (interface{}, error) Stats(context.Context) (*types.Any, error)
} }
// ExecOpts provides additional options for additional processes running in a task // ExecOpts provides additional options for additional processes running in a task

View File

@ -64,14 +64,22 @@ func (l *TaskList) Get(ctx context.Context, id string) (Task, error) {
} }
// GetAll tasks under a namespace // GetAll tasks under a namespace
func (l *TaskList) GetAll(ctx context.Context) ([]Task, error) { func (l *TaskList) GetAll(ctx context.Context, noNS bool) ([]Task, error) {
l.mu.Lock() l.mu.Lock()
defer l.mu.Unlock() defer l.mu.Unlock()
var o []Task
if noNS {
for ns := range l.tasks {
for _, t := range l.tasks[ns] {
o = append(o, t)
}
}
return o, nil
}
namespace, err := namespaces.NamespaceRequired(ctx) namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var o []Task
tasks, ok := l.tasks[namespace] tasks, ok := l.tasks[namespace]
if !ok { if !ok {
return o, nil return o, nil

View File

@ -26,8 +26,8 @@ import (
"github.com/containerd/containerd/events/exchange" "github.com/containerd/containerd/events/exchange"
"github.com/containerd/containerd/runtime/linux/runctypes" "github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/shim" "github.com/containerd/containerd/runtime/v1/shim"
"github.com/containerd/containerd/runtime/shim/client" "github.com/containerd/containerd/runtime/v1/shim/client"
"github.com/pkg/errors" "github.com/pkg/errors"
) )

View File

@ -89,7 +89,7 @@ func (e *execProcess) ExitedAt() time.Time {
func (e *execProcess) setExited(status int) { func (e *execProcess) setExited(status int) {
e.status = status e.status = status
e.exited = time.Now() e.exited = time.Now()
e.parent.platform.ShutdownConsole(context.Background(), e.console) e.parent.Platform.ShutdownConsole(context.Background(), e.console)
close(e.waitBlock) close(e.waitBlock)
} }
@ -180,7 +180,7 @@ func (e *execProcess) start(ctx context.Context) (err error) {
if err != nil { if err != nil {
return errors.Wrap(err, "failed to retrieve console master") return errors.Wrap(err, "failed to retrieve console master")
} }
if e.console, err = e.parent.platform.CopyConsole(ctx, console, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, &copyWaitGroup); err != nil { if e.console, err = e.parent.Platform.CopyConsole(ctx, console, e.stdio.Stdin, e.stdio.Stdout, e.stdio.Stderr, &e.wg, &copyWaitGroup); err != nil {
return errors.Wrap(err, "failed to start console copy") return errors.Wrap(err, "failed to start console copy")
} }
} else if !e.stdio.IsNull() { } else if !e.stdio.IsNull() {

View File

@ -33,11 +33,9 @@ import (
"github.com/containerd/console" "github.com/containerd/console"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/proc" "github.com/containerd/containerd/runtime/proc"
"github.com/containerd/fifo" "github.com/containerd/fifo"
runc "github.com/containerd/go-runc" runc "github.com/containerd/go-runc"
"github.com/containerd/typeurl"
google_protobuf "github.com/gogo/protobuf/types" google_protobuf "github.com/gogo/protobuf/types"
specs "github.com/opencontainers/runtime-spec/specs-go" specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors" "github.com/pkg/errors"
@ -59,12 +57,12 @@ type Init struct {
waitBlock chan struct{} waitBlock chan struct{}
workDir string WorkDir string
id string id string
bundle string Bundle string
console console.Console console console.Console
platform proc.Platform Platform proc.Platform
io runc.IO io runc.IO
runtime *runc.Runc runtime *runc.Runc
status int status int
@ -73,9 +71,11 @@ type Init struct {
closers []io.Closer closers []io.Closer
stdin io.Closer stdin io.Closer
stdio proc.Stdio stdio proc.Stdio
rootfs string Rootfs string
IoUID int IoUID int
IoGID int IoGID int
NoPivotRoot bool
NoNewKeyring bool
} }
// NewRunc returns a new runc instance for a process // NewRunc returns a new runc instance for a process
@ -94,90 +94,50 @@ func NewRunc(root, path, namespace, runtime, criu string, systemd bool) *runc.Ru
} }
} }
// New returns a new init process // New returns a new process
func New(context context.Context, path, workDir, runtimeRoot, namespace, criu string, systemdCgroup bool, platform proc.Platform, r *CreateConfig) (*Init, error) { func New(id string, runtime *runc.Runc, stdio proc.Stdio) *Init {
var success bool
var options runctypes.CreateOptions
if r.Options != nil {
v, err := typeurl.UnmarshalAny(r.Options)
if err != nil {
return nil, err
}
options = *v.(*runctypes.CreateOptions)
}
rootfs := filepath.Join(path, "rootfs")
// count the number of successful mounts so we can undo
// what was actually done rather than what should have been
// done.
defer func() {
if success {
return
}
if err2 := mount.UnmountAll(rootfs, 0); err2 != nil {
log.G(context).WithError(err2).Warn("Failed to cleanup rootfs mount")
}
}()
for _, rm := range r.Rootfs {
m := &mount.Mount{
Type: rm.Type,
Source: rm.Source,
Options: rm.Options,
}
if err := m.Mount(rootfs); err != nil {
return nil, errors.Wrapf(err, "failed to mount rootfs component %v", m)
}
}
runtime := NewRunc(runtimeRoot, path, namespace, r.Runtime, criu, systemdCgroup)
p := &Init{ p := &Init{
id: r.ID, id: id,
bundle: r.Bundle,
runtime: runtime, runtime: runtime,
platform: platform, stdio: stdio,
stdio: proc.Stdio{
Stdin: r.Stdin,
Stdout: r.Stdout,
Stderr: r.Stderr,
Terminal: r.Terminal,
},
rootfs: rootfs,
workDir: workDir,
status: 0, status: 0,
waitBlock: make(chan struct{}), waitBlock: make(chan struct{}),
IoUID: int(options.IoUid),
IoGID: int(options.IoGid),
} }
p.initState = &createdState{p: p} p.initState = &createdState{p: p}
return p
}
// Create the process with the provided config
func (p *Init) Create(ctx context.Context, r *CreateConfig) error {
var ( var (
err error err error
socket *runc.Socket socket *runc.Socket
) )
if r.Terminal { if r.Terminal {
if socket, err = runc.NewTempConsoleSocket(); err != nil { if socket, err = runc.NewTempConsoleSocket(); err != nil {
return nil, errors.Wrap(err, "failed to create OCI runtime console socket") return errors.Wrap(err, "failed to create OCI runtime console socket")
} }
defer socket.Close() defer socket.Close()
} else if hasNoIO(r) { } else if hasNoIO(r) {
if p.io, err = runc.NewNullIO(); err != nil { if p.io, err = runc.NewNullIO(); err != nil {
return nil, errors.Wrap(err, "creating new NULL IO") return errors.Wrap(err, "creating new NULL IO")
} }
} else { } else {
if p.io, err = runc.NewPipeIO(int(options.IoUid), int(options.IoGid)); err != nil { if p.io, err = runc.NewPipeIO(p.IoUID, p.IoGID); err != nil {
return nil, errors.Wrap(err, "failed to create OCI runtime io pipes") return errors.Wrap(err, "failed to create OCI runtime io pipes")
} }
} }
pidFile := filepath.Join(path, InitPidFile) pidFile := filepath.Join(p.Bundle, InitPidFile)
if r.Checkpoint != "" { if r.Checkpoint != "" {
opts := &runc.RestoreOpts{ opts := &runc.RestoreOpts{
CheckpointOpts: runc.CheckpointOpts{ CheckpointOpts: runc.CheckpointOpts{
ImagePath: r.Checkpoint, ImagePath: r.Checkpoint,
WorkDir: p.workDir, WorkDir: p.WorkDir,
ParentPath: r.ParentCheckpoint, ParentPath: r.ParentCheckpoint,
}, },
PidFile: pidFile, PidFile: pidFile,
IO: p.io, IO: p.io,
NoPivot: options.NoPivotRoot, NoPivot: p.NoPivotRoot,
Detach: true, Detach: true,
NoSubreaper: true, NoSubreaper: true,
} }
@ -185,25 +145,24 @@ func New(context context.Context, path, workDir, runtimeRoot, namespace, criu st
p: p, p: p,
opts: opts, opts: opts,
} }
success = true return nil
return p, nil
} }
opts := &runc.CreateOpts{ opts := &runc.CreateOpts{
PidFile: pidFile, PidFile: pidFile,
IO: p.io, IO: p.io,
NoPivot: options.NoPivotRoot, NoPivot: p.NoPivotRoot,
NoNewKeyring: options.NoNewKeyring, NoNewKeyring: p.NoNewKeyring,
} }
if socket != nil { if socket != nil {
opts.ConsoleSocket = socket opts.ConsoleSocket = socket
} }
if err := p.runtime.Create(context, r.ID, r.Bundle, opts); err != nil { if err := p.runtime.Create(ctx, r.ID, r.Bundle, opts); err != nil {
return nil, p.runtimeError(err, "OCI runtime create failed") return p.runtimeError(err, "OCI runtime create failed")
} }
if r.Stdin != "" { if r.Stdin != "" {
sc, err := fifo.OpenFifo(context, r.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0) sc, err := fifo.OpenFifo(ctx, r.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to open stdin fifo %s", r.Stdin) return errors.Wrapf(err, "failed to open stdin fifo %s", r.Stdin)
} }
p.stdin = sc p.stdin = sc
p.closers = append(p.closers, sc) p.closers = append(p.closers, sc)
@ -212,27 +171,26 @@ func New(context context.Context, path, workDir, runtimeRoot, namespace, criu st
if socket != nil { if socket != nil {
console, err := socket.ReceiveMaster() console, err := socket.ReceiveMaster()
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to retrieve console master") return errors.Wrap(err, "failed to retrieve console master")
} }
console, err = platform.CopyConsole(context, console, r.Stdin, r.Stdout, r.Stderr, &p.wg, &copyWaitGroup) console, err = p.Platform.CopyConsole(ctx, console, r.Stdin, r.Stdout, r.Stderr, &p.wg, &copyWaitGroup)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to start console copy") return errors.Wrap(err, "failed to start console copy")
} }
p.console = console p.console = console
} else if !hasNoIO(r) { } else if !hasNoIO(r) {
if err := copyPipes(context, p.io, r.Stdin, r.Stdout, r.Stderr, &p.wg, &copyWaitGroup); err != nil { if err := copyPipes(ctx, p.io, r.Stdin, r.Stdout, r.Stderr, &p.wg, &copyWaitGroup); err != nil {
return nil, errors.Wrap(err, "failed to start io pipe copy") return errors.Wrap(err, "failed to start io pipe copy")
} }
} }
copyWaitGroup.Wait() copyWaitGroup.Wait()
pid, err := runc.ReadPidFile(pidFile) pid, err := runc.ReadPidFile(pidFile)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to retrieve OCI runtime container pid") return errors.Wrap(err, "failed to retrieve OCI runtime container pid")
} }
p.pid = pid p.pid = pid
success = true return nil
return p, nil
} }
// Wait for the process to exit // Wait for the process to exit
@ -286,7 +244,7 @@ func (p *Init) start(context context.Context) error {
func (p *Init) setExited(status int) { func (p *Init) setExited(status int) {
p.exited = time.Now() p.exited = time.Now()
p.status = status p.status = status
p.platform.ShutdownConsole(context.Background(), p.console) p.Platform.ShutdownConsole(context.Background(), p.console)
close(p.waitBlock) close(p.waitBlock)
} }
@ -312,7 +270,7 @@ func (p *Init) delete(context context.Context) error {
} }
p.io.Close() p.io.Close()
} }
if err2 := mount.UnmountAll(p.rootfs, 0); err2 != nil { if err2 := mount.UnmountAll(p.Rootfs, 0); err2 != nil {
log.G(context).WithError(err2).Warn("failed to cleanup rootfs mount") log.G(context).WithError(err2).Warn("failed to cleanup rootfs mount")
if err == nil { if err == nil {
err = errors.Wrap(err2, "failed rootfs umount") err = errors.Wrap(err2, "failed rootfs umount")
@ -390,30 +348,22 @@ func (p *Init) exec(context context.Context, path string, r *ExecConfig) (proc.P
} }
func (p *Init) checkpoint(context context.Context, r *CheckpointConfig) error { func (p *Init) checkpoint(context context.Context, r *CheckpointConfig) error {
var options runctypes.CheckpointOptions
if r.Options != nil {
v, err := typeurl.UnmarshalAny(r.Options)
if err != nil {
return err
}
options = *v.(*runctypes.CheckpointOptions)
}
var actions []runc.CheckpointAction var actions []runc.CheckpointAction
if !options.Exit { if !r.Exit {
actions = append(actions, runc.LeaveRunning) actions = append(actions, runc.LeaveRunning)
} }
work := filepath.Join(p.workDir, "criu-work") work := filepath.Join(p.WorkDir, "criu-work")
defer os.RemoveAll(work) defer os.RemoveAll(work)
if err := p.runtime.Checkpoint(context, p.id, &runc.CheckpointOpts{ if err := p.runtime.Checkpoint(context, p.id, &runc.CheckpointOpts{
WorkDir: work, WorkDir: work,
ImagePath: r.Path, ImagePath: r.Path,
AllowOpenTCP: options.OpenTcp, AllowOpenTCP: r.AllowOpenTCP,
AllowExternalUnixSockets: options.ExternalUnixSockets, AllowExternalUnixSockets: r.AllowExternalUnixSockets,
AllowTerminal: options.Terminal, AllowTerminal: r.AllowTerminal,
FileLocks: options.FileLocks, FileLocks: r.FileLocks,
EmptyNamespaces: options.EmptyNamespaces, EmptyNamespaces: r.EmptyNamespaces,
}, actions...); err != nil { }, actions...); err != nil {
dumpLog := filepath.Join(p.bundle, "criu-dump.log") dumpLog := filepath.Join(p.Bundle, "criu-dump.log")
if cerr := copyFile(dumpLog, filepath.Join(work, "dump.log")); cerr != nil { if cerr := copyFile(dumpLog, filepath.Join(work, "dump.log")); cerr != nil {
log.G(context).Error(err) log.G(context).Error(err)
} }

View File

@ -209,7 +209,7 @@ func (s *createdCheckpointState) Start(ctx context.Context) error {
s.opts.ConsoleSocket = socket s.opts.ConsoleSocket = socket
} }
if _, err := s.p.runtime.Restore(ctx, p.id, p.bundle, s.opts); err != nil { if _, err := s.p.runtime.Restore(ctx, p.id, p.Bundle, s.opts); err != nil {
return p.runtimeError(err, "OCI runtime restore failed") return p.runtimeError(err, "OCI runtime restore failed")
} }
if sio.Stdin != "" { if sio.Stdin != "" {
@ -226,7 +226,7 @@ func (s *createdCheckpointState) Start(ctx context.Context) error {
if err != nil { if err != nil {
return errors.Wrap(err, "failed to retrieve console master") return errors.Wrap(err, "failed to retrieve console master")
} }
console, err = p.platform.CopyConsole(ctx, console, sio.Stdin, sio.Stdout, sio.Stderr, &p.wg, &copyWaitGroup) console, err = p.Platform.CopyConsole(ctx, console, sio.Stdin, sio.Stdout, sio.Stderr, &p.wg, &copyWaitGroup)
if err != nil { if err != nil {
return errors.Wrap(err, "failed to start console copy") return errors.Wrap(err, "failed to start console copy")
} }

View File

@ -56,5 +56,10 @@ type ExecConfig struct {
// CheckpointConfig holds task checkpoint configuration // CheckpointConfig holds task checkpoint configuration
type CheckpointConfig struct { type CheckpointConfig struct {
Path string Path string
Options *google_protobuf.Any Exit bool
AllowOpenTCP bool
AllowExternalUnixSockets bool
AllowTerminal bool
FileLocks bool
EmptyNamespaces []string
} }

View File

@ -25,7 +25,7 @@ import (
"github.com/containerd/containerd/api/types/task" "github.com/containerd/containerd/api/types/task"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime"
shim "github.com/containerd/containerd/runtime/shim/v1" shim "github.com/containerd/containerd/runtime/v1/shim/v1"
"github.com/containerd/ttrpc" "github.com/containerd/ttrpc"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -149,3 +149,18 @@ func (p *Process) Wait(ctx context.Context) (*runtime.Exit, error) {
Status: r.ExitStatus, Status: r.ExitStatus,
}, nil }, nil
} }
// Delete the process and return the exit status
func (p *Process) Delete(ctx context.Context) (*runtime.Exit, error) {
r, err := p.t.shim.DeleteProcess(ctx, &shim.DeleteProcessRequest{
ID: p.id,
})
if err != nil {
return nil, errdefs.FromGRPC(err)
}
return &runtime.Exit{
Status: r.ExitStatus,
Timestamp: r.ExitedAt,
Pid: r.Pid,
}, nil
}

View File

@ -40,9 +40,9 @@ import (
"github.com/containerd/containerd/platforms" "github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/linux/proc"
"github.com/containerd/containerd/runtime/linux/runctypes" "github.com/containerd/containerd/runtime/linux/runctypes"
shim "github.com/containerd/containerd/runtime/shim/v1" "github.com/containerd/containerd/runtime/v1/linux/proc"
shim "github.com/containerd/containerd/runtime/v1/shim/v1"
runc "github.com/containerd/go-runc" runc "github.com/containerd/go-runc"
"github.com/containerd/typeurl" "github.com/containerd/typeurl"
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
@ -69,7 +69,6 @@ func init() {
ID: "linux", ID: "linux",
InitFn: New, InitFn: New,
Requires: []plugin.Type{ Requires: []plugin.Type{
plugin.TaskMonitorPlugin,
plugin.MetadataPlugin, plugin.MetadataPlugin,
}, },
Config: &Config{ Config: &Config{
@ -105,10 +104,6 @@ func New(ic *plugin.InitContext) (interface{}, error) {
if err := os.MkdirAll(ic.State, 0711); err != nil { if err := os.MkdirAll(ic.State, 0711); err != nil {
return nil, err return nil, err
} }
monitor, err := ic.Get(plugin.TaskMonitorPlugin)
if err != nil {
return nil, err
}
m, err := ic.Get(plugin.MetadataPlugin) m, err := ic.Get(plugin.MetadataPlugin)
if err != nil { if err != nil {
return nil, err return nil, err
@ -117,7 +112,6 @@ func New(ic *plugin.InitContext) (interface{}, error) {
r := &Runtime{ r := &Runtime{
root: ic.Root, root: ic.Root,
state: ic.State, state: ic.State,
monitor: monitor.(runtime.TaskMonitor),
tasks: runtime.NewTaskList(), tasks: runtime.NewTaskList(),
db: m.(*metadata.DB), db: m.(*metadata.DB),
address: ic.Address, address: ic.Address,
@ -128,8 +122,6 @@ func New(ic *plugin.InitContext) (interface{}, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// TODO: need to add the tasks to the monitor
for _, t := range tasks { for _, t := range tasks {
if err := r.tasks.AddWithNamespace(t.namespace, t); err != nil { if err := r.tasks.AddWithNamespace(t.namespace, t); err != nil {
return nil, err return nil, err
@ -144,7 +136,6 @@ type Runtime struct {
state string state string
address string address string
monitor runtime.TaskMonitor
tasks *runtime.TaskList tasks *runtime.TaskList
db *metadata.DB db *metadata.DB
events *exchange.Exchange events *exchange.Exchange
@ -189,8 +180,8 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
shimopt := ShimLocal(r.config, r.events) shimopt := ShimLocal(r.config, r.events)
if !r.config.NoShim { if !r.config.NoShim {
var cgroup string var cgroup string
if opts.Options != nil { if opts.TaskOptions != nil {
v, err := typeurl.UnmarshalAny(opts.Options) v, err := typeurl.UnmarshalAny(opts.TaskOptions)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -205,14 +196,6 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
} }
lc := t.(*Task) lc := t.(*Task)
// Stop the monitor
if err := r.monitor.Stop(lc); err != nil {
log.G(ctx).WithError(err).WithFields(logrus.Fields{
"id": id,
"namespace": namespace,
}).Warn("failed to stop monitor")
}
log.G(ctx).WithFields(logrus.Fields{ log.G(ctx).WithFields(logrus.Fields{
"id": id, "id": id,
"namespace": namespace, "namespace": namespace,
@ -252,7 +235,7 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
Stderr: opts.IO.Stderr, Stderr: opts.IO.Stderr,
Terminal: opts.IO.Terminal, Terminal: opts.IO.Terminal,
Checkpoint: opts.Checkpoint, Checkpoint: opts.Checkpoint,
Options: opts.Options, Options: opts.TaskOptions,
} }
for _, m := range opts.Rootfs { for _, m := range opts.Rootfs {
sopts.Rootfs = append(sopts.Rootfs, &types.Mount{ sopts.Rootfs = append(sopts.Rootfs, &types.Mount{
@ -265,24 +248,14 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
if err != nil { if err != nil {
return nil, errdefs.FromGRPC(err) return nil, errdefs.FromGRPC(err)
} }
t, err := newTask(id, namespace, int(cr.Pid), s, r.monitor, r.events, t, err := newTask(id, namespace, int(cr.Pid), s, r.events,
proc.NewRunc(ropts.RuntimeRoot, sopts.Bundle, namespace, rt, ropts.CriuPath, ropts.SystemdCgroup)) proc.NewRunc(ropts.RuntimeRoot, sopts.Bundle, namespace, rt, ropts.CriuPath, ropts.SystemdCgroup), r.tasks, bundle)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := r.tasks.Add(ctx, t); err != nil { if err := r.tasks.Add(ctx, t); err != nil {
return nil, err return nil, err
} }
// after the task is created, add it to the monitor if it has a cgroup
// this can be different on a checkpoint/restore
if t.cg != nil {
if err = r.monitor.Monitor(t); err != nil {
if _, err := r.Delete(ctx, t); err != nil {
log.G(ctx).WithError(err).Error("deleting task after failed monitor")
}
return nil, err
}
}
r.events.Publish(ctx, runtime.TaskCreateEventTopic, &eventstypes.TaskCreate{ r.events.Publish(ctx, runtime.TaskCreateEventTopic, &eventstypes.TaskCreate{
ContainerID: sopts.ID, ContainerID: sopts.ID,
Bundle: sopts.Bundle, Bundle: sopts.Bundle,
@ -300,56 +273,9 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
return t, nil return t, nil
} }
// Delete a task removing all on disk state
func (r *Runtime) Delete(ctx context.Context, c runtime.Task) (*runtime.Exit, error) {
namespace, err := namespaces.NamespaceRequired(ctx)
if err != nil {
return nil, err
}
lc, ok := c.(*Task)
if !ok {
return nil, fmt.Errorf("task cannot be cast as *linux.Task")
}
if err := r.monitor.Stop(lc); err != nil {
return nil, err
}
bundle := loadBundle(
lc.id,
filepath.Join(r.state, namespace, lc.id),
filepath.Join(r.root, namespace, lc.id),
)
rsp, err := lc.shim.Delete(ctx, empty)
if err != nil {
if cerr := r.cleanupAfterDeadShim(ctx, bundle, namespace, c.ID(), lc.pid); cerr != nil {
log.G(ctx).WithError(err).Error("unable to cleanup task")
}
return nil, errdefs.FromGRPC(err)
}
r.tasks.Delete(ctx, lc.id)
if err := lc.shim.KillShim(ctx); err != nil {
log.G(ctx).WithError(err).Error("failed to kill shim")
}
if err := bundle.Delete(); err != nil {
log.G(ctx).WithError(err).Error("failed to delete bundle")
}
r.events.Publish(ctx, runtime.TaskDeleteEventTopic, &eventstypes.TaskDelete{
ContainerID: lc.id,
ExitStatus: rsp.ExitStatus,
ExitedAt: rsp.ExitedAt,
Pid: rsp.Pid,
})
return &runtime.Exit{
Status: rsp.ExitStatus,
Timestamp: rsp.ExitedAt,
Pid: rsp.Pid,
}, nil
}
// Tasks returns all tasks known to the runtime // Tasks returns all tasks known to the runtime
func (r *Runtime) Tasks(ctx context.Context) ([]runtime.Task, error) { func (r *Runtime) Tasks(ctx context.Context, all bool) ([]runtime.Task, error) {
return r.tasks.GetAll(ctx) return r.tasks.GetAll(ctx, all)
} }
func (r *Runtime) restoreTasks(ctx context.Context) ([]*Task, error) { func (r *Runtime) restoreTasks(ctx context.Context) ([]*Task, error) {
@ -422,8 +348,8 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) {
continue continue
} }
t, err := newTask(id, ns, pid, s, r.monitor, r.events, t, err := newTask(id, ns, pid, s, r.events,
proc.NewRunc(ropts.RuntimeRoot, bundle.path, ns, ropts.Runtime, ropts.CriuPath, ropts.SystemdCgroup)) proc.NewRunc(ropts.RuntimeRoot, bundle.path, ns, ropts.Runtime, ropts.CriuPath, ropts.SystemdCgroup), r.tasks, bundle)
if err != nil { if err != nil {
log.G(ctx).WithError(err).Error("loading task type") log.G(ctx).WithError(err).Error("loading task type")
continue continue

View File

@ -28,11 +28,13 @@ import (
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events/exchange" "github.com/containerd/containerd/events/exchange"
"github.com/containerd/containerd/identifiers" "github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/shim/client" "github.com/containerd/containerd/runtime/v1/shim/client"
shim "github.com/containerd/containerd/runtime/shim/v1" shim "github.com/containerd/containerd/runtime/v1/shim/v1"
runc "github.com/containerd/go-runc" runc "github.com/containerd/go-runc"
"github.com/containerd/ttrpc" "github.com/containerd/ttrpc"
"github.com/containerd/typeurl"
"github.com/gogo/protobuf/types" "github.com/gogo/protobuf/types"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -45,12 +47,12 @@ type Task struct {
shim *client.Client shim *client.Client
namespace string namespace string
cg cgroups.Cgroup cg cgroups.Cgroup
monitor runtime.TaskMonitor
events *exchange.Exchange events *exchange.Exchange
runtime *runc.Runc tasks *runtime.TaskList
bundle *bundle
} }
func newTask(id, namespace string, pid int, shim *client.Client, monitor runtime.TaskMonitor, events *exchange.Exchange, runtime *runc.Runc) (*Task, error) { func newTask(id, namespace string, pid int, shim *client.Client, events *exchange.Exchange, runtime *runc.Runc, list *runtime.TaskList, bundle *bundle) (*Task, error) {
var ( var (
err error err error
cg cgroups.Cgroup cg cgroups.Cgroup
@ -67,9 +69,9 @@ func newTask(id, namespace string, pid int, shim *client.Client, monitor runtime
shim: shim, shim: shim,
namespace: namespace, namespace: namespace,
cg: cg, cg: cg,
monitor: monitor,
events: events, events: events,
runtime: runtime, tasks: list,
bundle: bundle,
}, nil }, nil
} }
@ -78,13 +80,35 @@ func (t *Task) ID() string {
return t.id return t.id
} }
// Info returns task information about the runtime and namespace // Namespace of the task
func (t *Task) Info() runtime.TaskInfo { func (t *Task) Namespace() string {
return runtime.TaskInfo{ return t.namespace
ID: t.id,
Runtime: pluginID,
Namespace: t.namespace,
} }
// Delete the task and return the exit status
func (t *Task) Delete(ctx context.Context) (*runtime.Exit, error) {
rsp, err := t.shim.Delete(ctx, empty)
if err != nil {
return nil, errdefs.FromGRPC(err)
}
t.tasks.Delete(ctx, t.id)
if err := t.shim.KillShim(ctx); err != nil {
log.G(ctx).WithError(err).Error("failed to kill shim")
}
if err := t.bundle.Delete(); err != nil {
log.G(ctx).WithError(err).Error("failed to delete bundle")
}
t.events.Publish(ctx, runtime.TaskDeleteEventTopic, &eventstypes.TaskDelete{
ContainerID: t.id,
ExitStatus: rsp.ExitStatus,
ExitedAt: rsp.ExitedAt,
Pid: rsp.Pid,
})
return &runtime.Exit{
Status: rsp.ExitStatus,
Timestamp: rsp.ExitedAt,
Pid: rsp.Pid,
}, nil
} }
// Start the task // Start the task
@ -107,9 +131,6 @@ func (t *Task) Start(ctx context.Context) error {
t.mu.Lock() t.mu.Lock()
t.cg = cg t.cg = cg
t.mu.Unlock() t.mu.Unlock()
if err := t.monitor.Monitor(t); err != nil {
return err
}
} }
t.events.Publish(ctx, runtime.TaskStartEventTopic, &eventstypes.TaskStart{ t.events.Publish(ctx, runtime.TaskStartEventTopic, &eventstypes.TaskStart{
ContainerID: t.id, ContainerID: t.id,
@ -270,21 +291,6 @@ func (t *Task) Checkpoint(ctx context.Context, path string, options *types.Any)
return nil return nil
} }
// DeleteProcess removes the provided process from the task and deletes all on disk state
func (t *Task) DeleteProcess(ctx context.Context, id string) (*runtime.Exit, error) {
r, err := t.shim.DeleteProcess(ctx, &shim.DeleteProcessRequest{
ID: id,
})
if err != nil {
return nil, errdefs.FromGRPC(err)
}
return &runtime.Exit{
Status: r.ExitStatus,
Timestamp: r.ExitedAt,
Pid: r.Pid,
}, nil
}
// Update changes runtime information of a running task // Update changes runtime information of a running task
func (t *Task) Update(ctx context.Context, resources *types.Any) error { func (t *Task) Update(ctx context.Context, resources *types.Any) error {
if _, err := t.shim.Update(ctx, &shim.UpdateTaskRequest{ if _, err := t.shim.Update(ctx, &shim.UpdateTaskRequest{
@ -307,8 +313,8 @@ func (t *Task) Process(ctx context.Context, id string) (runtime.Process, error)
return p, nil return p, nil
} }
// Metrics returns runtime specific system level metric information for the task // Stats returns runtime specific system level metric information for the task
func (t *Task) Metrics(ctx context.Context) (interface{}, error) { func (t *Task) Stats(ctx context.Context) (*types.Any, error) {
t.mu.Lock() t.mu.Lock()
defer t.mu.Unlock() defer t.mu.Unlock()
if t.cg == nil { if t.cg == nil {
@ -318,7 +324,7 @@ func (t *Task) Metrics(ctx context.Context) (interface{}, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return stats, nil return typeurl.MarshalAny(stats)
} }
// Cgroup returns the underlying cgroup for a linux task // Cgroup returns the underlying cgroup for a linux task

View File

@ -37,8 +37,8 @@ import (
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/runtime/shim" "github.com/containerd/containerd/runtime/v1/shim"
shimapi "github.com/containerd/containerd/runtime/shim/v1" shimapi "github.com/containerd/containerd/runtime/v1/shim/v1"
"github.com/containerd/containerd/sys" "github.com/containerd/containerd/sys"
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
) )

View File

@ -23,7 +23,7 @@ import (
"path/filepath" "path/filepath"
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
shimapi "github.com/containerd/containerd/runtime/shim/v1" shimapi "github.com/containerd/containerd/runtime/v1/shim/v1"
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
) )

View File

@ -22,6 +22,7 @@ import (
"context" "context"
"fmt" "fmt"
"os" "os"
"path/filepath"
"sync" "sync"
"github.com/containerd/console" "github.com/containerd/console"
@ -30,12 +31,13 @@ import (
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events" "github.com/containerd/containerd/events"
"github.com/containerd/containerd/log" "github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/namespaces" "github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/runtime" "github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/linux/proc"
"github.com/containerd/containerd/runtime/linux/runctypes" "github.com/containerd/containerd/runtime/linux/runctypes"
rproc "github.com/containerd/containerd/runtime/proc" rproc "github.com/containerd/containerd/runtime/proc"
shimapi "github.com/containerd/containerd/runtime/shim/v1" "github.com/containerd/containerd/runtime/v1/linux/proc"
shimapi "github.com/containerd/containerd/runtime/v1/shim/v1"
runc "github.com/containerd/go-runc" runc "github.com/containerd/go-runc"
"github.com/containerd/typeurl" "github.com/containerd/typeurl"
ptypes "github.com/gogo/protobuf/types" ptypes "github.com/gogo/protobuf/types"
@ -108,7 +110,7 @@ type Service struct {
} }
// Create a new initial process and container with the underlying OCI runtime // Create a new initial process and container with the underlying OCI runtime
func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (*shimapi.CreateTaskResponse, error) { func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (_ *shimapi.CreateTaskResponse, err error) {
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@ -121,16 +123,8 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (*sh
Options: m.Options, Options: m.Options,
}) })
} }
process, err := proc.New(
ctx, config := &proc.CreateConfig{
s.config.Path,
s.config.WorkDir,
s.config.RuntimeRoot,
s.config.Namespace,
s.config.Criu,
s.config.SystemdCgroup,
s.platform,
&proc.CreateConfig{
ID: r.ID, ID: r.ID,
Bundle: r.Bundle, Bundle: r.Bundle,
Runtime: r.Runtime, Runtime: r.Runtime,
@ -142,11 +136,42 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (*sh
Checkpoint: r.Checkpoint, Checkpoint: r.Checkpoint,
ParentCheckpoint: r.ParentCheckpoint, ParentCheckpoint: r.ParentCheckpoint,
Options: r.Options, Options: r.Options,
}, }
rootfs := filepath.Join(r.Bundle, "rootfs")
defer func() {
if err != nil {
if err2 := mount.UnmountAll(rootfs, 0); err2 != nil {
log.G(ctx).WithError(err2).Warn("Failed to cleanup rootfs mount")
}
}
}()
for _, rm := range mounts {
m := &mount.Mount{
Type: rm.Type,
Source: rm.Source,
Options: rm.Options,
}
if err := m.Mount(rootfs); err != nil {
return nil, errors.Wrapf(err, "failed to mount rootfs component %v", m)
}
}
process, err := newInit(
ctx,
s.config.Path,
s.config.WorkDir,
s.config.RuntimeRoot,
s.config.Namespace,
s.config.Criu,
s.config.SystemdCgroup,
s.platform,
config,
) )
if err != nil { if err != nil {
return nil, errdefs.ToGRPC(err) return nil, errdefs.ToGRPC(err)
} }
if err := process.Create(ctx, config); err != nil {
return nil, errdefs.ToGRPC(err)
}
// save the main task id and bundle to the shim for additional requests // save the main task id and bundle to the shim for additional requests
s.id = r.ID s.id = r.ID
s.bundle = r.Bundle s.bundle = r.Bundle
@ -414,9 +439,22 @@ func (s *Service) Checkpoint(ctx context.Context, r *shimapi.CheckpointTaskReque
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
} }
var options runctypes.CheckpointOptions
if r.Options != nil {
v, err := typeurl.UnmarshalAny(r.Options)
if err != nil {
return nil, err
}
options = *v.(*runctypes.CheckpointOptions)
}
if err := p.(*proc.Init).Checkpoint(ctx, &proc.CheckpointConfig{ if err := p.(*proc.Init).Checkpoint(ctx, &proc.CheckpointConfig{
Path: r.Path, Path: r.Path,
Options: r.Options, Exit: options.Exit,
AllowOpenTCP: options.OpenTcp,
AllowExternalUnixSockets: options.ExternalUnixSockets,
AllowTerminal: options.Terminal,
FileLocks: options.FileLocks,
EmptyNamespaces: options.EmptyNamespaces,
}); err != nil { }); err != nil {
return nil, errdefs.ToGRPC(err) return nil, errdefs.ToGRPC(err)
} }
@ -545,3 +583,32 @@ func getTopic(ctx context.Context, e interface{}) string {
} }
return runtime.TaskUnknownTopic return runtime.TaskUnknownTopic
} }
func newInit(ctx context.Context, path, workDir, runtimeRoot, namespace, criu string, systemdCgroup bool, platform rproc.Platform, r *proc.CreateConfig) (*proc.Init, error) {
var options runctypes.CreateOptions
if r.Options != nil {
v, err := typeurl.UnmarshalAny(r.Options)
if err != nil {
return nil, err
}
options = *v.(*runctypes.CreateOptions)
}
rootfs := filepath.Join(path, "rootfs")
runtime := proc.NewRunc(runtimeRoot, path, namespace, r.Runtime, criu, systemdCgroup)
p := proc.New(r.ID, runtime, rproc.Stdio{
Stdin: r.Stdin,
Stdout: r.Stdout,
Stderr: r.Stderr,
Terminal: r.Terminal,
})
p.Bundle = r.Bundle
p.Platform = platform
p.Rootfs = rootfs
p.WorkDir = workDir
p.IoUID = int(options.IoUid)
p.IoGID = int(options.IoGid)
p.NoPivotRoot = options.NoPivotRoot
p.NoNewKeyring = options.NoNewKeyring
return p, nil
}

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