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/gc/scheduler"
_ "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/content"
_ "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/diff/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/tasks/v1"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin"
@ -137,7 +137,7 @@ func getServicesOpts(ic *plugin.InitContext) ([]containerd.ServicesOpt, error) {
return containerd.WithNamespaceService(s.(namespaces.NamespacesClient))
},
services.LeasesService: func(s interface{}) containerd.ServicesOpt {
return containerd.WithLeasesService(s.(leases.LeasesClient))
return containerd.WithLeasesService(s.(leases.Manager))
},
} {
p := plugins[s]

View File

@ -123,7 +123,7 @@ func Import(ctx context.Context, client *containerd.Client, reader io.Reader, op
defer deferCancel()
if err := done(deferCtx); err != nil {
// 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)
}
}()

View File

@ -2,13 +2,13 @@ github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
github.com/blang/semver v3.1.0
github.com/boltdb/bolt e9cf4fae01b5a8ff89d0ec6b32f0d9c9f79aefdd
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130
github.com/containerd/console 5d1b48d6114b8c9666f0c8b916f871af97b0a761
github.com/containerd/containerd b382b6fe0bdbf7604c0a4f5c2089c0b159ad58b2
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
github.com/containerd/console 4d8a41f4ce5b9bae77c41786ea2458330f43f081
github.com/containerd/containerd b9eeaa1ce83dd9970605ddbd0b35d4d3fa5f87bd
github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
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/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
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/image-spec v1.0.1
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/selinux 4a2974bf1ee960774ffd517717f1f45325af0206
github.com/pkg/errors v0.8.0
@ -62,7 +62,7 @@ github.com/xeipuuv/gojsonschema 1d523034197ff1f222f6429836dd36a2457a1874
golang.org/x/crypto 49796115aa4b964c318aad4f3084fdb41e9aa067
golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
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/time f51c12702a4d776e4c1fa9b0fabab841babae631
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944

View File

@ -137,6 +137,36 @@ func (c *cgroup) add(process Process) error {
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
func (c *cgroup) Delete() error {
c.mu.Lock()

View File

@ -24,6 +24,7 @@ import (
const (
cgroupProcs = "cgroup.procs"
cgroupTasks = "tasks"
defaultDirPerm = 0755
)
@ -48,8 +49,10 @@ type Process struct {
type Cgroup interface {
// New creates a new cgroup under the calling cgroup
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
// AddTask adds a process to the cgroup (tasks)
AddTask(Process) error
// Delete removes the cgroup as a whole
Delete() error
// 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
}
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) {
if cpus, err = ioutil.ReadFile(filepath.Join(path, "cpuset.cpus")); err != nil && !os.IsNotExist(err) {
return

View File

@ -19,6 +19,8 @@
MemoryEntry
BlkIOStat
BlkIOEntry
RdmaStat
RdmaEntry
*/
package cgroups
@ -49,6 +51,7 @@ type Metrics struct {
CPU *CPUStat `protobuf:"bytes,3,opt,name=cpu" json:"cpu,omitempty"`
Memory *MemoryStat `protobuf:"bytes,4,opt,name=memory" json:"memory,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{} }
@ -187,6 +190,25 @@ func (m *BlkIOEntry) Reset() { *m = BlkIOEntry{} }
func (*BlkIOEntry) ProtoMessage() {}
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() {
proto.RegisterType((*Metrics)(nil), "io.containerd.cgroups.v1.Metrics")
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((*BlkIOStat)(nil), "io.containerd.cgroups.v1.BlkIOStat")
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) {
size := m.Size()
@ -266,6 +290,16 @@ func (m *Metrics) MarshalTo(dAtA []byte) (int, error) {
}
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
}
@ -732,6 +766,7 @@ func (m *MemoryEntry) MarshalTo(dAtA []byte) (int, error) {
_ = i
var l int
_ = l
if m.Limit != 0 {
dAtA[i] = 0x8
i++
@ -914,6 +949,82 @@ func (m *BlkIOEntry) MarshalTo(dAtA []byte) (int, error) {
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 {
dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8)
@ -966,6 +1077,10 @@ func (m *Metrics) Size() (n int) {
l = m.Blkio.Size()
n += 1 + l + sovMetrics(uint64(l))
}
if m.Rdma != nil {
l = m.Rdma.Size()
n += 1 + l + sovMetrics(uint64(l))
}
return n
}
@ -1264,6 +1379,40 @@ func (m *BlkIOEntry) Size() (n int) {
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) {
for {
n++
@ -1287,6 +1436,7 @@ func (this *Metrics) String() string {
`CPU:` + strings.Replace(fmt.Sprintf("%v", this.CPU), "CPUStat", "CPUStat", 1) + `,`,
`Memory:` + strings.Replace(fmt.Sprintf("%v", this.Memory), "MemoryStat", "MemoryStat", 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
@ -1440,6 +1590,29 @@ func (this *BlkIOEntry) String() string {
}, "")
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 {
rv := reflect.ValueOf(v)
if rv.IsNil() {
@ -1451,6 +1624,7 @@ func valueToStringMetrics(v interface{}) string {
func (m *Metrics) Unmarshal(dAtA []byte) error {
l := len(dAtA)
iNdEx := 0
for iNdEx < l {
preIndex := iNdEx
var wire uint64
@ -1640,6 +1814,39 @@ func (m *Metrics) Unmarshal(dAtA []byte) error {
return err
}
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:
iNdEx = preIndex
skippy, err := skipMetrics(dAtA[iNdEx:])
@ -3656,6 +3863,236 @@ func (m *BlkIOEntry) Unmarshal(dAtA []byte) error {
}
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) {
l := len(dAtA)
iNdEx := 0

View File

@ -10,6 +10,7 @@ message Metrics {
CPUStat cpu = 3 [(gogoproto.customname) = "CPU"];
MemoryStat memory = 4;
BlkIOStat blkio = 5;
RdmaStat rdma = 6;
}
message HugetlbStat {
@ -109,3 +110,14 @@ message BlkIOEntry {
uint64 minor = 4;
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"
Memory Name = "memory"
Blkio Name = "blkio"
Rdma Name = "rdma"
)
// Subsystems returns a complete list of the default cgroups
@ -55,6 +56,7 @@ func Subsystems() []Name {
Cpuacct,
Memory,
Blkio,
Rdma,
}
if !isUserNS {
n = append(n, Devices)

View File

@ -80,6 +80,7 @@ func defaults(root string) ([]Subsystem, error) {
NewCpuacct(root),
NewMemory(root),
NewBlkio(root),
NewRdma(root),
}
// only add the devices cgroup if we are not in a user namespace
// 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
// 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)
@ -184,6 +184,28 @@ defer task.Delete(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
Please see [RELEASES.md](RELEASES.md) for details on versioning and stability

View File

@ -18,6 +18,7 @@
UpdateContainerRequest
UpdateContainerResponse
DeleteContainerRequest
ListContainerMessage
*/
package containers
@ -218,6 +219,14 @@ func (m *DeleteContainerRequest) Reset() { *m = DeleteContain
func (*DeleteContainerRequest) ProtoMessage() {}
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() {
proto.RegisterType((*Container)(nil), "containerd.services.containers.v1.Container")
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((*UpdateContainerResponse)(nil), "containerd.services.containers.v1.UpdateContainerResponse")
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.
@ -245,6 +255,7 @@ const _ = grpc.SupportPackageIsVersion4
type ContainersClient interface {
Get(ctx context.Context, in *GetContainerRequest, opts ...grpc.CallOption) (*GetContainerResponse, 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)
Update(ctx context.Context, in *UpdateContainerRequest, opts ...grpc.CallOption) (*UpdateContainerResponse, 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
}
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) {
out := new(CreateContainerResponse)
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 {
Get(context.Context, *GetContainerRequest) (*GetContainerResponse, error)
List(context.Context, *ListContainersRequest) (*ListContainersResponse, error)
ListStream(*ListContainersRequest, Containers_ListStreamServer) error
Create(context.Context, *CreateContainerRequest) (*CreateContainerResponse, error)
Update(context.Context, *UpdateContainerRequest) (*UpdateContainerResponse, 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)
}
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) {
in := new(CreateContainerRequest)
if err := dec(in); err != nil {
@ -432,7 +497,13 @@ var _Containers_serviceDesc = grpc.ServiceDesc{
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",
}
@ -842,6 +913,34 @@ func (m *DeleteContainerRequest) MarshalTo(dAtA []byte) (int, error) {
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 {
for v >= 1<<7 {
dAtA[offset] = uint8(v&0x7f | 0x80)
@ -1004,6 +1103,16 @@ func (m *DeleteContainerRequest) Size() (n int) {
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) {
for {
n++
@ -1158,6 +1267,16 @@ func (this *DeleteContainerRequest) String() string {
}, "")
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 {
rv := reflect.ValueOf(v)
if rv.IsNil() {
@ -2562,6 +2681,89 @@ func (m *DeleteContainerRequest) Unmarshal(dAtA []byte) error {
}
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) {
l := len(dAtA)
iNdEx := 0
@ -2672,54 +2874,57 @@ func init() {
}
var fileDescriptorContainers = []byte{
// 776 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcd, 0x72, 0xd2, 0x50,
0x14, 0x26, 0x81, 0x86, 0x72, 0x70, 0x46, 0xe7, 0x8a, 0x18, 0xe3, 0x0c, 0x50, 0x56, 0x8c, 0xa3,
0xc1, 0xa2, 0xa3, 0xfd, 0x71, 0x53, 0xfa, 0x37, 0x8e, 0xad, 0xd3, 0x89, 0x3a, 0xe3, 0xe8, 0xa2,
0x06, 0xb8, 0xa5, 0x91, 0xfc, 0x99, 0x7b, 0x61, 0x64, 0x5c, 0xe8, 0x23, 0xb8, 0xf3, 0x11, 0x7c,
0x95, 0x2e, 0x5d, 0xba, 0xaa, 0x2d, 0x4f, 0xe2, 0xe4, 0x26, 0x21, 0x29, 0x04, 0x85, 0x2a, 0xbb,
0x7b, 0xb8, 0xe7, 0xfb, 0xce, 0xc7, 0x77, 0xce, 0xb9, 0x00, 0x7b, 0x6d, 0x8d, 0x1e, 0x77, 0x1b,
0x72, 0xd3, 0x32, 0xaa, 0x4d, 0xcb, 0xa4, 0xaa, 0x66, 0x62, 0xa7, 0x15, 0x3d, 0xaa, 0xb6, 0x56,
0x25, 0xd8, 0xe9, 0x69, 0x4d, 0x4c, 0xc2, 0xcf, 0x49, 0xb5, 0xb7, 0x1c, 0x89, 0x64, 0xdb, 0xb1,
0xa8, 0x85, 0x96, 0x42, 0x9c, 0x1c, 0x60, 0xe4, 0x48, 0x56, 0x6f, 0x59, 0xca, 0xb5, 0xad, 0xb6,
0xc5, 0xb2, 0xab, 0xee, 0xc9, 0x03, 0x4a, 0xb7, 0xda, 0x96, 0xd5, 0xd6, 0x71, 0x95, 0x45, 0x8d,
0xee, 0x51, 0x55, 0x35, 0xfb, 0xfe, 0xd5, 0xed, 0xd1, 0x2b, 0x6c, 0xd8, 0x34, 0xb8, 0x2c, 0x8d,
0x5e, 0x1e, 0x69, 0x58, 0x6f, 0x1d, 0x1a, 0x2a, 0xe9, 0xf8, 0x19, 0xc5, 0xd1, 0x0c, 0xaa, 0x19,
0x98, 0x50, 0xd5, 0xb0, 0xbd, 0x84, 0xf2, 0x37, 0x01, 0x32, 0x9b, 0x81, 0x44, 0x94, 0x07, 0x5e,
0x6b, 0x89, 0x5c, 0x89, 0xab, 0x64, 0xea, 0xc2, 0xe0, 0xb4, 0xc8, 0x3f, 0xdd, 0x52, 0x78, 0xad,
0x85, 0x0e, 0x40, 0xd0, 0xd5, 0x06, 0xd6, 0x89, 0xc8, 0x97, 0x92, 0x95, 0x6c, 0x6d, 0x45, 0xfe,
0xeb, 0x57, 0x95, 0x87, 0xac, 0xf2, 0x1e, 0x83, 0x6e, 0x9b, 0xd4, 0xe9, 0x2b, 0x3e, 0x0f, 0xca,
0xc1, 0x82, 0x66, 0xa8, 0x6d, 0x2c, 0x26, 0xdd, 0x62, 0x8a, 0x17, 0xa0, 0xe7, 0x90, 0x76, 0xba,
0xa6, 0xab, 0x51, 0x4c, 0x95, 0xb8, 0x4a, 0xb6, 0xf6, 0x70, 0xa6, 0x42, 0x8a, 0x87, 0x55, 0x02,
0x12, 0x54, 0x81, 0x14, 0xb1, 0x71, 0x53, 0x5c, 0x60, 0x64, 0x39, 0xd9, 0x73, 0x43, 0x0e, 0xdc,
0x90, 0x37, 0xcc, 0xbe, 0xc2, 0x32, 0x50, 0x09, 0xb2, 0xc4, 0x54, 0x6d, 0x72, 0x6c, 0x51, 0x8a,
0x1d, 0x51, 0x60, 0xaa, 0xa2, 0x1f, 0xa1, 0x25, 0xb8, 0x12, 0x84, 0x87, 0x1d, 0xdc, 0x17, 0xd3,
0x17, 0x53, 0x9e, 0xe1, 0x3e, 0xda, 0x04, 0x68, 0x3a, 0x58, 0xa5, 0xb8, 0x75, 0xa8, 0x52, 0x71,
0x91, 0x15, 0x95, 0xc6, 0x8a, 0xbe, 0x0c, 0x5a, 0x50, 0x5f, 0x3c, 0x39, 0x2d, 0x26, 0xbe, 0xfe,
0x2a, 0x72, 0x4a, 0xc6, 0xc7, 0x6d, 0x50, 0x97, 0xa4, 0x6b, 0xb7, 0x02, 0x92, 0xcc, 0x2c, 0x24,
0x3e, 0x6e, 0x83, 0xa2, 0x06, 0x00, 0xfe, 0x48, 0xb1, 0x49, 0x34, 0xcb, 0x24, 0x22, 0xb0, 0xa6,
0x3d, 0x99, 0xc9, 0xcb, 0xed, 0x21, 0x9c, 0x35, 0xae, 0x9e, 0x72, 0xcb, 0x28, 0x11, 0x56, 0x69,
0x15, 0xb2, 0x91, 0xce, 0xa2, 0x6b, 0x90, 0x74, 0x6d, 0x61, 0xc3, 0xa3, 0xb8, 0x47, 0xb7, 0xc7,
0x3d, 0x55, 0xef, 0x62, 0x91, 0xf7, 0x7a, 0xcc, 0x82, 0x35, 0x7e, 0x85, 0x93, 0xf6, 0x21, 0xed,
0xf7, 0x0a, 0x21, 0x48, 0x99, 0xaa, 0x81, 0x7d, 0x1c, 0x3b, 0x23, 0x19, 0xd2, 0x96, 0x4d, 0x99,
0x74, 0xfe, 0x0f, 0x9d, 0x0b, 0x92, 0xa4, 0x17, 0x70, 0x75, 0x44, 0x6e, 0x8c, 0x9a, 0x3b, 0x51,
0x35, 0x93, 0x28, 0x43, 0x8d, 0xe5, 0x7b, 0x70, 0x7d, 0x17, 0xd3, 0xa1, 0x21, 0x0a, 0xfe, 0xd0,
0xc5, 0x84, 0x4e, 0x5a, 0x91, 0xf2, 0x31, 0xe4, 0x2e, 0xa6, 0x13, 0xdb, 0x32, 0x09, 0x46, 0x07,
0x90, 0x19, 0x5a, 0xcc, 0x60, 0xd9, 0xda, 0xdd, 0x59, 0x1a, 0xe1, 0x1b, 0x1f, 0x92, 0x94, 0x97,
0xe1, 0xc6, 0x9e, 0x46, 0xc2, 0x52, 0x24, 0x90, 0x26, 0x42, 0xfa, 0x48, 0xd3, 0x29, 0x76, 0x88,
0xc8, 0x95, 0x92, 0x95, 0x8c, 0x12, 0x84, 0x65, 0x1d, 0xf2, 0xa3, 0x10, 0x5f, 0x9e, 0x02, 0x10,
0x16, 0x66, 0xb0, 0xcb, 0xe9, 0x8b, 0xb0, 0x94, 0xdf, 0x43, 0x7e, 0x93, 0x8d, 0xf3, 0x98, 0x79,
0xff, 0xdf, 0x8c, 0x0e, 0xdc, 0x1c, 0xab, 0x35, 0x37, 0xe7, 0xbf, 0x73, 0x90, 0x7f, 0xc5, 0x76,
0x6c, 0xfe, 0xdf, 0x0c, 0xad, 0x43, 0xd6, 0xdb, 0x67, 0xf6, 0x9e, 0xfb, 0x53, 0x3b, 0xfe, 0x10,
0xec, 0xb8, 0x4f, 0xfe, 0xbe, 0x4a, 0x3a, 0x8a, 0xff, 0x6c, 0xb8, 0x67, 0xd7, 0x96, 0x31, 0xa1,
0x73, 0xb3, 0xe5, 0x3e, 0xe4, 0xb7, 0xb0, 0x8e, 0x63, 0x5c, 0x99, 0xb0, 0x2c, 0xb5, 0xb3, 0x14,
0x40, 0x38, 0x8c, 0xa8, 0x07, 0xc9, 0x5d, 0x4c, 0xd1, 0xa3, 0x29, 0x64, 0xc4, 0xac, 0xa4, 0xf4,
0x78, 0x66, 0x9c, 0x6f, 0xc5, 0x27, 0x48, 0xb9, 0x6b, 0x81, 0xa6, 0xf9, 0x39, 0x8b, 0x5d, 0x39,
0x69, 0xf5, 0x12, 0x48, 0xbf, 0xf8, 0x67, 0x10, 0xbc, 0xc9, 0x45, 0xd3, 0x90, 0xc4, 0x2f, 0x94,
0xb4, 0x76, 0x19, 0x68, 0x28, 0xc0, 0x9b, 0x91, 0xa9, 0x04, 0xc4, 0xcf, 0xfd, 0x54, 0x02, 0x26,
0x4d, 0xe2, 0x5b, 0x10, 0xbc, 0xb9, 0x99, 0x4a, 0x40, 0xfc, 0x88, 0x49, 0xf9, 0xb1, 0x8d, 0xd8,
0x76, 0xff, 0x21, 0xd5, 0xdf, 0x9d, 0x9c, 0x17, 0x12, 0x3f, 0xcf, 0x0b, 0x89, 0x2f, 0x83, 0x02,
0x77, 0x32, 0x28, 0x70, 0x3f, 0x06, 0x05, 0xee, 0x6c, 0x50, 0xe0, 0xde, 0xec, 0xfc, 0xc3, 0x9f,
0xbe, 0xf5, 0x30, 0x7a, 0x9d, 0x68, 0x08, 0xac, 0xe6, 0x83, 0xdf, 0x01, 0x00, 0x00, 0xff, 0xff,
0x95, 0x94, 0x84, 0xf2, 0x47, 0x0a, 0x00, 0x00,
// 820 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x56, 0xcb, 0x6e, 0x13, 0x49,
0x14, 0x75, 0xdb, 0x4e, 0x3b, 0xbe, 0x1e, 0x69, 0x46, 0x35, 0x1e, 0x4f, 0x4f, 0x8f, 0x64, 0x3b,
0x5e, 0x59, 0xa3, 0xa1, 0x9d, 0x18, 0x44, 0x5e, 0x6c, 0xe2, 0xbc, 0x04, 0x24, 0x28, 0xea, 0x80,
0x84, 0x60, 0x11, 0xda, 0x76, 0xc5, 0x69, 0xdc, 0x2f, 0xba, 0xca, 0x16, 0x16, 0x8b, 0xc0, 0x1f,
0xb0, 0xe3, 0x13, 0xf8, 0x95, 0x2c, 0x59, 0xb2, 0x0a, 0xc4, 0xe2, 0x43, 0x50, 0x57, 0x57, 0xbb,
0x3b, 0x7e, 0x80, 0x9d, 0x90, 0x5d, 0x5d, 0xd7, 0x3d, 0xf7, 0x9e, 0x3a, 0xb7, 0x4e, 0xb9, 0x61,
0xaf, 0xa5, 0xd3, 0x93, 0x4e, 0x5d, 0x69, 0xd8, 0x66, 0xa5, 0x61, 0x5b, 0x54, 0xd3, 0x2d, 0xec,
0x36, 0xa3, 0x4b, 0xcd, 0xd1, 0x2b, 0x04, 0xbb, 0x5d, 0xbd, 0x81, 0x49, 0xf8, 0x3b, 0xa9, 0x74,
0x97, 0x22, 0x91, 0xe2, 0xb8, 0x36, 0xb5, 0xd1, 0x42, 0x88, 0x53, 0x02, 0x8c, 0x12, 0xc9, 0xea,
0x2e, 0xc9, 0xd9, 0x96, 0xdd, 0xb2, 0x59, 0x76, 0xc5, 0x5b, 0xf9, 0x40, 0xf9, 0x9f, 0x96, 0x6d,
0xb7, 0x0c, 0x5c, 0x61, 0x51, 0xbd, 0x73, 0x5c, 0xd1, 0xac, 0x1e, 0xdf, 0xfa, 0x77, 0x78, 0x0b,
0x9b, 0x0e, 0x0d, 0x36, 0x8b, 0xc3, 0x9b, 0xc7, 0x3a, 0x36, 0x9a, 0x47, 0xa6, 0x46, 0xda, 0x3c,
0xa3, 0x30, 0x9c, 0x41, 0x75, 0x13, 0x13, 0xaa, 0x99, 0x8e, 0x9f, 0x50, 0xfa, 0x20, 0x42, 0x7a,
0x33, 0xa0, 0x88, 0x72, 0x10, 0xd7, 0x9b, 0x92, 0x50, 0x14, 0xca, 0xe9, 0x9a, 0xd8, 0x3f, 0x2f,
0xc4, 0xef, 0x6f, 0xa9, 0x71, 0xbd, 0x89, 0x0e, 0x40, 0x34, 0xb4, 0x3a, 0x36, 0x88, 0x14, 0x2f,
0x26, 0xca, 0x99, 0xea, 0x8a, 0xf2, 0xd3, 0xa3, 0x2a, 0x83, 0xaa, 0xca, 0x1e, 0x83, 0x6e, 0x5b,
0xd4, 0xed, 0xa9, 0xbc, 0x0e, 0xca, 0xc2, 0x9c, 0x6e, 0x6a, 0x2d, 0x2c, 0x25, 0xbc, 0x66, 0xaa,
0x1f, 0xa0, 0x47, 0x90, 0x72, 0x3b, 0x96, 0xc7, 0x51, 0x4a, 0x16, 0x85, 0x72, 0xa6, 0x7a, 0x67,
0xa6, 0x46, 0xaa, 0x8f, 0x55, 0x83, 0x22, 0xa8, 0x0c, 0x49, 0xe2, 0xe0, 0x86, 0x34, 0xc7, 0x8a,
0x65, 0x15, 0x5f, 0x0d, 0x25, 0x50, 0x43, 0xd9, 0xb0, 0x7a, 0x2a, 0xcb, 0x40, 0x45, 0xc8, 0x10,
0x4b, 0x73, 0xc8, 0x89, 0x4d, 0x29, 0x76, 0x25, 0x91, 0xb1, 0x8a, 0xfe, 0x84, 0x16, 0xe0, 0xb7,
0x20, 0x3c, 0x6a, 0xe3, 0x9e, 0x94, 0xba, 0x9c, 0xf2, 0x10, 0xf7, 0xd0, 0x26, 0x40, 0xc3, 0xc5,
0x1a, 0xc5, 0xcd, 0x23, 0x8d, 0x4a, 0xf3, 0xac, 0xa9, 0x3c, 0xd2, 0xf4, 0x71, 0x30, 0x82, 0xda,
0xfc, 0xd9, 0x79, 0x21, 0xf6, 0xfe, 0x4b, 0x41, 0x50, 0xd3, 0x1c, 0xb7, 0x41, 0xbd, 0x22, 0x1d,
0xa7, 0x19, 0x14, 0x49, 0xcf, 0x52, 0x84, 0xe3, 0x36, 0x28, 0xaa, 0x03, 0xe0, 0xd7, 0x14, 0x5b,
0x44, 0xb7, 0x2d, 0x22, 0x01, 0x1b, 0xda, 0xbd, 0x99, 0xb4, 0xdc, 0x1e, 0xc0, 0xd9, 0xe0, 0x6a,
0x49, 0xaf, 0x8d, 0x1a, 0xa9, 0x2a, 0xaf, 0x42, 0x26, 0x32, 0x59, 0xf4, 0x07, 0x24, 0x3c, 0x59,
0xd8, 0xe5, 0x51, 0xbd, 0xa5, 0x37, 0xe3, 0xae, 0x66, 0x74, 0xb0, 0x14, 0xf7, 0x67, 0xcc, 0x82,
0xb5, 0xf8, 0x8a, 0x20, 0xef, 0x43, 0x8a, 0xcf, 0x0a, 0x21, 0x48, 0x5a, 0x9a, 0x89, 0x39, 0x8e,
0xad, 0x91, 0x02, 0x29, 0xdb, 0xa1, 0x8c, 0x7a, 0xfc, 0x07, 0x93, 0x0b, 0x92, 0xe4, 0x43, 0xf8,
0x7d, 0x88, 0xee, 0x18, 0x36, 0xff, 0x45, 0xd9, 0x4c, 0x2a, 0x19, 0x72, 0x2c, 0xdd, 0x82, 0x3f,
0x77, 0x31, 0x1d, 0x08, 0xa2, 0xe2, 0x57, 0x1d, 0x4c, 0xe8, 0x24, 0x8b, 0x94, 0x4e, 0x20, 0x7b,
0x39, 0x9d, 0x38, 0xb6, 0x45, 0x30, 0x3a, 0x80, 0xf4, 0x40, 0x62, 0x06, 0xcb, 0x54, 0xff, 0x9f,
0x65, 0x10, 0x5c, 0xf8, 0xb0, 0x48, 0x69, 0x09, 0xfe, 0xda, 0xd3, 0x49, 0xd8, 0x8a, 0x04, 0xd4,
0x24, 0x48, 0x1d, 0xeb, 0x06, 0xc5, 0x2e, 0x91, 0x84, 0x62, 0xa2, 0x9c, 0x56, 0x83, 0xb0, 0x64,
0x40, 0x6e, 0x18, 0xc2, 0xe9, 0xa9, 0x00, 0x61, 0x63, 0x06, 0xbb, 0x1a, 0xbf, 0x48, 0x95, 0xd2,
0x4b, 0xc8, 0x6d, 0xb2, 0xeb, 0x3c, 0x22, 0xde, 0xaf, 0x17, 0xa3, 0x0d, 0x7f, 0x8f, 0xf4, 0xba,
0x31, 0xe5, 0x3f, 0x0a, 0x90, 0x7b, 0xc2, 0x3c, 0x76, 0xf3, 0x27, 0x43, 0xeb, 0x90, 0xf1, 0xfd,
0xcc, 0xde, 0x73, 0x7e, 0x6b, 0x47, 0x1f, 0x82, 0x1d, 0xef, 0xc9, 0xdf, 0xd7, 0x48, 0x5b, 0xe5,
0xcf, 0x86, 0xb7, 0xf6, 0x64, 0x19, 0x21, 0x7a, 0x63, 0xb2, 0x2c, 0x42, 0x6e, 0x0b, 0x1b, 0x78,
0x8c, 0x2a, 0x93, 0xcc, 0x52, 0x87, 0xec, 0xa5, 0xfb, 0xb8, 0x8f, 0x09, 0xf1, 0xde, 0xff, 0x07,
0xd7, 0xe4, 0x16, 0x61, 0x55, 0xfd, 0x36, 0x07, 0x10, 0x5e, 0x78, 0xd4, 0x85, 0xc4, 0x2e, 0xa6,
0xe8, 0xee, 0x14, 0xe5, 0xc6, 0xd8, 0x5e, 0x5e, 0x9e, 0x19, 0xc7, 0xe5, 0x7e, 0x03, 0x49, 0xef,
0xa8, 0x68, 0x9a, 0xbf, 0xcc, 0xb1, 0xb6, 0x96, 0x57, 0xaf, 0x80, 0xe4, 0xcd, 0xdf, 0x09, 0x00,
0xde, 0xd6, 0x21, 0x75, 0xb1, 0x66, 0x5e, 0x83, 0xc3, 0xf2, 0xac, 0x48, 0x3e, 0xd1, 0x45, 0x01,
0x9d, 0x82, 0xe8, 0x3b, 0x14, 0x4d, 0x73, 0x90, 0xf1, 0x0f, 0x87, 0xbc, 0x76, 0x15, 0x28, 0x17,
0xe1, 0x14, 0x44, 0xdf, 0x0b, 0x53, 0x11, 0x18, 0xef, 0xef, 0xa9, 0x08, 0x4c, 0x72, 0xdc, 0x73,
0x10, 0x7d, 0x7f, 0x4c, 0x45, 0x60, 0xbc, 0x95, 0xe4, 0xdc, 0x88, 0xf3, 0xb7, 0xbd, 0x2f, 0xc1,
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 {
rpc Get(GetContainerRequest) returns (GetContainerResponse);
rpc List(ListContainersRequest) returns (ListContainersResponse);
rpc ListStream(ListContainersRequest) returns (stream ListContainerMessage);
rpc Create(CreateContainerRequest) returns (CreateContainerResponse);
rpc Update(UpdateContainerRequest) returns (UpdateContainerResponse);
rpc Delete(DeleteContainerRequest) returns (google.protobuf.Empty);
@ -156,3 +157,7 @@ message UpdateContainerResponse {
message DeleteContainerRequest {
string id = 1;
}
message ListContainerMessage {
Container container = 1;
}

View File

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

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 {
io.Reader
compression Compression
closer func() error
}
@ -55,6 +63,10 @@ func (r *readCloserWrapper) Close() error {
return nil
}
func (r *readCloserWrapper) GetCompression() Compression {
return r.compression
}
type writeCloserWrapper struct {
io.Writer
closer func() error
@ -84,7 +96,7 @@ func DetectCompression(source []byte) Compression {
}
// 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.Reset(archive)
bs, err := buf.Peek(10)
@ -105,14 +117,14 @@ func DecompressStream(archive io.Reader) (io.ReadCloser, error) {
}
switch compression := DetectCompression(bs); compression {
case Uncompressed:
readBufWrapper := &readCloserWrapper{buf, closer}
readBufWrapper := &readCloserWrapper{buf, compression, closer}
return readBufWrapper, nil
case Gzip:
gzReader, err := gzip.NewReader(buf)
if err != nil {
return nil, err
}
readBufWrapper := &readCloserWrapper{gzReader, closer}
readBufWrapper := &readCloserWrapper{gzReader, compression, closer}
return readBufWrapper, nil
default:
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")
}
}
if options.Filter == nil {
options.Filter = all
}
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
hdr.Name = filepath.Clean(hdr.Name)
accept, err := options.Filter(hdr)
if err != nil {
return 0, err
}
if !accept {
continue
}
if skipFile(hdr) {
log.G(ctx).Warnf("file %q ignored: archive may not be supported on system", hdr.Name)
continue
@ -366,10 +377,11 @@ func createTarFile(ctx context.Context, path, extractDir string, hdr *tar.Header
}
case tar.TypeLink:
targetPath, err := fs.RootPath(extractDir, hdr.Linkname)
targetPath, err := hardlinkRootPath(extractDir, hdr.Linkname)
if err != nil {
return err
}
if err := os.Link(targetPath, path); err != nil {
return err
}
@ -648,3 +660,27 @@ func copyBuffered(ctx context.Context, dst io.Writer, src io.Reader) (written in
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
import "archive/tar"
// ApplyOpt allows setting mutable archive apply properties on creation
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
type ApplyOptions struct {
Filter Filter // Filter tar headers
}

View File

@ -22,6 +22,7 @@ package archive
type ApplyOptions struct {
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
Filter Filter // Filter tar headers
}
// WithParentLayers adds parent layers to the apply process this is required

View File

@ -20,7 +20,6 @@ import (
"fmt"
"io"
"net"
"sync"
winio "github.com/Microsoft/go-winio"
"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) {
var (
wg sync.WaitGroup
set []io.Closer
)
@ -85,9 +83,7 @@ func copyIO(fifos *FIFOSet, ioset *Streams) (*cio, error) {
}(l)
set = append(set, l)
wg.Add(1)
go func() {
defer wg.Done()
c, err := l.Accept()
if err != nil {
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)
set = append(set, l)
wg.Add(1)
go func() {
defer wg.Done()
c, err := l.Accept()
if err != nil {
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/events"
"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/pkg/dialer"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/remotes"
"github.com/containerd/containerd/remotes/docker"
@ -79,8 +82,12 @@ func New(address string, opts ...ClientOpt) (*Client, error) {
return nil, err
}
}
rt := fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS)
if copts.defaultRuntime != "" {
rt = copts.defaultRuntime
}
c := &Client{
runtime: fmt.Sprintf("%s.%s", plugin.RuntimePlugin, runtime.GOOS),
runtime: rt,
}
if copts.services != nil {
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
// and returns a platform specific image object
func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image, error) {
pullCtx := defaultRemoteContext()
for _, o := range opts {
@ -292,7 +323,12 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image
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)
if err != nil {
@ -300,82 +336,92 @@ func (c *Client) Pull(ctx context.Context, ref string, opts ...RemoteOpt) (Image
}
defer done(ctx)
name, desc, err := pullCtx.Resolver.Resolve(ctx, ref)
img, err := c.fetch(ctx, pullCtx, ref)
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 {
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 (
schema1Converter *schema1.Converter
handler images.Handler
)
if desc.MediaType == images.MediaTypeDockerSchema1Manifest && pullCtx.ConvertSchema1 {
if desc.MediaType == images.MediaTypeDockerSchema1Manifest && rCtx.ConvertSchema1 {
schema1Converter = schema1.NewConverter(store, fetcher)
handler = images.Handlers(append(pullCtx.BaseHandlers, schema1Converter)...)
handler = images.Handlers(append(rCtx.BaseHandlers, schema1Converter)...)
} else {
// Get all the children for a descriptor
childrenHandler := images.ChildrenHandler(store)
// Set any children labels for that content
childrenHandler = images.SetChildrenLabels(store, childrenHandler)
// 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),
childrenHandler,
)...)
}
if err := images.Dispatch(ctx, handler, desc); err != nil {
return nil, err
return images.Image{}, err
}
if schema1Converter != nil {
desc, err = schema1Converter.Convert(ctx)
if err != nil {
return nil, err
return images.Image{}, err
}
}
img := &image{
client: c,
i: images.Image{
img := images.Image{
Name: name,
Target: desc,
Labels: pullCtx.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)
}
Labels: rCtx.Labels,
}
is := c.ImageService()
for {
if created, err := is.Create(ctx, img.i); err != nil {
if created, err := is.Create(ctx, img); err != nil {
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 image was removed, try create again
if errdefs.IsNotFound(err) {
continue
}
return nil, err
return images.Image{}, err
}
img.i = updated
img = updated
} else {
img.i = created
img = created
}
return img, nil
}
}
@ -403,10 +449,7 @@ func (c *Client) GetImage(ctx context.Context, ref string) (Image, error) {
if err != nil {
return nil, err
}
return &image{
client: c,
i: i,
}, nil
return NewImage(c, i), nil
}
// 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))
for i, img := range imgs {
images[i] = &image{
client: c,
i: img,
}
images[i] = NewImage(c, img)
}
return images, nil
}
@ -451,6 +491,8 @@ func (c *Client) NamespaceService() namespaces.Store {
if c.namespaceStore != nil {
return c.namespaceStore
}
c.connMu.Lock()
defer c.connMu.Unlock()
return NewNamespaceStoreFromClient(namespacesapi.NewNamespacesClient(c.conn))
}
@ -459,6 +501,8 @@ func (c *Client) ContainerService() containers.Store {
if c.containerStore != nil {
return c.containerStore
}
c.connMu.Lock()
defer c.connMu.Unlock()
return NewRemoteContainerStore(containersapi.NewContainersClient(c.conn))
}
@ -467,6 +511,8 @@ func (c *Client) ContentStore() content.Store {
if c.contentStore != nil {
return c.contentStore
}
c.connMu.Lock()
defer c.connMu.Unlock()
return contentproxy.NewContentStore(contentapi.NewContentClient(c.conn))
}
@ -475,6 +521,8 @@ func (c *Client) SnapshotService(snapshotterName string) snapshots.Snapshotter {
if c.snapshotters != nil {
return c.snapshotters[snapshotterName]
}
c.connMu.Lock()
defer c.connMu.Unlock()
return snproxy.NewSnapshotter(snapshotsapi.NewSnapshotsClient(c.conn), snapshotterName)
}
@ -483,6 +531,8 @@ func (c *Client) TaskService() tasks.TasksClient {
if c.taskService != nil {
return c.taskService
}
c.connMu.Lock()
defer c.connMu.Unlock()
return tasks.NewTasksClient(c.conn)
}
@ -491,6 +541,8 @@ func (c *Client) ImageService() images.Store {
if c.imageStore != nil {
return c.imageStore
}
c.connMu.Lock()
defer c.connMu.Unlock()
return NewImageStoreFromClient(imagesapi.NewImagesClient(c.conn))
}
@ -499,24 +551,32 @@ func (c *Client) DiffService() DiffService {
if c.diffService != nil {
return c.diffService
}
c.connMu.Lock()
defer c.connMu.Unlock()
return NewDiffServiceFromClient(diffapi.NewDiffClient(c.conn))
}
// IntrospectionService returns the underlying Introspection Client
func (c *Client) IntrospectionService() introspectionapi.IntrospectionClient {
c.connMu.Lock()
defer c.connMu.Unlock()
return introspectionapi.NewIntrospectionClient(c.conn)
}
// LeasesService returns the underlying Leases Client
func (c *Client) LeasesService() leasesapi.LeasesClient {
func (c *Client) LeasesService() leases.Manager {
if c.leasesService != nil {
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
func (c *Client) HealthService() grpc_health_v1.HealthClient {
c.connMu.Lock()
defer c.connMu.Unlock()
return grpc_health_v1.NewHealthClient(c.conn)
}
@ -525,11 +585,15 @@ func (c *Client) EventService() EventService {
if c.eventService != nil {
return c.eventService
}
c.connMu.Lock()
defer c.connMu.Unlock()
return NewEventServiceFromClient(eventsapi.NewEventsClient(c.conn))
}
// VersionService returns the underlying VersionClient
func (c *Client) VersionService() versionservice.VersionClient {
c.connMu.Lock()
defer c.connMu.Unlock()
return versionservice.NewVersionClient(c.conn)
}

View File

@ -18,12 +18,14 @@ package containerd
import (
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/remotes"
"google.golang.org/grpc"
)
type clientOpts struct {
defaultns string
defaultRuntime string
services *services
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
func WithDialOpts(opts []grpc.DialOption) ClientOpt {
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
// content for
func WithPlatform(platform string) RemoteOpt {
if platform == "" {
platform = platforms.Default()
}
return func(_ *Client, c *RemoteContext) error {
for _, p := range c.Platforms {
if p == platform {

View File

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

View File

@ -48,6 +48,11 @@ high performance container runtime
`
func init() {
logrus.SetFormatter(&logrus.TextFormatter{
TimestampFormat: log.RFC3339NanoFixed,
FullTimestamp: true,
})
// Discard grpc logs so that they don't mess with our stdio
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")
}
// unmount all temp mounts on boot for the server
if err := mount.CleanupTempMounts(0); err != nil {
return errors.Wrap(err, "unmounting temp mounts")
warnings, err := mount.CleanupTempMounts(0)
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
if address == "" {

View File

@ -37,8 +37,12 @@ var ociHook = cli.Command{
if err != nil {
return err
}
spec, err := loadSpec(state.Bundle)
if err != nil {
return err
}
var (
ctx = newTemplateContext(state)
ctx = newTemplateContext(state, spec)
args = []string(context.Args())
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) {
var s specs.State
if err := json.NewDecoder(r).Decode(&s); err != nil {
@ -60,9 +83,10 @@ func loadHookState(r io.Reader) (*specs.State, error) {
return &s, nil
}
func newTemplateContext(state *specs.State) *templateContext {
func newTemplateContext(state *specs.State, spec *hookSpec) *templateContext {
t := &templateContext{
state: state,
root: spec.Root.Path,
}
t.funcs = template.FuncMap{
"id": t.id,
@ -77,6 +101,7 @@ func newTemplateContext(state *specs.State) *templateContext {
type templateContext struct {
state *specs.State
root string
funcs template.FuncMap
}
@ -89,7 +114,10 @@ func (t *templateContext) bundle() 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 {

View File

@ -24,6 +24,8 @@ import (
"github.com/containerd/containerd/cmd/ctr/commands/content"
"github.com/containerd/containerd/cmd/ctr/commands/events"
"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"
"github.com/containerd/containerd/cmd/ctr/commands/plugins"
"github.com/containerd/containerd/cmd/ctr/commands/pprof"
@ -96,11 +98,13 @@ containerd CLI
content.Command,
events.Command,
images.Command,
leases.Command,
namespacesCmd.Command,
pprof.Command,
run.Command,
snapshots.Command,
tasks.Command,
install.Command,
}, extraCmds...)
app.Before = func(context *cli.Context) error {
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
func NewClient(context *cli.Context) (*containerd.Client, gocontext.Context, gocontext.CancelFunc, error) {
client, err := containerd.New(context.GlobalString("address"))
func NewClient(context *cli.Context, opts ...containerd.ClientOpt) (*containerd.Client, gocontext.Context, gocontext.CancelFunc, error) {
client, err := containerd.New(context.GlobalString("address"), opts...)
if err != nil {
return nil, nil, nil, err
}

View File

@ -105,7 +105,7 @@ var (
},
cli.StringFlag{
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),
},
cli.BoolFlag{

View File

@ -32,6 +32,7 @@ import (
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/pkg/progress"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/remotes"
digest "github.com/opencontainers/go-digest"
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'.
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 {
var (
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
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)
if err != nil {
return nil, err
return images.Image{}, err
}
ongoing := newJobs(ref)
@ -109,15 +119,19 @@ func Fetch(ctx context.Context, client *containerd.Client, ref string, cliContex
}
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))
}
}
img, err := client.Pull(pctx, ref, opts...)
img, err := client.Fetch(pctx, ref, opts...)
stopProgress()
if err != nil {
return nil, err
return images.Image{}, err
}
<-progress

View File

@ -19,10 +19,13 @@ package images
import (
"fmt"
"github.com/containerd/containerd"
"github.com/containerd/containerd/cmd/ctr/commands"
"github.com/containerd/containerd/cmd/ctr/commands/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/platforms"
"github.com/pkg/errors"
"github.com/urfave/cli"
)
@ -43,7 +46,7 @@ command. As part of this process, we do the following:
cli.StringSliceFlag{
Name: "platform",
Usage: "Pull content from a specific platform",
Value: &cli.StringSlice{platforms.Default()},
Value: &cli.StringSlice{},
},
cli.BoolFlag{
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")
// TODO: Show unpack status
fmt.Printf("unpacking %s...\n", img.Target().Digest)
err = img.Unpack(ctx, context.String("snapshotter"))
if err == nil {
fmt.Println("done")
var p []string
if context.Bool("all-platforms") {
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
}
}
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 (
gocontext "context"
"encoding/csv"
"encoding/json"
"fmt"
"io/ioutil"
"strings"
"github.com/containerd/console"
@ -37,17 +35,6 @@ import (
"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 {
return func(ctx gocontext.Context, client oci.Client, container *containers.Container, s *specs.Spec) error {
mounts := make([]specs.Mount, 0)

View File

@ -44,7 +44,7 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
if err != nil {
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 (
@ -52,6 +52,13 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli
cOpts []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, withMounts(context))
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") {
opts = append(opts, nvidia.WithGPUs(nvidia.WithDevices(context.Int("gpus")), nvidia.WithAllCapabilities))
}
if context.IsSet("config") {
var s specs.Spec
if err := loadSpec(context.String("config"), &s); err != nil {
return nil, err
}
spec = containerd.WithSpec(&s, opts...)
} else {
spec = containerd.WithNewSpec(opts...)
}
cOpts = append(cOpts, spec)
// 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
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.WithEnv(context.StringSlice("env")))
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))
}
if context.IsSet("config") {
var s specs.Spec
if err := loadSpec(context.String("config"), &s); err != nil {
return nil, err
}
spec = containerd.WithSpec(&s, opts...)
} else {
spec = containerd.WithNewSpec(opts...)
}
cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label"))))
cOpts = append(cOpts, containerd.WithImage(image))

View File

@ -26,7 +26,7 @@ import (
"github.com/containerd/console"
"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/typeurl"
ptypes "github.com/gogo/protobuf/types"
@ -36,8 +36,6 @@ import (
"github.com/urfave/cli"
)
var empty = &ptypes.Empty{}
var fifoFlags = []cli.Flag{
cli.StringFlag{
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{
Name: "shim",
Usage: "interact with a shim directly",
@ -77,13 +75,13 @@ var Command = cli.Command{
var startCommand = cli.Command{
Name: "start",
Usage: "start a container with a shim",
Usage: "start a container with a task",
Action: func(context *cli.Context) error {
service, err := getShimService(context)
service, err := getTaskService(context)
if err != nil {
return err
}
_, err = service.Start(gocontext.Background(), &shim.StartRequest{
_, err = service.Start(gocontext.Background(), &task.StartRequest{
ID: context.Args().First(),
})
return err
@ -92,13 +90,15 @@ var startCommand = cli.Command{
var deleteCommand = cli.Command{
Name: "delete",
Usage: "delete a container with a shim",
Usage: "delete a container with a task",
Action: func(context *cli.Context) error {
service, err := getShimService(context)
service, err := getTaskService(context)
if err != nil {
return err
}
r, err := service.Delete(gocontext.Background(), empty)
r, err := service.Delete(gocontext.Background(), &task.DeleteRequest{
ID: context.Args().First(),
})
if err != nil {
return err
}
@ -109,13 +109,13 @@ var deleteCommand = cli.Command{
var stateCommand = cli.Command{
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 {
service, err := getShimService(context)
service, err := getTaskService(context)
if err != nil {
return err
}
r, err := service.State(gocontext.Background(), &shim.StateRequest{
r, err := service.State(gocontext.Background(), &task.StateRequest{
ID: context.Args().First(),
})
if err != nil {
@ -128,7 +128,7 @@ var stateCommand = cli.Command{
var execCommand = cli.Command{
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,
cli.BoolFlag{
Name: "attach,a",
@ -149,7 +149,7 @@ var execCommand = cli.Command{
},
),
Action: func(context *cli.Context) error {
service, err := getShimService(context)
service, err := getTaskService(context)
if err != nil {
return err
}
@ -178,7 +178,7 @@ var execCommand = cli.Command{
return err
}
rq := &shim.ExecProcessRequest{
rq := &task.ExecProcessRequest{
ID: id,
Spec: &ptypes.Any{
TypeUrl: url,
@ -192,7 +192,7 @@ var execCommand = cli.Command{
if _, err := service.Exec(ctx, rq); err != nil {
return err
}
r, err := service.Start(ctx, &shim.StartRequest{
r, err := service.Start(ctx, &task.StartRequest{
ID: id,
})
if err != nil {
@ -211,7 +211,7 @@ var execCommand = cli.Command{
if err != nil {
return err
}
if _, err := service.ResizePty(ctx, &shim.ResizePtyRequest{
if _, err := service.ResizePty(ctx, &task.ResizePtyRequest{
ID: id,
Width: uint32(size.Width),
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")
if bindSocket == "" {
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
// 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 (
"fmt"
"runtime"
"github.com/containerd/containerd"
"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/urfave/cli"
)
@ -34,13 +37,18 @@ var checkpointCommand = cli.Command{
Name: "exit",
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 {
id := context.Args().First()
if id == "" {
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 {
return err
}
@ -55,7 +63,7 @@ var checkpointCommand = cli.Command{
}
var opts []containerd.CheckpointTaskOpts
if context.Bool("exit") {
opts = append(opts, containerd.WithExit)
opts = append(opts, withExit(context.String("runtime")))
}
checkpoint, err := task.Checkpoint(ctx, opts...)
if err != nil {
@ -65,3 +73,29 @@ var checkpointCommand = cli.Command{
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
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 {
ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio, cio.WithTerminal}, ioOpts...)...)
}
if nullIO {
if tty {
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
} else {
ioCreator = cio.NewCreator(append([]cio.Opt{cio.WithStdio}, ioOpts...)...)
}
return container.NewTask(ctx, ioCreator)
}

View File

@ -173,10 +173,7 @@ func (c *container) Image(ctx context.Context) (Image, error) {
if err != nil {
return nil, errors.Wrapf(err, "failed to get image %s for container", r.Image)
}
return &image{
client: c.client,
i: i,
}, nil
return NewImage(c.client, i), nil
}
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
func WithSpec(s *oci.Spec, opts ...oci.SpecOpts) NewContainerOpts {
return func(ctx context.Context, client *Client, c *containers.Container) error {
for _, o := range opts {
if err := o(ctx, client, c, s); err != nil {
if err := oci.ApplyOpts(ctx, client, c, s, opts...); err != nil {
return err
}
}
var err error
c.Spec, err = typeurl.MarshalAny(s)
return err

View File

@ -18,11 +18,15 @@ package containerd
import (
"context"
"errors"
"io"
containersapi "github.com/containerd/containerd/api/services/containers/v1"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/errdefs"
ptypes "github.com/gogo/protobuf/types"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
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) {
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{
Filters: filters,
})
if err != nil {
return nil, errdefs.FromGRPC(err)
}
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) {

View File

@ -322,6 +322,40 @@ func (s *store) ListStatuses(ctx context.Context, fs ...string) ([]content.Statu
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.
func (s *store) status(ingestPath string) (content.Status, error) {
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
// Detailed explaination of options can be found:
// https://github.com/nvidia/nvidia-container-runtime#supported-driver-capabilities
type Capability int
type Capability string
const (
// Compute capability
Compute Capability = iota + 1
Compute Capability = "compute"
// Compat32 capability
Compat32
Compat32 Capability = "compat32"
// Graphics capability
Graphics
Graphics Capability = "graphics"
// Utility capability
Utility
Utility Capability = "utility"
// Video capability
Video
Video Capability = "video"
// Display capability
Display
Display Capability = "display"
)
var allCaps = []Capability{
Compute,
Compat32,
Graphics,
Utility,
Video,
Display,
}
// WithGPUs adds NVIDIA gpu support to a container
func WithGPUs(opts ...Opts) oci.SpecOpts {
return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error {
@ -60,10 +69,13 @@ func WithGPUs(opts ...Opts) oci.SpecOpts {
return err
}
}
if c.OCIHookPath == "" {
path, err := exec.LookPath("containerd")
if err != nil {
return err
}
c.OCIHookPath = path
}
nvidiaPath, err := exec.LookPath(nvidiaCLI)
if err != nil {
return err
@ -72,7 +84,7 @@ func WithGPUs(opts ...Opts) oci.SpecOpts {
s.Hooks = &specs.Hooks{}
}
s.Hooks.Prestart = append(s.Hooks.Prestart, specs.Hook{
Path: path,
Path: c.OCIHookPath,
Args: append([]string{
"containerd",
"oci-hook",
@ -86,13 +98,13 @@ func WithGPUs(opts ...Opts) oci.SpecOpts {
}
type config struct {
Devices []int
DeviceUUID string
Devices []string
Capabilities []Capability
LoadKmods bool
LDCache string
LDConfig string
Requirements []string
OCIHookPath string
}
func (c *config) args() []string {
@ -108,13 +120,10 @@ func (c *config) args() []string {
"configure",
)
if len(c.Devices) > 0 {
args = append(args, fmt.Sprintf("--device=%s", strings.Join(toStrings(c.Devices), ",")))
}
if c.DeviceUUID != "" {
args = append(args, fmt.Sprintf("--device=%s", c.DeviceUUID))
args = append(args, fmt.Sprintf("--device=%s", strings.Join(c.Devices, ",")))
}
for _, c := range c.Capabilities {
args = append(args, fmt.Sprintf("--%s", capFlags[c]))
args = append(args, fmt.Sprintf("--%s", c))
}
if c.LDConfig != "" {
args = append(args, fmt.Sprintf("--ldconfig=%s", c.LDConfig))
@ -126,56 +135,47 @@ func (c *config) args() []string {
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
type Opts func(*config) error
// WithDevices adds the provided device indexes to the container
func WithDevices(ids ...int) Opts {
return func(c *config) error {
c.Devices = ids
for _, i := range ids {
c.Devices = append(c.Devices, strconv.Itoa(i))
}
return nil
}
}
// WithDeviceUUID adds the specific device UUID to the container
func WithDeviceUUID(guid string) Opts {
// WithDeviceUUIDs adds the specific device UUID to the container
func WithDeviceUUIDs(uuids ...string) Opts {
return func(c *config) error {
c.DeviceUUID = guid
c.Devices = append(c.Devices, uuids...)
return nil
}
}
// WithAllDevices adds all gpus to the container
func WithAllDevices(c *config) error {
c.DeviceUUID = "all"
c.Devices = []string{"all"}
return nil
}
// WithAllCapabilities adds all capabilities to the container for the gpus
func WithAllCapabilities(c *config) error {
for k := range capFlags {
c.Capabilities = append(c.Capabilities, k)
}
c.Capabilities = allCaps
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
func WithRequiredCUDAVersion(major, minor int) Opts {
return func(c *config) error {
@ -183,3 +183,23 @@ func WithRequiredCUDAVersion(major, minor int) Opts {
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 (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"math/rand"
"time"
"github.com/containerd/containerd/archive"

View File

@ -58,6 +58,16 @@ func NewImage(client *Client, i images.Image) Image {
return &image{
client: client,
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
i images.Image
platform 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) {
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) {
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) {
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) {
sn := i.client.SnapshotService(snapshotterName)
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 {
return false, err
}
@ -117,7 +128,7 @@ func (i *image) Unpack(ctx context.Context, snapshotterName string) error {
}
defer done(ctx)
layers, err := i.getLayers(ctx, platforms.Default())
layers, err := i.getLayers(ctx, i.platform)
if err != nil {
return err
}
@ -154,7 +165,7 @@ func (i *image) Unpack(ctx context.Context, snapshotterName string) error {
}
if unpacked {
desc, err := i.i.Config(ctx, cs, platforms.Default())
desc, err := i.i.Config(ctx, cs, i.platform)
if err != nil {
return err
}

View File

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

View File

@ -80,10 +80,7 @@ func (c *Client) Import(ctx context.Context, importer images.Importer, reader io
imgrec = updated
}
images = append(images, &image{
client: c,
i: imgrec,
})
images = append(images, NewImage(c, imgrec))
}
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"
"time"
leasesapi "github.com/containerd/containerd/api/services/leases/v1"
"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
func (c *Client) WithLease(ctx context.Context) (context.Context, func(context.Context) error, error) {
_, ok := leases.Lease(ctx)
_, ok := leases.FromContext(ctx)
if ok {
return ctx, func(context.Context) error {
return 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 {
return nil, nil, err
}
ctx = leases.WithLease(ctx, l.ID())
ctx = leases.WithLease(ctx, l.ID)
return ctx, func(ctx context.Context) error {
return l.Delete(ctx)
return ls.Delete(ctx, l)
}, 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)
}
// Lease returns the lease from the context.
func Lease(ctx context.Context) (string, bool) {
// FromContext returns the lease from the context.
func FromContext(ctx context.Context) (string, bool) {
lid, ok := ctx.Value(leaseKey{}).(string)
if !ok {
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.
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.
// It supports trace level.
func ParseLevel(lvl string) (logrus.Level, error) {

View File

@ -23,6 +23,7 @@ import (
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/filters"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/leases"
)
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) {
if len(m) == 0 {
return "", false

View File

@ -72,6 +72,7 @@ var (
bucketKeyCreatedAt = []byte("createdat")
bucketKeyExpected = []byte("expected")
bucketKeyRef = []byte("ref")
bucketKeyExpireAt = []byte("expireat")
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
}
if err := removeIngestLease(ctx, tx, ref); err != nil {
return err
}
// if not shared content, delete active ingest on backend
if expected == "" {
return cs.Store.Abort(ctx, bref)
@ -395,6 +399,11 @@ func (cs *contentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (
return err
}
leased, err := addIngestLease(ctx, tx, wOpts.Ref)
if err != nil {
return err
}
brefb := bkt.Get(bucketKeyRef)
if brefb == nil {
sid, err := bkt.NextSequence()
@ -409,6 +418,18 @@ func (cs *contentStore) Writer(ctx context.Context, opts ...content.WriterOpt) (
} else {
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 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 {
return err
}
if err := removeIngestLease(ctx, tx, nw.ref); err != nil {
return err
}
return addContentLease(ctx, tx, dgst)
})
}
@ -697,6 +721,30 @@ func writeInfo(info *content.Info, bkt *bolt.Bucket) error {
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) {
cs.l.Lock()
t1 := time.Now()
@ -707,7 +755,8 @@ func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, er
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 {
v1bkt := tx.Bucket(bucketKeyVersion)
if v1bkt == nil {
@ -730,7 +779,7 @@ func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, er
if bbkt != nil {
if err := bbkt.ForEach(func(ck, cv []byte) error {
if cv == nil {
seen[string(ck)] = struct{}{}
contentSeen[string(ck)] = struct{}{}
}
return nil
}); err != nil {
@ -742,9 +791,17 @@ func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, er
if ibkt != nil {
if err := ibkt.ForEach(func(ref, v []byte) error {
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 {
seen[string(expected)] = struct{}{}
contentSeen[string(expected)] = struct{}{}
}
bref := bkt.Get(bucketKeyRef)
if len(bref) > 0 {
ingestSeen[string(bref)] = struct{}{}
}
}
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 {
if _, ok := seen[info.Digest.String()]; !ok {
if _, ok := contentSeen[info.Digest.String()]; !ok {
if err := cs.Store.Delete(ctx, info.Digest); err != nil {
return err
}
@ -768,5 +825,40 @@ func (cs *contentStore) garbageCollect(ctx context.Context) (d time.Duration, er
}
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
}

View File

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

View File

@ -21,6 +21,7 @@ import (
"context"
"fmt"
"strings"
"time"
"github.com/boltdb/bolt"
"github.com/containerd/containerd/gc"
@ -39,12 +40,17 @@ const (
ResourceContainer
// ResourceTask specifies a task resource
ResourceTask
// ResourceLease specifies a lease
ResourceLease
// ResourceIngest specifies a content ingest
ResourceIngest
)
var (
labelGCRoot = []byte("containerd.io/gc.root")
labelGCSnapRef = []byte("containerd.io/gc.ref.snapshot.")
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 {
@ -53,6 +59,8 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
return nil
}
expThreshold := time.Now()
// iterate through each namespace
v1c := v1bkt.Cursor()
@ -71,6 +79,30 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
}
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)
if cbkt != nil {
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
}); err != nil {
return err
@ -141,8 +187,30 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
cbkt := nbkt.Bucket(bucketKeyObjectContent)
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 err := cbkt.ForEach(func(k, v []byte) error {
if v != nil {
@ -153,6 +221,7 @@ func scanRoots(ctx context.Context, tx *bolt.Tx, nc chan<- gc.Node) error {
return err
}
}
}
cbkt = nbkt.Bucket(bucketKeyObjectContainers)
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)
} 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
@ -261,6 +343,18 @@ func scanAll(ctx context.Context, tx *bolt.Tx, fn func(ctx context.Context, n gc
nbkt := v1bkt.Bucket(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)
if sbkt != nil {
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)
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 err := cbkt.ForEach(func(k, v []byte) error {
if v != nil {
@ -296,6 +402,7 @@ func scanAll(ctx context.Context, tx *bolt.Tx, fn func(ctx context.Context, n gc
}
}
}
}
return nil
}
@ -334,6 +441,20 @@ func remove(ctx context.Context, tx *bolt.Tx, node gc.Node) error {
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

View File

@ -22,6 +22,7 @@ import (
"github.com/boltdb/bolt"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/filters"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/metadata/boltutil"
"github.com/containerd/containerd/namespaces"
@ -29,17 +30,6 @@ import (
"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
// and also returns existing leases
type LeaseManager struct {
@ -55,49 +45,56 @@ func NewLeaseManager(tx *bolt.Tx) *LeaseManager {
}
// 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)
if err != nil {
return Lease{}, err
return leases.Lease{}, err
}
topbkt, err := createBucketIfNotExists(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
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 == bolt.ErrBucketExists {
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()
createdAt, err := t.MarshalBinary()
if err != nil {
return Lease{}, err
return leases.Lease{}, err
}
if err := txbkt.Put(bucketKeyCreatedAt, createdAt); err != nil {
return Lease{}, err
return leases.Lease{}, err
}
if labels != nil {
if err := boltutil.WriteLabels(txbkt, labels); err != nil {
return Lease{}, err
if l.Labels != nil {
if err := boltutil.WriteLabels(txbkt, l.Labels); err != nil {
return leases.Lease{}, err
}
}
l.CreatedAt = t
return Lease{
ID: lid,
CreatedAt: t,
Labels: labels,
}, nil
return l, nil
}
// 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)
if err != nil {
return err
@ -105,26 +102,34 @@ func (lm *LeaseManager) Delete(ctx context.Context, lid string) error {
topbkt := getBucket(lm.tx, bucketKeyVersion, []byte(namespace), bucketKeyObjectLeases)
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 nil
}
// 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)
if err != nil {
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)
if topbkt == nil {
return leases, nil
return ll, nil
}
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)
l := Lease{
l := leases.Lease{
ID: string(k),
}
@ -150,21 +155,20 @@ func (lm *LeaseManager) List(ctx context.Context, includeResources bool, filter
}
l.Labels = labels
// TODO: Read Snapshots
// TODO: Read Content
leases = append(leases, l)
if filter.Match(adaptLease(l)) {
ll = append(ll, l)
}
return nil
}); err != nil {
return nil, err
}
return leases, nil
return ll, nil
}
func addSnapshotLease(ctx context.Context, tx *bolt.Tx, snapshotter, key string) error {
lid, ok := leases.Lease(ctx)
lid, ok := leases.FromContext(ctx)
if !ok {
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 {
lid, ok := leases.Lease(ctx)
lid, ok := leases.FromContext(ctx)
if !ok {
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 {
lid, ok := leases.Lease(ctx)
lid, ok := leases.FromContext(ctx)
if !ok {
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 {
lid, ok := leases.Lease(ctx)
lid, ok := leases.FromContext(ctx)
if !ok {
return nil
}
@ -255,3 +259,51 @@ func removeContentLease(ctx context.Context, tx *bolt.Tx, dgst digest.Digest) er
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"
eventstypes "github.com/containerd/containerd/api/events"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/platforms"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/linux"
"github.com/containerd/containerd/runtime/v1/linux"
metrics "github.com/docker/go-metrics"
"github.com/sirupsen/logrus"
)
@ -80,16 +81,21 @@ type cgroupsMonitor struct {
}
func (m *cgroupsMonitor) Monitor(c runtime.Task) error {
info := c.Info()
t := c.(*linux.Task)
if err := m.collector.Add(c); err != nil {
return err
}
t, ok := c.(*linux.Task)
if !ok {
return nil
}
cg, err := t.Cgroup()
if err != nil {
if errdefs.IsNotFound(err) {
return nil
}
return err
}
if err := m.collector.Add(info.ID, info.Namespace, cg); err != nil {
return err
}
err = m.oom.Add(info.ID, info.Namespace, cg, m.trigger)
err = m.oom.Add(c.ID(), c.Namespace(), cg, m.trigger)
if err == cgroups.ErrMemoryNotSupported {
logrus.WithError(err).Warn("OOM monitoring failed")
return nil
@ -98,17 +104,7 @@ func (m *cgroupsMonitor) Monitor(c runtime.Task) error {
}
func (m *cgroupsMonitor) Stop(c runtime.Task) error {
info := c.Info()
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)
m.collector.Remove(c)
return nil
}

View File

@ -19,12 +19,16 @@
package cgroups
import (
"context"
"errors"
"fmt"
"sync"
"github.com/containerd/cgroups"
"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"
"github.com/prometheus/client_golang/prometheus"
)
@ -49,7 +53,7 @@ func newCollector(ns *metrics.Namespace) *collector {
// add machine cpus and memory info
c := &collector{
ns: ns,
cgroups: make(map[string]*task),
tasks: make(map[string]runtime.Task),
}
c.metrics = append(c.metrics, pidMetrics...)
c.metrics = append(c.metrics, cpuMetrics...)
@ -61,12 +65,6 @@ func newCollector(ns *metrics.Namespace) *collector {
return c
}
type task struct {
id string
namespace string
cgroup cgroups.Cgroup
}
func taskID(id, namespace string) string {
return fmt.Sprintf("%s-%s", id, namespace)
}
@ -76,7 +74,7 @@ func taskID(id, namespace string) string {
type collector struct {
mu sync.RWMutex
cgroups map[string]*task
tasks map[string]runtime.Task
ns *metrics.Namespace
metrics []*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) {
c.mu.RLock()
wg := &sync.WaitGroup{}
for _, t := range c.cgroups {
for _, t := range c.tasks {
wg.Add(1)
go c.collect(t.id, t.namespace, t.cgroup, ch, true, wg)
go c.collect(t, ch, true, wg)
}
storedLoop:
for {
@ -109,45 +107,52 @@ storedLoop:
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 {
defer wg.Done()
}
stats, err := cg.Stat(cgroups.IgnoreNotExist)
ctx := namespaces.WithNamespace(context.Background(), t.Namespace())
stats, err := t.Stats(ctx)
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
}
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
func (c *collector) Add(id, namespace string, cg cgroups.Cgroup) error {
func (c *collector) Add(t runtime.Task) error {
if c.ns == nil {
return nil
}
c.mu.Lock()
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
}
c.cgroups[taskID(id, namespace)] = &task{
id: id,
namespace: namespace,
cgroup: cg,
}
c.tasks[id] = t
return nil
}
// 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 {
return
}
c.mu.Lock()
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 {
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++ {
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
func CleanupTempMounts(flags int) error {
func CleanupTempMounts(flags int) (warnings []error, err error) {
mounts, err := Self()
if err != nil {
return err
return nil, err
}
var toUnmount []string
for _, m := range mounts {
@ -53,11 +53,12 @@ func CleanupTempMounts(flags int) error {
sort.Sort(sort.Reverse(sort.StringSlice(toUnmount)))
for _, path := range toUnmount {
if err := UnmountAll(path, flags); err != nil {
return err
warnings = append(warnings, err)
continue
}
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
func CleanupTempMounts(flags int) error {
return nil
func CleanupTempMounts(flags int) ([]error, error) {
return nil, nil
}

View File

@ -34,10 +34,23 @@ func GenerateSpec(ctx context.Context, client Client, c *containers.Container, o
if err != nil {
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 {
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 (
"context"
"encoding/json"
"io/ioutil"
"strings"
"github.com/containerd/containerd/containers"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
)
// 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
func WithProcessArgs(args ...string) SpecOpts {
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
func WithImageConfig(image Image) SpecOpts {
return WithImageConfigArgs(image, nil)
}
// WithImageConfigArgs configures the spec to from the configuration of an Image with additional args that
// replaces the CMD of the image
func WithImageConfigArgs(image Image, args []string) SpecOpts {
return func(ctx context.Context, client Client, c *containers.Container, s *Spec) error {
ic, err := image.Config(ctx)
if err != nil {
@ -133,6 +139,9 @@ func WithImageConfig(image Image) SpecOpts {
setProcess(s)
s.Process.Env = append(s.Process.Env, config.Env...)
cmd := config.Cmd
if len(args) > 0 {
cmd = args
}
s.Process.Args = append(config.Entrypoint, cmd...)
cwd := config.WorkingDir
if cwd == "" {
@ -348,8 +357,8 @@ func WithUIDGID(uid, gid uint32) SpecOpts {
// WithUserID sets the correct UID and GID for the container based
// on the image's /etc/passwd contents. If /etc/passwd does not exist,
// or uid is not found in /etc/passwd, it sets gid to be the same with
// uid, and not returns error.
// or uid is not found in /etc/passwd, it sets the requested uid,
// additionally sets the gid to 0, and does not return an error.
func WithUserID(uid uint32) SpecOpts {
return func(ctx context.Context, client Client, c *containers.Container, s *Spec) (err error) {
setProcess(s)
@ -362,7 +371,7 @@ func WithUserID(uid uint32) SpecOpts {
})
if err != nil {
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 err
@ -388,7 +397,7 @@ func WithUserID(uid uint32) SpecOpts {
})
if err != nil {
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 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)
if err != nil {
return nil, err
return err
}
s := &Spec{
*s = Spec{
Version: specs.Version,
Root: &specs.Root{
Path: defaultRootfsPath,
@ -183,5 +184,5 @@ func createDefaultSpec(ctx context.Context, id string) (*Spec, error) {
Namespaces: defaultNamespaces(),
},
}
return s, nil
return nil
}

View File

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

View File

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

View File

@ -18,7 +18,6 @@ package schema1
import (
"bytes"
"compress/gzip"
"context"
"encoding/base64"
"encoding/json"
@ -31,6 +30,7 @@ import (
"golang.org/x/sync/errgroup"
"github.com/containerd/containerd/archive/compression"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/images"
@ -257,6 +257,7 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
var (
ref = remotes.MakeRefKey(ctx, desc)
calc = newBlobStateCalculator()
compressMethod = compression.Gzip
)
// 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()
gr, err := gzip.NewReader(content.NewReader(ra))
r, err := compression.DecompressStream(content.NewReader(ra))
if err != nil {
return err
}
defer gr.Close()
_, err = io.Copy(calc, gr)
compressMethod = r.GetCompression()
_, err = io.Copy(calc, r)
r.Close()
if err != nil {
return err
}
@ -303,13 +305,14 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
pr, pw := io.Pipe()
eg.Go(func() error {
gr, err := gzip.NewReader(pr)
r, err := compression.DecompressStream(pr)
if err != nil {
return err
}
defer gr.Close()
_, err = io.Copy(calc, gr)
compressMethod = r.GetCompression()
_, err = io.Copy(calc, r)
r.Close()
pr.CloseWithError(err)
return err
})
@ -333,6 +336,11 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
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()
c.mu.Lock()
@ -342,6 +350,7 @@ func (c *Converter) fetchBlob(ctx context.Context, desc ocispec.Descriptor) erro
return nil
}
func (c *Converter) schema1ManifestHistory() ([]ocispec.History, []digest.Digest, error) {
if c.pulledManifest == nil {
return nil, nil, errors.New("missing schema 1 manifest for conversion")

View File

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

View File

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

View File

@ -46,14 +46,16 @@ type Process interface {
Start(context.Context) error
// Wait for the process to exit
Wait(context.Context) (*Exit, error)
// Delete deletes the process
Delete(context.Context) (*Exit, error)
}
// Task is the runtime object for an executing container
type Task interface {
Process
// Information of the container
Info() TaskInfo
// Namespace that the task exists in
Namespace() string
// Pause pauses the container process
Pause(context.Context) error
// Resume unpauses the container process
@ -64,14 +66,12 @@ type Task interface {
Pids(context.Context) ([]ProcessInfo, error)
// Checkpoint checkpoints a container to an image with live system data
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(context.Context, *types.Any) error
// Process returns a process within the task for the provided id
Process(context.Context, string) (Process, error)
// Metrics returns runtime specific metrics for a task
Metrics(context.Context) (interface{}, error)
// Stats returns runtime specific metrics for a task
Stats(context.Context) (*types.Any, error)
}
// 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
func (l *TaskList) GetAll(ctx context.Context) ([]Task, error) {
func (l *TaskList) GetAll(ctx context.Context, noNS bool) ([]Task, error) {
l.mu.Lock()
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)
if err != nil {
return nil, err
}
var o []Task
tasks, ok := l.tasks[namespace]
if !ok {
return o, nil

View File

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

View File

@ -89,7 +89,7 @@ func (e *execProcess) ExitedAt() time.Time {
func (e *execProcess) setExited(status int) {
e.status = status
e.exited = time.Now()
e.parent.platform.ShutdownConsole(context.Background(), e.console)
e.parent.Platform.ShutdownConsole(context.Background(), e.console)
close(e.waitBlock)
}
@ -180,7 +180,7 @@ func (e *execProcess) start(ctx context.Context) (err error) {
if err != nil {
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")
}
} else if !e.stdio.IsNull() {

View File

@ -33,11 +33,9 @@ import (
"github.com/containerd/console"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/runtime/linux/runctypes"
"github.com/containerd/containerd/runtime/proc"
"github.com/containerd/fifo"
runc "github.com/containerd/go-runc"
"github.com/containerd/typeurl"
google_protobuf "github.com/gogo/protobuf/types"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@ -59,12 +57,12 @@ type Init struct {
waitBlock chan struct{}
workDir string
WorkDir string
id string
bundle string
Bundle string
console console.Console
platform proc.Platform
Platform proc.Platform
io runc.IO
runtime *runc.Runc
status int
@ -73,9 +71,11 @@ type Init struct {
closers []io.Closer
stdin io.Closer
stdio proc.Stdio
rootfs string
Rootfs string
IoUID int
IoGID int
NoPivotRoot bool
NoNewKeyring bool
}
// 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
func New(context context.Context, path, workDir, runtimeRoot, namespace, criu string, systemdCgroup bool, platform proc.Platform, r *CreateConfig) (*Init, error) {
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)
// New returns a new process
func New(id string, runtime *runc.Runc, stdio proc.Stdio) *Init {
p := &Init{
id: r.ID,
bundle: r.Bundle,
id: id,
runtime: runtime,
platform: platform,
stdio: proc.Stdio{
Stdin: r.Stdin,
Stdout: r.Stdout,
Stderr: r.Stderr,
Terminal: r.Terminal,
},
rootfs: rootfs,
workDir: workDir,
stdio: stdio,
status: 0,
waitBlock: make(chan struct{}),
IoUID: int(options.IoUid),
IoGID: int(options.IoGid),
}
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 (
err error
socket *runc.Socket
)
if r.Terminal {
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()
} else if hasNoIO(r) {
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 {
if p.io, err = runc.NewPipeIO(int(options.IoUid), int(options.IoGid)); err != nil {
return nil, errors.Wrap(err, "failed to create OCI runtime io pipes")
if p.io, err = runc.NewPipeIO(p.IoUID, p.IoGID); err != nil {
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 != "" {
opts := &runc.RestoreOpts{
CheckpointOpts: runc.CheckpointOpts{
ImagePath: r.Checkpoint,
WorkDir: p.workDir,
WorkDir: p.WorkDir,
ParentPath: r.ParentCheckpoint,
},
PidFile: pidFile,
IO: p.io,
NoPivot: options.NoPivotRoot,
NoPivot: p.NoPivotRoot,
Detach: true,
NoSubreaper: true,
}
@ -185,25 +145,24 @@ func New(context context.Context, path, workDir, runtimeRoot, namespace, criu st
p: p,
opts: opts,
}
success = true
return p, nil
return nil
}
opts := &runc.CreateOpts{
PidFile: pidFile,
IO: p.io,
NoPivot: options.NoPivotRoot,
NoNewKeyring: options.NoNewKeyring,
NoPivot: p.NoPivotRoot,
NoNewKeyring: p.NoNewKeyring,
}
if socket != nil {
opts.ConsoleSocket = socket
}
if err := p.runtime.Create(context, r.ID, r.Bundle, opts); err != nil {
return nil, p.runtimeError(err, "OCI runtime create failed")
if err := p.runtime.Create(ctx, r.ID, r.Bundle, opts); err != nil {
return p.runtimeError(err, "OCI runtime create failed")
}
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 {
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.closers = append(p.closers, sc)
@ -212,27 +171,26 @@ func New(context context.Context, path, workDir, runtimeRoot, namespace, criu st
if socket != nil {
console, err := socket.ReceiveMaster()
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 {
return nil, errors.Wrap(err, "failed to start console copy")
return errors.Wrap(err, "failed to start console copy")
}
p.console = console
} else if !hasNoIO(r) {
if err := copyPipes(context, p.io, r.Stdin, r.Stdout, r.Stderr, &p.wg, &copyWaitGroup); err != nil {
return nil, errors.Wrap(err, "failed to start io pipe copy")
if err := copyPipes(ctx, p.io, r.Stdin, r.Stdout, r.Stderr, &p.wg, &copyWaitGroup); err != nil {
return errors.Wrap(err, "failed to start io pipe copy")
}
}
copyWaitGroup.Wait()
pid, err := runc.ReadPidFile(pidFile)
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
success = true
return p, nil
return nil
}
// Wait for the process to exit
@ -286,7 +244,7 @@ func (p *Init) start(context context.Context) error {
func (p *Init) setExited(status int) {
p.exited = time.Now()
p.status = status
p.platform.ShutdownConsole(context.Background(), p.console)
p.Platform.ShutdownConsole(context.Background(), p.console)
close(p.waitBlock)
}
@ -312,7 +270,7 @@ func (p *Init) delete(context context.Context) error {
}
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")
if err == nil {
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 {
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
if !options.Exit {
if !r.Exit {
actions = append(actions, runc.LeaveRunning)
}
work := filepath.Join(p.workDir, "criu-work")
work := filepath.Join(p.WorkDir, "criu-work")
defer os.RemoveAll(work)
if err := p.runtime.Checkpoint(context, p.id, &runc.CheckpointOpts{
WorkDir: work,
ImagePath: r.Path,
AllowOpenTCP: options.OpenTcp,
AllowExternalUnixSockets: options.ExternalUnixSockets,
AllowTerminal: options.Terminal,
FileLocks: options.FileLocks,
EmptyNamespaces: options.EmptyNamespaces,
AllowOpenTCP: r.AllowOpenTCP,
AllowExternalUnixSockets: r.AllowExternalUnixSockets,
AllowTerminal: r.AllowTerminal,
FileLocks: r.FileLocks,
EmptyNamespaces: r.EmptyNamespaces,
}, 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 {
log.G(context).Error(err)
}

View File

@ -209,7 +209,7 @@ func (s *createdCheckpointState) Start(ctx context.Context) error {
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")
}
if sio.Stdin != "" {
@ -226,7 +226,7 @@ func (s *createdCheckpointState) Start(ctx context.Context) error {
if err != nil {
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 {
return errors.Wrap(err, "failed to start console copy")
}

View File

@ -56,5 +56,10 @@ type ExecConfig struct {
// CheckpointConfig holds task checkpoint configuration
type CheckpointConfig struct {
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/errdefs"
"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/pkg/errors"
)
@ -149,3 +149,18 @@ func (p *Process) Wait(ctx context.Context) (*runtime.Exit, error) {
Status: r.ExitStatus,
}, 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/plugin"
"github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/linux/proc"
"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"
"github.com/containerd/typeurl"
ptypes "github.com/gogo/protobuf/types"
@ -69,7 +69,6 @@ func init() {
ID: "linux",
InitFn: New,
Requires: []plugin.Type{
plugin.TaskMonitorPlugin,
plugin.MetadataPlugin,
},
Config: &Config{
@ -105,10 +104,6 @@ func New(ic *plugin.InitContext) (interface{}, error) {
if err := os.MkdirAll(ic.State, 0711); err != nil {
return nil, err
}
monitor, err := ic.Get(plugin.TaskMonitorPlugin)
if err != nil {
return nil, err
}
m, err := ic.Get(plugin.MetadataPlugin)
if err != nil {
return nil, err
@ -117,7 +112,6 @@ func New(ic *plugin.InitContext) (interface{}, error) {
r := &Runtime{
root: ic.Root,
state: ic.State,
monitor: monitor.(runtime.TaskMonitor),
tasks: runtime.NewTaskList(),
db: m.(*metadata.DB),
address: ic.Address,
@ -128,8 +122,6 @@ func New(ic *plugin.InitContext) (interface{}, error) {
if err != nil {
return nil, err
}
// TODO: need to add the tasks to the monitor
for _, t := range tasks {
if err := r.tasks.AddWithNamespace(t.namespace, t); err != nil {
return nil, err
@ -144,7 +136,6 @@ type Runtime struct {
state string
address string
monitor runtime.TaskMonitor
tasks *runtime.TaskList
db *metadata.DB
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)
if !r.config.NoShim {
var cgroup string
if opts.Options != nil {
v, err := typeurl.UnmarshalAny(opts.Options)
if opts.TaskOptions != nil {
v, err := typeurl.UnmarshalAny(opts.TaskOptions)
if err != nil {
return nil, err
}
@ -205,14 +196,6 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
}
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{
"id": id,
"namespace": namespace,
@ -252,7 +235,7 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
Stderr: opts.IO.Stderr,
Terminal: opts.IO.Terminal,
Checkpoint: opts.Checkpoint,
Options: opts.Options,
Options: opts.TaskOptions,
}
for _, m := range opts.Rootfs {
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 {
return nil, errdefs.FromGRPC(err)
}
t, err := newTask(id, namespace, int(cr.Pid), s, r.monitor, r.events,
proc.NewRunc(ropts.RuntimeRoot, sopts.Bundle, namespace, rt, ropts.CriuPath, ropts.SystemdCgroup))
t, err := newTask(id, namespace, int(cr.Pid), s, r.events,
proc.NewRunc(ropts.RuntimeRoot, sopts.Bundle, namespace, rt, ropts.CriuPath, ropts.SystemdCgroup), r.tasks, bundle)
if err != nil {
return nil, err
}
if err := r.tasks.Add(ctx, t); err != nil {
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{
ContainerID: sopts.ID,
Bundle: sopts.Bundle,
@ -300,56 +273,9 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
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
func (r *Runtime) Tasks(ctx context.Context) ([]runtime.Task, error) {
return r.tasks.GetAll(ctx)
func (r *Runtime) Tasks(ctx context.Context, all bool) ([]runtime.Task, error) {
return r.tasks.GetAll(ctx, all)
}
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
}
t, err := newTask(id, ns, pid, s, r.monitor, r.events,
proc.NewRunc(ropts.RuntimeRoot, bundle.path, ns, ropts.Runtime, ropts.CriuPath, ropts.SystemdCgroup))
t, err := newTask(id, ns, pid, s, r.events,
proc.NewRunc(ropts.RuntimeRoot, bundle.path, ns, ropts.Runtime, ropts.CriuPath, ropts.SystemdCgroup), r.tasks, bundle)
if err != nil {
log.G(ctx).WithError(err).Error("loading task type")
continue

View File

@ -28,11 +28,13 @@ import (
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events/exchange"
"github.com/containerd/containerd/identifiers"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/shim/client"
shim "github.com/containerd/containerd/runtime/shim/v1"
"github.com/containerd/containerd/runtime/v1/shim/client"
shim "github.com/containerd/containerd/runtime/v1/shim/v1"
runc "github.com/containerd/go-runc"
"github.com/containerd/ttrpc"
"github.com/containerd/typeurl"
"github.com/gogo/protobuf/types"
"github.com/pkg/errors"
)
@ -45,12 +47,12 @@ type Task struct {
shim *client.Client
namespace string
cg cgroups.Cgroup
monitor runtime.TaskMonitor
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 (
err error
cg cgroups.Cgroup
@ -67,9 +69,9 @@ func newTask(id, namespace string, pid int, shim *client.Client, monitor runtime
shim: shim,
namespace: namespace,
cg: cg,
monitor: monitor,
events: events,
runtime: runtime,
tasks: list,
bundle: bundle,
}, nil
}
@ -78,13 +80,35 @@ func (t *Task) ID() string {
return t.id
}
// Info returns task information about the runtime and namespace
func (t *Task) Info() runtime.TaskInfo {
return runtime.TaskInfo{
ID: t.id,
Runtime: pluginID,
Namespace: t.namespace,
// Namespace of the task
func (t *Task) Namespace() string {
return 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
@ -107,9 +131,6 @@ func (t *Task) Start(ctx context.Context) error {
t.mu.Lock()
t.cg = cg
t.mu.Unlock()
if err := t.monitor.Monitor(t); err != nil {
return err
}
}
t.events.Publish(ctx, runtime.TaskStartEventTopic, &eventstypes.TaskStart{
ContainerID: t.id,
@ -270,21 +291,6 @@ func (t *Task) Checkpoint(ctx context.Context, path string, options *types.Any)
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
func (t *Task) Update(ctx context.Context, resources *types.Any) error {
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
}
// Metrics returns runtime specific system level metric information for the task
func (t *Task) Metrics(ctx context.Context) (interface{}, error) {
// Stats returns runtime specific system level metric information for the task
func (t *Task) Stats(ctx context.Context) (*types.Any, error) {
t.mu.Lock()
defer t.mu.Unlock()
if t.cg == nil {
@ -318,7 +324,7 @@ func (t *Task) Metrics(ctx context.Context) (interface{}, error) {
if err != nil {
return nil, err
}
return stats, nil
return typeurl.MarshalAny(stats)
}
// 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/log"
"github.com/containerd/containerd/runtime/shim"
shimapi "github.com/containerd/containerd/runtime/shim/v1"
"github.com/containerd/containerd/runtime/v1/shim"
shimapi "github.com/containerd/containerd/runtime/v1/shim/v1"
"github.com/containerd/containerd/sys"
ptypes "github.com/gogo/protobuf/types"
)

View File

@ -23,7 +23,7 @@ import (
"path/filepath"
"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"
)

View File

@ -22,6 +22,7 @@ import (
"context"
"fmt"
"os"
"path/filepath"
"sync"
"github.com/containerd/console"
@ -30,12 +31,13 @@ import (
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/events"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/runtime"
"github.com/containerd/containerd/runtime/linux/proc"
"github.com/containerd/containerd/runtime/linux/runctypes"
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"
"github.com/containerd/typeurl"
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
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()
defer s.mu.Unlock()
@ -121,16 +123,8 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (*sh
Options: m.Options,
})
}
process, err := proc.New(
ctx,
s.config.Path,
s.config.WorkDir,
s.config.RuntimeRoot,
s.config.Namespace,
s.config.Criu,
s.config.SystemdCgroup,
s.platform,
&proc.CreateConfig{
config := &proc.CreateConfig{
ID: r.ID,
Bundle: r.Bundle,
Runtime: r.Runtime,
@ -142,11 +136,42 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (*sh
Checkpoint: r.Checkpoint,
ParentCheckpoint: r.ParentCheckpoint,
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 {
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
s.id = r.ID
s.bundle = r.Bundle
@ -414,9 +439,22 @@ func (s *Service) Checkpoint(ctx context.Context, r *shimapi.CheckpointTaskReque
if p == nil {
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{
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 {
return nil, errdefs.ToGRPC(err)
}
@ -545,3 +583,32 @@ func getTopic(ctx context.Context, e interface{}) string {
}
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