Update containerd vendor to 1.2 beta

Signed-off-by: Derek McGowan <derek@mcgstyle.net>
This commit is contained in:
Derek McGowan 2018-08-17 13:08:22 -07:00
parent f57f260b49
commit 98e23f2a8e
No known key found for this signature in database
GPG key ID: F58C5D0A4405ACDB
118 changed files with 3455 additions and 783 deletions

View file

@ -10,7 +10,7 @@ import (
"path/filepath"
"strconv"
"github.com/containerd/containerd/runtime/linux"
"github.com/containerd/containerd/runtime/v1/linux"
"github.com/docker/docker/cmd/dockerd/hack"
"github.com/docker/docker/daemon"
"github.com/docker/docker/libcontainerd"

View file

@ -563,7 +563,17 @@ func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDi
opts := []containerd.CheckpointTaskOpts{}
if exit {
opts = append(opts, containerd.WithExit)
opts = append(opts, func(r *containerd.CheckpointTaskInfo) error {
if r.Options == nil {
r.Options = &runctypes.CheckpointOptions{
Exit: true,
}
} else {
opts, _ := r.Options.(*runctypes.CheckpointOptions)
opts.Exit = true
}
return nil
})
}
img, err := p.(containerd.Task).Checkpoint(ctx, opts...)
if err != nil {

View file

@ -76,7 +76,7 @@ google.golang.org/grpc v1.12.0
# This does not need to match RUNC_COMMIT as it is used for helper packages but should be newer or equal
github.com/opencontainers/runc ad0f5255060d36872be04de22f8731f38ef2d7b1
github.com/opencontainers/runtime-spec v1.0.1
github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce # v1.0.1-43-gd810dbc
github.com/opencontainers/image-spec v1.0.1
github.com/seccomp/libseccomp-golang 32f571b70023028bd57d9288c20efbcb237f3ce0
@ -114,12 +114,12 @@ github.com/googleapis/gax-go v2.0.0
google.golang.org/genproto 694d95ba50e67b2e363f3483057db5d4910c18f9
# containerd
github.com/containerd/containerd b41633746ed4833f52c3c071e8edcfa2713e5677
github.com/containerd/containerd v1.2.0-beta.0
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/continuity 0377f7d767206f3a9e8881d0f02267b0d89c7a62
github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130
github.com/containerd/console 5d1b48d6114b8c9666f0c8b916f871af97b0a761
github.com/containerd/go-runc f271fa2021de855d4d918dbef83c5fe19db1bdd
github.com/containerd/continuity d3c23511c1bf5851696cba83143d9cbcd666869b
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
github.com/containerd/console 4d8a41f4ce5b9bae77c41786ea2458330f43f081
github.com/containerd/go-runc edcf3de1f4971445c42d61f20d506b30612aa031
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containerd/ttrpc 94dde388801693c54f88a6596f713b51a8b30b2d
github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef

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

@ -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 compression
import (
"bufio"
"bytes"
"compress/gzip"
"fmt"
"io"
"sync"
)
type (
// Compression is the state represents if compressed or not.
Compression int
)
const (
// Uncompressed represents the uncompressed.
Uncompressed Compression = iota
// Gzip is gzip compression algorithm.
Gzip
)
var (
bufioReader32KPool = &sync.Pool{
New: func() interface{} { return bufio.NewReaderSize(nil, 32*1024) },
}
)
// 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
}
func (r *readCloserWrapper) Close() error {
if r.closer != nil {
return r.closer()
}
return nil
}
func (r *readCloserWrapper) GetCompression() Compression {
return r.compression
}
type writeCloserWrapper struct {
io.Writer
closer func() error
}
func (w *writeCloserWrapper) Close() error {
if w.closer != nil {
w.closer()
}
return nil
}
// DetectCompression detects the compression algorithm of the source.
func DetectCompression(source []byte) Compression {
for compression, m := range map[Compression][]byte{
Gzip: {0x1F, 0x8B, 0x08},
} {
if len(source) < len(m) {
// Len too short
continue
}
if bytes.Equal(m, source[:len(m)]) {
return compression
}
}
return Uncompressed
}
// DecompressStream decompresses the archive and returns a ReaderCloser with the decompressed archive.
func DecompressStream(archive io.Reader) (DecompressReadCloser, error) {
buf := bufioReader32KPool.Get().(*bufio.Reader)
buf.Reset(archive)
bs, err := buf.Peek(10)
if err != nil && err != io.EOF {
// Note: we'll ignore any io.EOF error because there are some odd
// cases where the layer.tar file will be empty (zero bytes) and
// that results in an io.EOF from the Peek() call. So, in those
// cases we'll just treat it as a non-compressed stream and
// that means just create an empty layer.
// See Issue docker/docker#18170
return nil, err
}
closer := func() error {
buf.Reset(nil)
bufioReader32KPool.Put(buf)
return nil
}
switch compression := DetectCompression(bs); compression {
case Uncompressed:
readBufWrapper := &readCloserWrapper{buf, compression, closer}
return readBufWrapper, nil
case Gzip:
gzReader, err := gzip.NewReader(buf)
if err != nil {
return nil, err
}
readBufWrapper := &readCloserWrapper{gzReader, compression, closer}
return readBufWrapper, nil
default:
return nil, fmt.Errorf("unsupported compression format %s", (&compression).Extension())
}
}
// CompressStream compresseses the dest with specified compression algorithm.
func CompressStream(dest io.Writer, compression Compression) (io.WriteCloser, error) {
switch compression {
case Uncompressed:
return &writeCloserWrapper{dest, nil}, nil
case Gzip:
return gzip.NewWriter(dest), nil
default:
return nil, fmt.Errorf("unsupported compression format %s", (&compression).Extension())
}
}
// Extension returns the extension of a file that uses the specified compression algorithm.
func (compression *Compression) Extension() string {
switch *compression {
case Gzip:
return "gz"
}
return ""
}

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

@ -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

@ -40,7 +40,7 @@ type Container struct {
// Image specifies the image reference used for a container.
//
// This property is optional but immutable.
// This property is optional and mutable.
Image string
// Runtime specifies which runtime should be used when launching container
@ -60,7 +60,7 @@ type Container struct {
// look up the mounts from the snapshot service and include those on the
// task create request.
//
// This field is not required but immutable.
// This field is not required but mutable.
SnapshotKey string
// Snapshotter specifies the snapshotter name used for rootfs

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

@ -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

@ -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

@ -17,16 +17,41 @@
package mount
import (
"fmt"
"os"
"path"
"strings"
"time"
"github.com/containerd/containerd/sys"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
var pagesize = 4096
func init() {
pagesize = os.Getpagesize()
}
// Mount to the provided target path
func (m *Mount) Mount(target string) error {
flags, data := parseMountOptions(m.Options)
var (
chdir string
options = m.Options
)
// avoid hitting one page limit of mount argument buffer
//
// NOTE: 512 is a buffer during pagesize check.
if m.Type == "overlay" && optionsSize(options) >= pagesize-512 {
chdir, options = compactLowerdirOption(options)
}
flags, data := parseMountOptions(options)
if len(data) > pagesize {
return errors.Errorf("mount options is too long")
}
// propagation types.
const ptypes = unix.MS_SHARED | unix.MS_PRIVATE | unix.MS_SLAVE | unix.MS_UNBINDABLE
@ -38,7 +63,7 @@ func (m *Mount) Mount(target string) error {
if flags&unix.MS_REMOUNT == 0 || data != "" {
// Initial call applying all non-propagation flags for mount
// or remount with changed data
if err := unix.Mount(m.Source, target, m.Type, uintptr(oflags), data); err != nil {
if err := mountAt(chdir, m.Source, target, m.Type, uintptr(oflags), data); err != nil {
return err
}
}
@ -155,3 +180,129 @@ func parseMountOptions(options []string) (int, string) {
}
return flag, strings.Join(data, ",")
}
// compactLowerdirOption updates overlay lowdir option and returns the common
// dir among all the lowdirs.
func compactLowerdirOption(opts []string) (string, []string) {
idx, dirs := findOverlayLowerdirs(opts)
if idx == -1 || len(dirs) == 1 {
// no need to compact if there is only one lowerdir
return "", opts
}
// find out common dir
commondir := longestCommonPrefix(dirs)
if commondir == "" {
return "", opts
}
// NOTE: the snapshot id is based on digits.
// in order to avoid to get snapshots/x, should be back to parent dir.
// however, there is assumption that the common dir is ${root}/io.containerd.v1.overlayfs/snapshots.
commondir = path.Dir(commondir)
if commondir == "/" {
return "", opts
}
commondir = commondir + "/"
newdirs := make([]string, 0, len(dirs))
for _, dir := range dirs {
newdirs = append(newdirs, dir[len(commondir):])
}
newopts := copyOptions(opts)
newopts = append(newopts[:idx], newopts[idx+1:]...)
newopts = append(newopts, fmt.Sprintf("lowerdir=%s", strings.Join(newdirs, ":")))
return commondir, newopts
}
// findOverlayLowerdirs returns the index of lowerdir in mount's options and
// all the lowerdir target.
func findOverlayLowerdirs(opts []string) (int, []string) {
var (
idx = -1
prefix = "lowerdir="
)
for i, opt := range opts {
if strings.HasPrefix(opt, prefix) {
idx = i
break
}
}
if idx == -1 {
return -1, nil
}
return idx, strings.Split(opts[idx][len(prefix):], ":")
}
// longestCommonPrefix finds the longest common prefix in the string slice.
func longestCommonPrefix(strs []string) string {
if len(strs) == 0 {
return ""
} else if len(strs) == 1 {
return strs[0]
}
// find out the min/max value by alphabetical order
min, max := strs[0], strs[0]
for _, str := range strs[1:] {
if min > str {
min = str
}
if max < str {
max = str
}
}
// find out the common part between min and max
for i := 0; i < len(min) && i < len(max); i++ {
if min[i] != max[i] {
return min[:i]
}
}
return min
}
// copyOptions copies the options.
func copyOptions(opts []string) []string {
if len(opts) == 0 {
return nil
}
acopy := make([]string, len(opts))
copy(acopy, opts)
return acopy
}
// optionsSize returns the byte size of options of mount.
func optionsSize(opts []string) int {
size := 0
for _, opt := range opts {
size += len(opt)
}
return size
}
func mountAt(chdir string, source, target, fstype string, flags uintptr, data string) error {
if chdir == "" {
return unix.Mount(source, target, fstype, flags, data)
}
f, err := os.Open(chdir)
if err != nil {
return errors.Wrap(err, "failed to mountat")
}
defer f.Close()
fs, err := f.Stat()
if err != nil {
return errors.Wrap(err, "failed to mountat")
}
if !fs.IsDir() {
return errors.Wrap(errors.Errorf("%s is not dir", chdir), "failed to mountat")
}
return errors.Wrap(sys.FMountat(f.Fd(), source, target, fstype, flags, data), "failed to mountat")
}

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
@ -593,6 +602,110 @@ func WithSeccompUnconfined(_ context.Context, _ Client, _ *containers.Container,
return nil
}
// WithParentCgroupDevices uses the default cgroup setup to inherit the container's parent cgroup's
// allowed and denied devices
func WithParentCgroupDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
setLinux(s)
if s.Linux.Resources == nil {
s.Linux.Resources = &specs.LinuxResources{}
}
s.Linux.Resources.Devices = nil
return nil
}
// WithDefaultUnixDevices adds the default devices for unix such as /dev/null, /dev/random to
// the container's resource cgroup spec
func WithDefaultUnixDevices(_ context.Context, _ Client, _ *containers.Container, s *Spec) error {
setLinux(s)
if s.Linux.Resources == nil {
s.Linux.Resources = &specs.LinuxResources{}
}
intptr := func(i int64) *int64 {
return &i
}
s.Linux.Resources.Devices = append(s.Linux.Resources.Devices, []specs.LinuxDeviceCgroup{
{
// "/dev/null",
Type: "c",
Major: intptr(1),
Minor: intptr(3),
Access: rwm,
Allow: true,
},
{
// "/dev/random",
Type: "c",
Major: intptr(1),
Minor: intptr(8),
Access: rwm,
Allow: true,
},
{
// "/dev/full",
Type: "c",
Major: intptr(1),
Minor: intptr(7),
Access: rwm,
Allow: true,
},
{
// "/dev/tty",
Type: "c",
Major: intptr(5),
Minor: intptr(0),
Access: rwm,
Allow: true,
},
{
// "/dev/zero",
Type: "c",
Major: intptr(1),
Minor: intptr(5),
Access: rwm,
Allow: true,
},
{
// "/dev/urandom",
Type: "c",
Major: intptr(1),
Minor: intptr(9),
Access: rwm,
Allow: true,
},
{
// "/dev/console",
Type: "c",
Major: intptr(5),
Minor: intptr(1),
Access: rwm,
Allow: true,
},
// /dev/pts/ - pts namespaces are "coming soon"
{
Type: "c",
Major: intptr(136),
Access: rwm,
Allow: true,
},
{
Type: "c",
Major: intptr(5),
Minor: intptr(2),
Access: rwm,
Allow: true,
},
{
// tuntap
Type: "c",
Major: intptr(10),
Minor: intptr(200),
Access: rwm,
Allow: true,
},
}...)
return nil
}
// WithPrivileged sets up options for a privileged container
// TODO(justincormack) device handling
var WithPrivileged = Compose(

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

@ -109,6 +109,7 @@ package platforms
import (
"regexp"
"runtime"
"strconv"
"strings"
"github.com/containerd/containerd/errdefs"
@ -230,6 +231,16 @@ func Parse(specifier string) (specs.Platform, error) {
return specs.Platform{}, errors.Wrapf(errdefs.ErrInvalidArgument, "%q: cannot parse platform specifier", specifier)
}
// MustParse is like Parses but panics if the specifier cannot be parsed.
// Simplifies initialization of global variables.
func MustParse(specifier string) specs.Platform {
p, err := Parse(specifier)
if err != nil {
panic("platform: Parse(" + strconv.Quote(specifier) + "): " + err.Error())
}
return p
}
// Format returns a string specifier from the provided platform specification.
func Format(platform specs.Platform) string {
if platform.OS == "" {

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

@ -53,6 +53,7 @@ var (
type dockerResolver struct {
credentials func(string) (string, string, error)
host func(string) (string, error)
plainHTTP bool
client *http.Client
tracker StatusTracker
@ -65,6 +66,9 @@ type ResolverOptions struct {
// is interpretted as a long lived token.
Credentials func(string) (string, string, error)
// Host provides the hostname given a namespace.
Host func(string) (string, error)
// PlainHTTP specifies to use plain http and not https
PlainHTTP bool
@ -77,14 +81,27 @@ type ResolverOptions struct {
Tracker StatusTracker
}
// DefaultHost is the default host function.
func DefaultHost(ns string) (string, error) {
if ns == "docker.io" {
return "registry-1.docker.io", nil
}
return ns, nil
}
// NewResolver returns a new resolver to a Docker registry
func NewResolver(options ResolverOptions) remotes.Resolver {
tracker := options.Tracker
if tracker == nil {
tracker = NewInMemoryTracker()
}
host := options.Host
if host == nil {
host = DefaultHost
}
return &dockerResolver{
credentials: options.Credentials,
host: host,
plainHTTP: options.PlainHTTP,
client: options.Client,
tracker: tracker,
@ -270,18 +287,19 @@ func (r *dockerResolver) base(refspec reference.Spec) (*dockerBase, error) {
)
host := refspec.Hostname()
base.Scheme = "https"
if host == "docker.io" {
base.Host = "registry-1.docker.io"
} else {
base.Host = host
if r.plainHTTP || strings.HasPrefix(host, "localhost:") {
base.Scheme = "http"
if r.host != nil {
base.Host, err = r.host(host)
if err != nil {
return nil, err
}
}
base.Scheme = "https"
if r.plainHTTP || strings.HasPrefix(base.Host, "localhost:") {
base.Scheme = "http"
}
if r.credentials != nil {
username, secret, err = r.credentials(base.Host)
if err != nil {

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
}

View file

@ -1,11 +1,11 @@
// Code generated by protoc-gen-gogo. DO NOT EDIT.
// source: github.com/containerd/containerd/runtime/shim/v1/shim.proto
// source: github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto
/*
Package shim is a generated protocol buffer package.
It is generated from these files:
github.com/containerd/containerd/runtime/shim/v1/shim.proto
github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto
It has these top-level messages:
CreateTaskRequest
@ -4352,80 +4352,80 @@ var (
)
func init() {
proto.RegisterFile("github.com/containerd/containerd/runtime/shim/v1/shim.proto", fileDescriptorShim)
proto.RegisterFile("github.com/containerd/containerd/runtime/v1/shim/v1/shim.proto", fileDescriptorShim)
}
var fileDescriptorShim = []byte{
// 1135 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x4d, 0x6f, 0xdb, 0x46,
0x13, 0x36, 0x69, 0x7d, 0x8e, 0x22, 0xbf, 0xf6, 0xbe, 0x8e, 0xcb, 0x28, 0x80, 0x2c, 0x10, 0x68,
0xe0, 0xa2, 0x08, 0x55, 0xcb, 0x6d, 0xd2, 0x36, 0x40, 0x00, 0xdb, 0x09, 0x0a, 0xa3, 0x0d, 0x6c,
0xd0, 0x4e, 0x13, 0xb4, 0x28, 0x0c, 0x5a, 0x5c, 0x4b, 0x0b, 0x4b, 0x24, 0xc3, 0x5d, 0xba, 0x76,
0x4f, 0x3d, 0xf5, 0xdc, 0x9f, 0xd3, 0x9f, 0xe0, 0x43, 0x0e, 0x3d, 0xf6, 0x94, 0x36, 0xba, 0xf7,
0x3f, 0x14, 0xfb, 0x21, 0x93, 0x92, 0xcc, 0x90, 0xf2, 0xc5, 0xe2, 0xec, 0x3e, 0xb3, 0x3b, 0x3b,
0xcf, 0xb3, 0x33, 0x6b, 0x78, 0xd2, 0x23, 0xac, 0x1f, 0x9d, 0x58, 0x5d, 0x7f, 0xd8, 0xee, 0xfa,
0x1e, 0x73, 0x88, 0x87, 0x43, 0x37, 0xf9, 0x19, 0x46, 0x1e, 0x23, 0x43, 0xdc, 0xa6, 0x7d, 0x32,
0x6c, 0x9f, 0x6f, 0x8a, 0x5f, 0x2b, 0x08, 0x7d, 0xe6, 0xa3, 0x56, 0x0c, 0xb3, 0x14, 0xcc, 0x1a,
0x10, 0x2f, 0xba, 0xb0, 0x04, 0xe8, 0x7c, 0xb3, 0x71, 0xaf, 0xe7, 0xfb, 0xbd, 0x01, 0x6e, 0x0b,
0xfc, 0x49, 0x74, 0xda, 0x76, 0xbc, 0x4b, 0xe9, 0xdc, 0xb8, 0x3f, 0x3d, 0x85, 0x87, 0x01, 0x1b,
0x4f, 0xae, 0xf6, 0xfc, 0x9e, 0x2f, 0x3e, 0xdb, 0xfc, 0x4b, 0x8d, 0xae, 0x4f, 0xbb, 0xf0, 0x1d,
0x29, 0x73, 0x86, 0x81, 0x02, 0x3c, 0xca, 0x3c, 0x8d, 0x13, 0x90, 0x36, 0xbb, 0x0c, 0x30, 0x6d,
0x0f, 0xfd, 0xc8, 0x63, 0xca, 0xef, 0xeb, 0x39, 0xfc, 0x98, 0x43, 0xcf, 0xc4, 0x1f, 0xe9, 0x6b,
0xfe, 0xab, 0xc3, 0xca, 0x6e, 0x88, 0x1d, 0x86, 0x8f, 0x1c, 0x7a, 0x66, 0xe3, 0x37, 0x11, 0xa6,
0x0c, 0xad, 0x81, 0x4e, 0x5c, 0x43, 0x6b, 0x69, 0x1b, 0xd5, 0x9d, 0xd2, 0xe8, 0xdd, 0xba, 0xbe,
0xf7, 0xcc, 0xd6, 0x89, 0x8b, 0xd6, 0xa0, 0x74, 0x12, 0x79, 0xee, 0x00, 0x1b, 0x3a, 0x9f, 0xb3,
0x95, 0x85, 0x0c, 0x28, 0xab, 0x0c, 0x1a, 0x8b, 0x62, 0x62, 0x6c, 0xa2, 0x36, 0x94, 0x42, 0xdf,
0x67, 0xa7, 0xd4, 0x28, 0xb4, 0x16, 0x37, 0x6a, 0x9d, 0x8f, 0xac, 0x44, 0xd6, 0x45, 0x48, 0xd6,
0x0b, 0x7e, 0x14, 0x5b, 0xc1, 0x50, 0x03, 0x2a, 0x0c, 0x87, 0x43, 0xe2, 0x39, 0x03, 0xa3, 0xd8,
0xd2, 0x36, 0x2a, 0xf6, 0xb5, 0x8d, 0x56, 0xa1, 0x48, 0x99, 0x4b, 0x3c, 0xa3, 0x24, 0x36, 0x91,
0x06, 0x0f, 0x8a, 0x32, 0xd7, 0x8f, 0x98, 0x51, 0x96, 0x41, 0x49, 0x4b, 0x8d, 0xe3, 0x30, 0x34,
0x2a, 0xd7, 0xe3, 0x38, 0x0c, 0x51, 0x13, 0xa0, 0xdb, 0xc7, 0xdd, 0xb3, 0xc0, 0x27, 0x1e, 0x33,
0xaa, 0x62, 0x2e, 0x31, 0x82, 0x3e, 0x85, 0x95, 0xc0, 0x09, 0xb1, 0xc7, 0x8e, 0x13, 0x30, 0x10,
0xb0, 0x65, 0x39, 0xb1, 0x1b, 0x83, 0x2d, 0x28, 0xfb, 0x01, 0x23, 0xbe, 0x47, 0x8d, 0x5a, 0x4b,
0xdb, 0xa8, 0x75, 0x56, 0x2d, 0x49, 0xb3, 0x35, 0xa6, 0xd9, 0xda, 0xf6, 0x2e, 0xed, 0x31, 0xc8,
0x7c, 0x00, 0x28, 0x99, 0x6e, 0x1a, 0xf8, 0x1e, 0xc5, 0x68, 0x19, 0x16, 0x03, 0x95, 0xf0, 0xba,
0xcd, 0x3f, 0xcd, 0xdf, 0x34, 0x58, 0x7a, 0x86, 0x07, 0x98, 0xe1, 0x74, 0x10, 0x5a, 0x87, 0x1a,
0xbe, 0x20, 0xec, 0x98, 0x32, 0x87, 0x45, 0x54, 0x70, 0x52, 0xb7, 0x81, 0x0f, 0x1d, 0x8a, 0x11,
0xb4, 0x0d, 0x55, 0x6e, 0x61, 0xf7, 0xd8, 0x61, 0x82, 0x99, 0x5a, 0xa7, 0x31, 0x13, 0xdf, 0xd1,
0x58, 0x86, 0x3b, 0x95, 0xab, 0x77, 0xeb, 0x0b, 0xbf, 0xff, 0xbd, 0xae, 0xd9, 0x15, 0xe9, 0xb6,
0xcd, 0x4c, 0x0b, 0x56, 0x65, 0x1c, 0x07, 0xa1, 0xdf, 0xc5, 0x94, 0x66, 0x48, 0xc4, 0xfc, 0x43,
0x03, 0xf4, 0xfc, 0x02, 0x77, 0xf3, 0xc1, 0x27, 0xe8, 0xd6, 0xd3, 0xe8, 0x5e, 0xbc, 0x99, 0xee,
0x42, 0x0a, 0xdd, 0xc5, 0x09, 0xba, 0x37, 0xa0, 0x40, 0x03, 0xdc, 0x15, 0x9a, 0x49, 0xa3, 0x47,
0x20, 0xcc, 0xbb, 0xf0, 0xff, 0x89, 0xc8, 0x65, 0xde, 0xcd, 0xd7, 0xb0, 0x6c, 0x63, 0x4a, 0x7e,
0xc1, 0x07, 0xec, 0x32, 0xeb, 0x38, 0xab, 0x50, 0xfc, 0x99, 0xb8, 0xac, 0xaf, 0xb8, 0x90, 0x06,
0x0f, 0xad, 0x8f, 0x49, 0xaf, 0x2f, 0x39, 0xa8, 0xdb, 0xca, 0x32, 0x1f, 0xc0, 0x1d, 0x4e, 0x14,
0xce, 0xca, 0xe9, 0x5b, 0x1d, 0xea, 0x0a, 0xa8, 0xb4, 0x30, 0xef, 0x05, 0x55, 0xda, 0x59, 0x8c,
0xb5, 0xb3, 0xc5, 0xd3, 0x25, 0x64, 0xc3, 0xd3, 0xb8, 0xd4, 0xb9, 0x9f, 0xbc, 0x98, 0xe7, 0x9b,
0xea, 0x6e, 0x4a, 0x1d, 0xd9, 0x0a, 0x1a, 0x33, 0x52, 0xbc, 0x99, 0x91, 0x52, 0x0a, 0x23, 0xe5,
0x09, 0x46, 0x92, 0x9c, 0x57, 0xa6, 0x38, 0x9f, 0x92, 0x74, 0xf5, 0xc3, 0x92, 0x86, 0x5b, 0x49,
0x7a, 0x1f, 0x6a, 0xdf, 0x92, 0xc1, 0x20, 0x47, 0xb1, 0xa3, 0xa4, 0x37, 0x16, 0x66, 0xdd, 0x56,
0x16, 0xcf, 0xa5, 0x33, 0x18, 0x88, 0x5c, 0x56, 0x6c, 0xfe, 0x69, 0x3e, 0x85, 0xa5, 0xdd, 0x81,
0x4f, 0xf1, 0xde, 0x7e, 0x0e, 0x7d, 0xc8, 0x04, 0x4a, 0xad, 0x4b, 0xc3, 0xfc, 0x04, 0xfe, 0xf7,
0x1d, 0xa1, 0xec, 0x80, 0xb8, 0x99, 0xd7, 0xcb, 0x86, 0xe5, 0x18, 0xaa, 0xc4, 0xf0, 0x14, 0xaa,
0x81, 0xd4, 0x2c, 0xa6, 0x86, 0x26, 0xca, 0x6c, 0xeb, 0x46, 0x36, 0x95, 0xb2, 0xf7, 0xbc, 0x53,
0xdf, 0x8e, 0x5d, 0xcc, 0x1f, 0xe1, 0x6e, 0x5c, 0xd1, 0x92, 0x6d, 0x00, 0x41, 0x21, 0x70, 0x58,
0x5f, 0x86, 0x61, 0x8b, 0xef, 0x64, 0xc1, 0xd3, 0xf3, 0x14, 0xbc, 0x87, 0xb0, 0x7c, 0xd8, 0x27,
0x43, 0xb1, 0xe7, 0x38, 0xe0, 0x7b, 0x50, 0xe1, 0x2d, 0xf6, 0x38, 0x2e, 0x67, 0x65, 0x6e, 0x1f,
0x10, 0xd7, 0xfc, 0x06, 0x56, 0x5e, 0x06, 0xee, 0x54, 0x3b, 0xea, 0x40, 0x35, 0xc4, 0xd4, 0x8f,
0xc2, 0xae, 0x38, 0x60, 0xfa, 0xae, 0x31, 0x4c, 0xdd, 0xad, 0x90, 0x65, 0x25, 0xf4, 0x2b, 0x71,
0xb5, 0x38, 0x2e, 0xe3, 0x6a, 0xa9, 0x2b, 0xa4, 0xc7, 0x35, 0xfa, 0x63, 0xa8, 0xbd, 0x72, 0x48,
0xe6, 0x0e, 0x21, 0xdc, 0x91, 0x30, 0xb5, 0xc1, 0x94, 0xc4, 0xb5, 0x0f, 0x4b, 0x5c, 0xbf, 0x8d,
0xc4, 0x3b, 0x6f, 0x6b, 0x50, 0xe0, 0x69, 0x47, 0x7d, 0x28, 0x8a, 0xca, 0x81, 0x2c, 0x2b, 0xeb,
0xb9, 0x63, 0x25, 0x6b, 0x51, 0xa3, 0x9d, 0x1b, 0xaf, 0x8e, 0x45, 0xa1, 0x24, 0x3b, 0x1b, 0xda,
0xca, 0x76, 0x9d, 0x79, 0x72, 0x34, 0x3e, 0x9f, 0xcf, 0x49, 0x6d, 0x2a, 0x8f, 0x17, 0xb2, 0x9c,
0xc7, 0xbb, 0x96, 0x43, 0xce, 0xe3, 0x25, 0x64, 0x61, 0x43, 0x49, 0xf6, 0x41, 0xb4, 0x36, 0xc3,
0xc5, 0x73, 0xfe, 0xf6, 0x6b, 0x7c, 0x96, 0xbd, 0xe4, 0x54, 0x47, 0xbf, 0x84, 0xfa, 0x44, 0x6f,
0x45, 0x8f, 0xf2, 0x2e, 0x31, 0xd9, 0x5d, 0x6f, 0xb1, 0xf5, 0x1b, 0xa8, 0x8c, 0xeb, 0x08, 0xda,
0xcc, 0xf6, 0x9e, 0x2a, 0x4f, 0x8d, 0xce, 0x3c, 0x2e, 0x6a, 0xcb, 0xc7, 0x50, 0x3c, 0x70, 0x22,
0x9a, 0x9e, 0xc0, 0x94, 0x71, 0xf4, 0x25, 0x94, 0x6c, 0x4c, 0xa3, 0xe1, 0xfc, 0x9e, 0x3f, 0x01,
0x24, 0xde, 0x6a, 0x8f, 0x73, 0x48, 0xec, 0xa6, 0x3a, 0x98, 0xba, 0xfc, 0x0b, 0x28, 0xf0, 0x46,
0x82, 0x1e, 0x66, 0x2f, 0x9c, 0x68, 0x38, 0xa9, 0xcb, 0x1d, 0x41, 0x81, 0xbf, 0x3f, 0x50, 0x8e,
0xab, 0x30, 0xfb, 0xc2, 0x4a, 0x5d, 0xf5, 0x15, 0x54, 0xaf, 0x9f, 0x2f, 0x28, 0x07, 0x6f, 0xd3,
0x6f, 0x9d, 0xd4, 0x85, 0x0f, 0xa1, 0xac, 0xba, 0x1e, 0xca, 0xa1, 0xbf, 0xc9, 0x06, 0x99, 0xba,
0xe8, 0xf7, 0x50, 0x19, 0xb7, 0x8b, 0x54, 0xb6, 0x73, 0x1c, 0x62, 0xa6, 0xe5, 0xbc, 0x84, 0x92,
0xec, 0x2b, 0x79, 0xaa, 0xd3, 0x4c, 0x07, 0x4a, 0x0d, 0x17, 0x43, 0x81, 0xd7, 0xf6, 0x3c, 0x0a,
0x48, 0xb4, 0x8a, 0x86, 0x95, 0x17, 0x2e, 0xa3, 0xdf, 0xd9, 0xbf, 0x7a, 0xdf, 0x5c, 0xf8, 0xeb,
0x7d, 0x73, 0xe1, 0xd7, 0x51, 0x53, 0xbb, 0x1a, 0x35, 0xb5, 0x3f, 0x47, 0x4d, 0xed, 0x9f, 0x51,
0x53, 0xfb, 0xe1, 0x8b, 0x79, 0xff, 0x03, 0x7e, 0xc2, 0x7f, 0x5f, 0xeb, 0x27, 0x25, 0x71, 0x92,
0xad, 0xff, 0x02, 0x00, 0x00, 0xff, 0xff, 0x5b, 0x88, 0x3a, 0x56, 0x43, 0x0f, 0x00, 0x00,
// 1133 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x4f, 0x4f, 0x1b, 0x47,
0x14, 0x67, 0x17, 0xff, 0x7d, 0x8e, 0x29, 0x4c, 0x09, 0xdd, 0x38, 0x92, 0xb1, 0x56, 0x6a, 0x44,
0x55, 0x65, 0x5d, 0x4c, 0x95, 0xa4, 0xad, 0x84, 0x04, 0x24, 0xaa, 0x50, 0x1b, 0x05, 0x2d, 0xa4,
0x89, 0x5a, 0x55, 0x68, 0xf1, 0x0e, 0xf6, 0x08, 0x7b, 0x67, 0xb3, 0x33, 0x4b, 0xa1, 0xa7, 0x9e,
0x7a, 0xee, 0xc7, 0xe9, 0x47, 0xe0, 0x90, 0x43, 0x8f, 0x3d, 0xa5, 0x0d, 0xf7, 0x7e, 0x87, 0x6a,
0xfe, 0x18, 0xaf, 0x6d, 0x36, 0xbb, 0x70, 0xc1, 0xfb, 0x66, 0x7e, 0x6f, 0xe6, 0xcd, 0xfb, 0xfd,
0xe6, 0xbd, 0x01, 0x36, 0x7b, 0x84, 0xf7, 0xe3, 0x23, 0xa7, 0x4b, 0x87, 0xed, 0x2e, 0x0d, 0xb8,
0x47, 0x02, 0x1c, 0xf9, 0xc9, 0xcf, 0x28, 0x0e, 0x38, 0x19, 0xe2, 0xf6, 0xe9, 0x7a, 0x9b, 0xf5,
0xc9, 0x70, 0xf4, 0xeb, 0x84, 0x11, 0xe5, 0x14, 0xb5, 0xc6, 0x48, 0x47, 0x23, 0x9d, 0x01, 0x09,
0xe2, 0x33, 0x47, 0x82, 0x4e, 0xd7, 0x1b, 0xf7, 0x7a, 0x94, 0xf6, 0x06, 0xb8, 0x2d, 0xf1, 0x47,
0xf1, 0x71, 0xdb, 0x0b, 0xce, 0x95, 0x73, 0xe3, 0xfe, 0xf4, 0x14, 0x1e, 0x86, 0x7c, 0x34, 0xb9,
0xdc, 0xa3, 0x3d, 0x2a, 0x3f, 0xdb, 0xe2, 0x4b, 0x8f, 0xae, 0x4e, 0xbb, 0x88, 0x1d, 0x19, 0xf7,
0x86, 0xa1, 0x06, 0x3c, 0xca, 0x3c, 0x90, 0x17, 0x92, 0x36, 0x3f, 0x0f, 0x31, 0x6b, 0x0f, 0x69,
0x1c, 0x70, 0xed, 0xf7, 0xf5, 0x0d, 0xfc, 0xb8, 0xc7, 0x4e, 0xe4, 0x1f, 0xe5, 0x6b, 0xff, 0x67,
0xc2, 0xd2, 0x4e, 0x84, 0x3d, 0x8e, 0x0f, 0x3c, 0x76, 0xe2, 0xe2, 0x37, 0x31, 0x66, 0x1c, 0xad,
0x80, 0x49, 0x7c, 0xcb, 0x68, 0x19, 0x6b, 0xd5, 0xed, 0xd2, 0xe5, 0xbb, 0x55, 0x73, 0xf7, 0xa9,
0x6b, 0x12, 0x1f, 0xad, 0x40, 0xe9, 0x28, 0x0e, 0xfc, 0x01, 0xb6, 0x4c, 0x31, 0xe7, 0x6a, 0x0b,
0x59, 0x50, 0xd6, 0x19, 0xb4, 0xe6, 0xe5, 0xc4, 0xc8, 0x44, 0x6d, 0x28, 0x45, 0x94, 0xf2, 0x63,
0x66, 0x15, 0x5a, 0xf3, 0x6b, 0xb5, 0xce, 0x27, 0x4e, 0x22, 0xeb, 0x32, 0x24, 0xe7, 0xb9, 0x38,
0x8a, 0xab, 0x61, 0xa8, 0x01, 0x15, 0x8e, 0xa3, 0x21, 0x09, 0xbc, 0x81, 0x55, 0x6c, 0x19, 0x6b,
0x15, 0xf7, 0xca, 0x46, 0xcb, 0x50, 0x64, 0xdc, 0x27, 0x81, 0x55, 0x92, 0x9b, 0x28, 0x43, 0x04,
0xc5, 0xb8, 0x4f, 0x63, 0x6e, 0x95, 0x55, 0x50, 0xca, 0xd2, 0xe3, 0x38, 0x8a, 0xac, 0xca, 0xd5,
0x38, 0x8e, 0x22, 0xd4, 0x04, 0xe8, 0xf6, 0x71, 0xf7, 0x24, 0xa4, 0x24, 0xe0, 0x56, 0x55, 0xce,
0x25, 0x46, 0xd0, 0xe7, 0xb0, 0x14, 0x7a, 0x11, 0x0e, 0xf8, 0x61, 0x02, 0x06, 0x12, 0xb6, 0xa8,
0x26, 0x76, 0xc6, 0x60, 0x07, 0xca, 0x34, 0xe4, 0x84, 0x06, 0xcc, 0xaa, 0xb5, 0x8c, 0xb5, 0x5a,
0x67, 0xd9, 0x51, 0x34, 0x3b, 0x23, 0x9a, 0x9d, 0xad, 0xe0, 0xdc, 0x1d, 0x81, 0xec, 0x07, 0x80,
0x92, 0xe9, 0x66, 0x21, 0x0d, 0x18, 0x46, 0x8b, 0x30, 0x1f, 0xea, 0x84, 0xd7, 0x5d, 0xf1, 0x69,
0xff, 0x6e, 0xc0, 0xc2, 0x53, 0x3c, 0xc0, 0x1c, 0xa7, 0x83, 0xd0, 0x2a, 0xd4, 0xf0, 0x19, 0xe1,
0x87, 0x8c, 0x7b, 0x3c, 0x66, 0x92, 0x93, 0xba, 0x0b, 0x62, 0x68, 0x5f, 0x8e, 0xa0, 0x2d, 0xa8,
0x0a, 0x0b, 0xfb, 0x87, 0x1e, 0x97, 0xcc, 0xd4, 0x3a, 0x8d, 0x99, 0xf8, 0x0e, 0x46, 0x32, 0xdc,
0xae, 0x5c, 0xbc, 0x5b, 0x9d, 0xfb, 0xe3, 0x9f, 0x55, 0xc3, 0xad, 0x28, 0xb7, 0x2d, 0x6e, 0x3b,
0xb0, 0xac, 0xe2, 0xd8, 0x8b, 0x68, 0x17, 0x33, 0x96, 0x21, 0x11, 0xfb, 0x4f, 0x03, 0xd0, 0xb3,
0x33, 0xdc, 0xcd, 0x07, 0x9f, 0xa0, 0xdb, 0x4c, 0xa3, 0x7b, 0xfe, 0x7a, 0xba, 0x0b, 0x29, 0x74,
0x17, 0x27, 0xe8, 0x5e, 0x83, 0x02, 0x0b, 0x71, 0x57, 0x6a, 0x26, 0x8d, 0x1e, 0x89, 0xb0, 0xef,
0xc2, 0xc7, 0x13, 0x91, 0xab, 0xbc, 0xdb, 0xaf, 0x61, 0xd1, 0xc5, 0x8c, 0xfc, 0x8a, 0xf7, 0xf8,
0x79, 0xd6, 0x71, 0x96, 0xa1, 0xf8, 0x0b, 0xf1, 0x79, 0x5f, 0x73, 0xa1, 0x0c, 0x11, 0x5a, 0x1f,
0x93, 0x5e, 0x5f, 0x71, 0x50, 0x77, 0xb5, 0x65, 0x3f, 0x80, 0x3b, 0x82, 0x28, 0x9c, 0x95, 0xd3,
0xb7, 0x26, 0xd4, 0x35, 0x50, 0x6b, 0xe1, 0xa6, 0x17, 0x54, 0x6b, 0x67, 0x7e, 0xac, 0x9d, 0x0d,
0x91, 0x2e, 0x29, 0x1b, 0x91, 0xc6, 0x85, 0xce, 0xfd, 0xe4, 0xc5, 0x3c, 0x5d, 0xd7, 0x77, 0x53,
0xe9, 0xc8, 0xd5, 0xd0, 0x31, 0x23, 0xc5, 0xeb, 0x19, 0x29, 0xa5, 0x30, 0x52, 0x9e, 0x60, 0x24,
0xc9, 0x79, 0x65, 0x8a, 0xf3, 0x29, 0x49, 0x57, 0x3f, 0x2c, 0x69, 0xb8, 0x95, 0xa4, 0x5f, 0x40,
0xed, 0x3b, 0x32, 0x18, 0xe4, 0x28, 0x76, 0x8c, 0xf4, 0x46, 0xc2, 0xac, 0xbb, 0xda, 0x12, 0xb9,
0xf4, 0x06, 0x03, 0x99, 0xcb, 0x8a, 0x2b, 0x3e, 0xed, 0x4d, 0x58, 0xd8, 0x19, 0x50, 0x86, 0x77,
0x5f, 0xe4, 0xd0, 0x87, 0x4a, 0xa0, 0xd2, 0xba, 0x32, 0xec, 0xcf, 0xe0, 0xa3, 0xef, 0x09, 0xe3,
0x7b, 0xc4, 0xcf, 0xbc, 0x5e, 0x2e, 0x2c, 0x8e, 0xa1, 0x5a, 0x0c, 0x9b, 0x50, 0x0d, 0x95, 0x66,
0x31, 0xb3, 0x0c, 0x59, 0x66, 0x5b, 0xd7, 0xb2, 0xa9, 0x95, 0xbd, 0x1b, 0x1c, 0x53, 0x77, 0xec,
0x62, 0xff, 0x04, 0x77, 0xc7, 0x15, 0x2d, 0xd9, 0x06, 0x10, 0x14, 0x42, 0x8f, 0xf7, 0x55, 0x18,
0xae, 0xfc, 0x4e, 0x16, 0x3c, 0x33, 0x4f, 0xc1, 0x7b, 0x08, 0x8b, 0xfb, 0x7d, 0x32, 0x94, 0x7b,
0x8e, 0x02, 0xbe, 0x07, 0x15, 0xd1, 0x62, 0x0f, 0xc7, 0xe5, 0xac, 0x2c, 0xec, 0x3d, 0xe2, 0xdb,
0xdf, 0xc2, 0xd2, 0xcb, 0xd0, 0x9f, 0x6a, 0x47, 0x1d, 0xa8, 0x46, 0x98, 0xd1, 0x38, 0xea, 0xca,
0x03, 0xa6, 0xef, 0x3a, 0x86, 0xe9, 0xbb, 0x15, 0xf1, 0xac, 0x84, 0x7e, 0x25, 0xaf, 0x96, 0xc0,
0x65, 0x5c, 0x2d, 0x7d, 0x85, 0xcc, 0x71, 0x8d, 0xfe, 0x14, 0x6a, 0xaf, 0x3c, 0x92, 0xb9, 0x43,
0x04, 0x77, 0x14, 0x4c, 0x6f, 0x30, 0x25, 0x71, 0xe3, 0xc3, 0x12, 0x37, 0x6f, 0x23, 0xf1, 0xce,
0xdb, 0x1a, 0x14, 0x44, 0xda, 0x51, 0x1f, 0x8a, 0xb2, 0x72, 0x20, 0xc7, 0xc9, 0x7a, 0xee, 0x38,
0xc9, 0x5a, 0xd4, 0x68, 0xe7, 0xc6, 0xeb, 0x63, 0x31, 0x28, 0xa9, 0xce, 0x86, 0x36, 0xb2, 0x5d,
0x67, 0x9e, 0x1c, 0x8d, 0x2f, 0x6f, 0xe6, 0xa4, 0x37, 0x55, 0xc7, 0x8b, 0x78, 0xce, 0xe3, 0x5d,
0xc9, 0x21, 0xe7, 0xf1, 0x12, 0xb2, 0x70, 0xa1, 0xa4, 0xfa, 0x20, 0x5a, 0x99, 0xe1, 0xe2, 0x99,
0x78, 0xfb, 0x35, 0xbe, 0xc8, 0x5e, 0x72, 0xaa, 0xa3, 0x9f, 0x43, 0x7d, 0xa2, 0xb7, 0xa2, 0x47,
0x79, 0x97, 0x98, 0xec, 0xae, 0xb7, 0xd8, 0xfa, 0x0d, 0x54, 0x46, 0x75, 0x04, 0xad, 0x67, 0x7b,
0x4f, 0x95, 0xa7, 0x46, 0xe7, 0x26, 0x2e, 0x7a, 0xcb, 0xc7, 0x50, 0xdc, 0xf3, 0x62, 0x96, 0x9e,
0xc0, 0x94, 0x71, 0xf4, 0x04, 0x4a, 0x2e, 0x66, 0xf1, 0xf0, 0xe6, 0x9e, 0x3f, 0x03, 0x24, 0xde,
0x6a, 0x8f, 0x73, 0x48, 0xec, 0xba, 0x3a, 0x98, 0xba, 0xfc, 0x73, 0x28, 0x88, 0x46, 0x82, 0x1e,
0x66, 0x2f, 0x9c, 0x68, 0x38, 0xa9, 0xcb, 0x1d, 0x40, 0x41, 0xbc, 0x3f, 0x50, 0x8e, 0xab, 0x30,
0xfb, 0xc2, 0x4a, 0x5d, 0xf5, 0x15, 0x54, 0xaf, 0x9e, 0x2f, 0x28, 0x07, 0x6f, 0xd3, 0x6f, 0x9d,
0xd4, 0x85, 0xf7, 0xa1, 0xac, 0xbb, 0x1e, 0xca, 0xa1, 0xbf, 0xc9, 0x06, 0x99, 0xba, 0xe8, 0x0f,
0x50, 0x19, 0xb5, 0x8b, 0x54, 0xb6, 0x73, 0x1c, 0x62, 0xa6, 0xe5, 0xbc, 0x84, 0x92, 0xea, 0x2b,
0x79, 0xaa, 0xd3, 0x4c, 0x07, 0x4a, 0x0d, 0x17, 0x43, 0x41, 0xd4, 0xf6, 0x3c, 0x0a, 0x48, 0xb4,
0x8a, 0x86, 0x93, 0x17, 0xae, 0xa2, 0xdf, 0x76, 0x2f, 0xde, 0x37, 0xe7, 0xfe, 0x7e, 0xdf, 0x9c,
0xfb, 0xed, 0xb2, 0x69, 0x5c, 0x5c, 0x36, 0x8d, 0xbf, 0x2e, 0x9b, 0xc6, 0xbf, 0x97, 0x4d, 0xe3,
0xc7, 0x27, 0xb7, 0xf8, 0x27, 0xf8, 0x1b, 0xf1, 0xfb, 0xda, 0x3c, 0x2a, 0xc9, 0xc3, 0x6c, 0xfc,
0x1f, 0x00, 0x00, 0xff, 0xff, 0x64, 0x52, 0x86, 0xc0, 0x49, 0x0f, 0x00, 0x00,
}

View file

@ -9,7 +9,7 @@ import "google/protobuf/timestamp.proto";
import "github.com/containerd/containerd/api/types/mount.proto";
import "github.com/containerd/containerd/api/types/task/task.proto";
option go_package = "github.com/containerd/containerd/runtime/shim/v1;shim";
option go_package = "github.com/containerd/containerd/runtime/v1/shim/v1;shim";
// Shim service is launched for each container and is responsible for owning the IO
// for the container and its additional processes. The shim is also the parent of

View file

@ -20,12 +20,12 @@ import (
containersapi "github.com/containerd/containerd/api/services/containers/v1"
"github.com/containerd/containerd/api/services/diff/v1"
imagesapi "github.com/containerd/containerd/api/services/images/v1"
"github.com/containerd/containerd/api/services/leases/v1"
namespacesapi "github.com/containerd/containerd/api/services/namespaces/v1"
"github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/images"
"github.com/containerd/containerd/leases"
"github.com/containerd/containerd/namespaces"
"github.com/containerd/containerd/snapshots"
)
@ -39,7 +39,7 @@ type services struct {
taskService tasks.TasksClient
diffService DiffService
eventService EventService
leasesService leases.LeasesClient
leasesService leases.Manager
}
// ServicesOpt allows callers to set options on the services
@ -105,7 +105,7 @@ func WithNamespaceService(namespaceService namespacesapi.NamespacesClient) Servi
}
// WithLeasesService sets the lease service.
func WithLeasesService(leasesService leases.LeasesClient) ServicesOpt {
func WithLeasesService(leasesService leases.Manager) ServicesOpt {
return func(s *services) {
s.leasesService = leasesService
}

View file

@ -43,6 +43,8 @@ type Config struct {
OOMScore int `toml:"oom_score"`
// Cgroup specifies cgroup information for the containerd daemon process
Cgroup CgroupConfig `toml:"cgroup"`
// ProxyPlugins configures plugins which are communicated to over GRPC
ProxyPlugins map[string]ProxyPlugin `toml:"proxy_plugins"`
md toml.MetaData
}
@ -75,6 +77,12 @@ type CgroupConfig struct {
Path string `toml:"path"`
}
// ProxyPlugin provides a proxy plugin configuration
type ProxyPlugin struct {
Type string `toml:"type"`
Address string `toml:"address"`
}
// Decode unmarshals a plugin specific configuration by plugin id
func (c *Config) Decode(id string, v interface{}) (interface{}, error) {
data, ok := c.Plugins[id]

View file

@ -26,19 +26,26 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/boltdb/bolt"
csapi "github.com/containerd/containerd/api/services/content/v1"
ssapi "github.com/containerd/containerd/api/services/snapshots/v1"
"github.com/containerd/containerd/content"
"github.com/containerd/containerd/content/local"
csproxy "github.com/containerd/containerd/content/proxy"
"github.com/containerd/containerd/defaults"
"github.com/containerd/containerd/events/exchange"
"github.com/containerd/containerd/log"
"github.com/containerd/containerd/metadata"
"github.com/containerd/containerd/pkg/dialer"
"github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/snapshots"
ssproxy "github.com/containerd/containerd/snapshots/proxy"
metrics "github.com/docker/go-metrics"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
"github.com/pkg/errors"
"google.golang.org/grpc"
)
@ -62,7 +69,7 @@ func New(ctx context.Context, config *Config) (*Server, error) {
if err := apply(ctx, config); err != nil {
return nil, err
}
plugins, err := LoadPlugins(config)
plugins, err := LoadPlugins(ctx, config)
if err != nil {
return nil, err
}
@ -204,7 +211,7 @@ func (s *Server) Stop() {
// LoadPlugins loads all plugins into containerd and generates an ordered graph
// of all plugins.
func LoadPlugins(config *Config) ([]*plugin.Registration, error) {
func LoadPlugins(ctx context.Context, config *Config) ([]*plugin.Registration, error) {
// load all plugins into containerd
if err := plugin.Load(filepath.Join(config.Root, "plugins")); err != nil {
return nil, err
@ -265,10 +272,85 @@ func LoadPlugins(config *Config) ([]*plugin.Registration, error) {
},
})
clients := &proxyClients{}
for name, pp := range config.ProxyPlugins {
var (
t plugin.Type
f func(*grpc.ClientConn) interface{}
address = pp.Address
)
switch pp.Type {
case string(plugin.SnapshotPlugin), "snapshot":
t = plugin.SnapshotPlugin
ssname := name
f = func(conn *grpc.ClientConn) interface{} {
return ssproxy.NewSnapshotter(ssapi.NewSnapshotsClient(conn), ssname)
}
case string(plugin.ContentPlugin), "content":
t = plugin.ContentPlugin
f = func(conn *grpc.ClientConn) interface{} {
return csproxy.NewContentStore(csapi.NewContentClient(conn))
}
default:
log.G(ctx).WithField("type", pp.Type).Warn("unknown proxy plugin type")
}
plugin.Register(&plugin.Registration{
Type: t,
ID: name,
InitFn: func(ic *plugin.InitContext) (interface{}, error) {
ic.Meta.Exports["address"] = address
conn, err := clients.getClient(address)
if err != nil {
return nil, err
}
return f(conn), nil
},
})
}
// return the ordered graph for plugins
return plugin.Graph(config.DisabledPlugins), nil
}
type proxyClients struct {
m sync.Mutex
clients map[string]*grpc.ClientConn
}
func (pc *proxyClients) getClient(address string) (*grpc.ClientConn, error) {
pc.m.Lock()
defer pc.m.Unlock()
if pc.clients == nil {
pc.clients = map[string]*grpc.ClientConn{}
} else if c, ok := pc.clients[address]; ok {
return c, nil
}
gopts := []grpc.DialOption{
grpc.WithInsecure(),
grpc.WithBackoffMaxDelay(3 * time.Second),
grpc.WithDialer(dialer.Dialer),
// TODO(stevvooe): We may need to allow configuration of this on the client.
grpc.WithDefaultCallOptions(grpc.MaxCallRecvMsgSize(defaults.DefaultMaxRecvMsgSize)),
grpc.WithDefaultCallOptions(grpc.MaxCallSendMsgSize(defaults.DefaultMaxSendMsgSize)),
}
conn, err := grpc.Dial(dialer.DialAddress(address), gopts...)
if err != nil {
return nil, errors.Wrapf(err, "failed to dial %q", address)
}
pc.clients[address] = conn
return conn, nil
}
func trapClosedConnErr(err error) error {
if err == nil {
return nil

View file

@ -0,0 +1,119 @@
/*
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 sys
import (
"runtime"
"syscall"
"unsafe"
"github.com/pkg/errors"
"golang.org/x/sys/unix"
)
// FMountat performs mount from the provided directory.
func FMountat(dirfd uintptr, source, target, fstype string, flags uintptr, data string) error {
var (
sourceP, targetP, fstypeP, dataP *byte
pid uintptr
ws unix.WaitStatus
err error
errno syscall.Errno
)
sourceP, err = syscall.BytePtrFromString(source)
if err != nil {
return err
}
targetP, err = syscall.BytePtrFromString(target)
if err != nil {
return err
}
fstypeP, err = syscall.BytePtrFromString(fstype)
if err != nil {
return err
}
if data != "" {
dataP, err = syscall.BytePtrFromString(data)
if err != nil {
return err
}
}
runtime.LockOSThread()
defer runtime.UnlockOSThread()
pid, errno = forkAndMountat(dirfd,
uintptr(unsafe.Pointer(sourceP)),
uintptr(unsafe.Pointer(targetP)),
uintptr(unsafe.Pointer(fstypeP)),
flags,
uintptr(unsafe.Pointer(dataP)))
if errno != 0 {
return errors.Wrap(errno, "failed to fork thread")
}
_, err = unix.Wait4(int(pid), &ws, 0, nil)
for err == syscall.EINTR {
_, err = unix.Wait4(int(pid), &ws, 0, nil)
}
if err != nil {
return errors.Wrapf(err, "failed to find pid=%d process", pid)
}
errno = syscall.Errno(ws.ExitStatus())
if errno != 0 {
return errors.Wrap(errno, "failed to mount")
}
return nil
}
// forkAndMountat will fork thread, change working dir and mount.
//
// precondition: the runtime OS thread must be locked.
func forkAndMountat(dirfd uintptr, source, target, fstype, flags, data uintptr) (pid uintptr, errno syscall.Errno) {
// block signal during clone
beforeFork()
// the cloned thread shares the open file descriptor, but the thread
// never be reused by runtime.
pid, _, errno = syscall.RawSyscall6(syscall.SYS_CLONE, uintptr(syscall.SIGCHLD)|syscall.CLONE_FILES, 0, 0, 0, 0, 0)
if errno != 0 || pid != 0 {
// restore all signals
afterFork()
return
}
// restore all signals
afterForkInChild()
// change working dir
_, _, errno = syscall.RawSyscall(syscall.SYS_FCHDIR, dirfd, 0, 0)
if errno != 0 {
goto childerr
}
_, _, errno = syscall.RawSyscall6(syscall.SYS_MOUNT, source, target, fstype, flags, data, 0)
childerr:
syscall.RawSyscall(syscall.SYS_EXIT, uintptr(errno), 0, 0)
panic("unreachable")
}

View file

@ -18,7 +18,9 @@
package sys
import "golang.org/x/sys/unix"
import (
"golang.org/x/sys/unix"
)
// Exit is the wait4 information from an exited process
type Exit struct {

View file

@ -0,0 +1,52 @@
/*
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 sys
import (
"unsafe"
"golang.org/x/sys/unix"
)
// If arg2 is nonzero, set the "child subreaper" attribute of the
// calling process; if arg2 is zero, unset the attribute. When a
// process is marked as a child subreaper, all of the children
// that it creates, and their descendants, will be marked as
// having a subreaper. In effect, a subreaper fulfills the role
// of init(1) for its descendant processes. Upon termination of
// a process that is orphaned (i.e., its immediate parent has
// already terminated) and marked as having a subreaper, the
// nearest still living ancestor subreaper will receive a SIGCHLD
// signal and be able to wait(2) on the process to discover its
// termination status.
const setChildSubreaper = 36
// SetSubreaper sets the value i as the subreaper setting for the calling process
func SetSubreaper(i int) error {
return unix.Prctl(setChildSubreaper, uintptr(i), 0, 0, 0)
}
// GetSubreaper returns the subreaper setting for the calling process
func GetSubreaper() (int, error) {
var i uintptr
if err := unix.Prctl(unix.PR_GET_CHILD_SUBREAPER, uintptr(unsafe.Pointer(&i)), 0, 0, 0); err != nil {
return -1, err
}
return int(i), nil
}

View file

@ -0,0 +1,30 @@
/*
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 sys
import (
_ "unsafe" // required for go:linkname.
)
//go:linkname beforeFork syscall.runtime_BeforeFork
func beforeFork()
//go:linkname afterFork syscall.runtime_AfterFork
func afterFork()
//go:linkname afterForkInChild syscall.runtime_AfterForkInChild
func afterForkInChild()

View file

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

View file

@ -40,6 +40,7 @@ import (
"github.com/containerd/typeurl"
google_protobuf "github.com/gogo/protobuf/types"
digest "github.com/opencontainers/go-digest"
is "github.com/opencontainers/image-spec/specs-go"
"github.com/opencontainers/image-spec/specs-go/v1"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
@ -424,6 +425,9 @@ func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Imag
return nil, err
}
index := v1.Index{
Versioned: is.Versioned{
SchemaVersion: 2,
},
Annotations: make(map[string]string),
}
if err := t.checkpointTask(ctx, &index, request); err != nil {
@ -454,10 +458,7 @@ func (t *task) Checkpoint(ctx context.Context, opts ...CheckpointTaskOpts) (Imag
if im, err = t.client.ImageService().Create(ctx, im); err != nil {
return nil, err
}
return &image{
client: t.client,
i: im,
}, nil
return NewImage(t.client, im), nil
}
// UpdateTaskInfo allows updated specific settings to be changed on a task

View file

@ -22,7 +22,6 @@ import (
"github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/runtime/linux/runctypes"
)
// NewTaskOpts allows the caller to set options on a new task
@ -36,14 +35,6 @@ func WithRootFS(mounts []mount.Mount) NewTaskOpts {
}
}
// WithExit causes the task to exit after a successful checkpoint
func WithExit(r *CheckpointTaskInfo) error {
r.Options = &runctypes.CheckpointOptions{
Exit: true,
}
return nil
}
// WithCheckpointName sets the image name for the checkpoint
func WithCheckpointName(name string) CheckpointTaskOpts {
return func(r *CheckpointTaskInfo) error {

View file

@ -1,6 +1,6 @@
github.com/containerd/go-runc f271fa2021de855d4d918dbef83c5fe19db1bdd5
github.com/containerd/console 5d1b48d6114b8c9666f0c8b916f871af97b0a761
github.com/containerd/cgroups fe281dd265766145e943a034aa41086474ea6130
github.com/containerd/go-runc edcf3de1f4971445c42d61f20d506b30612aa031
github.com/containerd/console 4d8a41f4ce5b9bae77c41786ea2458330f43f081
github.com/containerd/cgroups 5e610833b72089b37d0e615de9a92dfc043757c2
github.com/containerd/typeurl a93fcdb778cd272c6e9b3028b2f42d813e785d40
github.com/containerd/fifo 3d5202aec260678c48179c56f40e6f38a095738c
github.com/containerd/btrfs 2e1aa0ddf94f91fa282b6ed87c23bf0d64911244
@ -19,7 +19,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.0
github.com/gogo/protobuf v1.0.0
github.com/gogo/googleapis 08a7655d27152912db7aaf4f983275eaf8d128ef
github.com/golang/protobuf v1.1.0
github.com/opencontainers/runtime-spec v1.0.1
github.com/opencontainers/runtime-spec d810dbc60d8c5aeeb3d054bd1132fab2121968ce # v1.0.1-43-gd810dbc
github.com/opencontainers/runc 69663f0bd4b60df09991c08812a60108003fa340
github.com/sirupsen/logrus v1.0.0
github.com/urfave/cli 7bc6a0acffa589f415f88aca16cc1de5ffd66f9c
@ -27,7 +27,7 @@ golang.org/x/net b3756b4b77d7b13260a0a2ec658753cf48922eac
google.golang.org/grpc v1.12.0
github.com/pkg/errors v0.8.0
github.com/opencontainers/go-digest c9281466c8b2f606084ac71339773efd177436e7
golang.org/x/sys 314a259e304ff91bd6985da2a7149bbf91237993 https://github.com/golang/sys
golang.org/x/sys 1b2967e3c290b7c545b3db0deeda16e9be4f98a2 https://github.com/golang/sys
github.com/opencontainers/image-spec v1.0.1
golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
@ -43,7 +43,7 @@ gotest.tools v2.1.0
github.com/google/go-cmp v0.1.0
# cri dependencies
github.com/containerd/cri v1.11.0
github.com/containerd/cri v1.11.1
github.com/containerd/go-cni 5882530828ecf62032409b298a3e8b19e08b6534
github.com/blang/semver v3.1.0
github.com/containernetworking/cni v0.6.0

View file

@ -6,6 +6,7 @@ import (
"errors"
"fmt"
"os"
"path/filepath"
"sort"
"github.com/containerd/continuity/devices"
@ -25,6 +26,18 @@ func (d *driver) Mkfifo(path string, mode os.FileMode) error {
return devices.Mknod(path, mode, 0, 0)
}
// Lchmod changes the mode of an file not following symlinks.
func (d *driver) Lchmod(path string, mode os.FileMode) (err error) {
if !filepath.IsAbs(path) {
path, err = filepath.Abs(path)
if err != nil {
return
}
}
return sysx.Fchmodat(0, path, uint32(mode), sysx.AtSymlinkNofollow)
}
// Getxattr returns all of the extended attributes for the file at path p.
func (d *driver) Getxattr(p string) (map[string][]byte, error) {
xattrs, err := sysx.Listxattr(p)

View file

@ -1,19 +0,0 @@
package driver
import (
"os"
"golang.org/x/sys/unix"
)
// Lchmod changes the mode of a file not following symlinks.
func (d *driver) Lchmod(path string, mode os.FileMode) error {
// On Linux, file mode is not supported for symlinks,
// and fchmodat() does not support AT_SYMLINK_NOFOLLOW,
// so symlinks need to be skipped entirely.
if st, err := os.Stat(path); err == nil && st.Mode()&os.ModeSymlink != 0 {
return nil
}
return unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), 0)
}

View file

@ -1,14 +0,0 @@
// +build darwin freebsd solaris
package driver
import (
"os"
"golang.org/x/sys/unix"
)
// Lchmod changes the mode of a file not following symlinks.
func (d *driver) Lchmod(path string, mode os.FileMode) error {
return unix.Fchmodat(unix.AT_FDCWD, path, uint32(mode), unix.AT_SYMLINK_NOFOLLOW)
}

View file

@ -10,8 +10,8 @@ type Usage struct {
// DiskUsage counts the number of inodes and disk usage for the resources under
// path.
func DiskUsage(ctx context.Context, roots ...string) (Usage, error) {
return diskUsage(ctx, roots...)
func DiskUsage(roots ...string) (Usage, error) {
return diskUsage(roots...)
}
// DiffUsage counts the numbers of inodes and disk usage in the

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