Add wait API endpoint for waiting on process exit

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
This commit is contained in:
Michael Crosby 2017-09-18 15:54:29 -04:00
parent 2f5dda6229
commit d67763d922
23 changed files with 1314 additions and 355 deletions

View File

@ -3576,6 +3576,45 @@ file {
json_name: "metrics" json_name: "metrics"
} }
} }
message_type {
name: "WaitRequest"
field {
name: "container_id"
number: 1
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "containerId"
}
field {
name: "exec_id"
number: 2
label: LABEL_OPTIONAL
type: TYPE_STRING
json_name: "execId"
}
}
message_type {
name: "WaitResponse"
field {
name: "exit_status"
number: 1
label: LABEL_OPTIONAL
type: TYPE_UINT32
json_name: "exitStatus"
}
field {
name: "exited_at"
number: 2
label: LABEL_OPTIONAL
type: TYPE_MESSAGE
type_name: ".google.protobuf.Timestamp"
options {
65010: 1
65001: 0
}
json_name: "exitedAt"
}
}
service { service {
name: "Tasks" name: "Tasks"
method { method {
@ -3658,6 +3697,11 @@ file {
input_type: ".containerd.services.tasks.v1.MetricsRequest" input_type: ".containerd.services.tasks.v1.MetricsRequest"
output_type: ".containerd.services.tasks.v1.MetricsResponse" output_type: ".containerd.services.tasks.v1.MetricsResponse"
} }
method {
name: "Wait"
input_type: ".containerd.services.tasks.v1.WaitRequest"
output_type: ".containerd.services.tasks.v1.WaitResponse"
}
} }
options { options {
go_package: "github.com/containerd/containerd/api/services/tasks/v1;tasks" go_package: "github.com/containerd/containerd/api/services/tasks/v1;tasks"

View File

@ -34,6 +34,8 @@
UpdateTaskRequest UpdateTaskRequest
MetricsRequest MetricsRequest
MetricsResponse MetricsResponse
WaitRequest
WaitResponse
*/ */
package tasks package tasks
@ -316,6 +318,24 @@ func (m *MetricsResponse) Reset() { *m = MetricsResponse{} }
func (*MetricsResponse) ProtoMessage() {} func (*MetricsResponse) ProtoMessage() {}
func (*MetricsResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{24} } func (*MetricsResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{24} }
type WaitRequest struct {
ContainerID string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"`
ExecID string `protobuf:"bytes,2,opt,name=exec_id,json=execId,proto3" json:"exec_id,omitempty"`
}
func (m *WaitRequest) Reset() { *m = WaitRequest{} }
func (*WaitRequest) ProtoMessage() {}
func (*WaitRequest) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{25} }
type WaitResponse struct {
ExitStatus uint32 `protobuf:"varint,1,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"`
ExitedAt time.Time `protobuf:"bytes,2,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"`
}
func (m *WaitResponse) Reset() { *m = WaitResponse{} }
func (*WaitResponse) ProtoMessage() {}
func (*WaitResponse) Descriptor() ([]byte, []int) { return fileDescriptorTasks, []int{26} }
func init() { func init() {
proto.RegisterType((*CreateTaskRequest)(nil), "containerd.services.tasks.v1.CreateTaskRequest") proto.RegisterType((*CreateTaskRequest)(nil), "containerd.services.tasks.v1.CreateTaskRequest")
proto.RegisterType((*CreateTaskResponse)(nil), "containerd.services.tasks.v1.CreateTaskResponse") proto.RegisterType((*CreateTaskResponse)(nil), "containerd.services.tasks.v1.CreateTaskResponse")
@ -342,6 +362,8 @@ func init() {
proto.RegisterType((*UpdateTaskRequest)(nil), "containerd.services.tasks.v1.UpdateTaskRequest") proto.RegisterType((*UpdateTaskRequest)(nil), "containerd.services.tasks.v1.UpdateTaskRequest")
proto.RegisterType((*MetricsRequest)(nil), "containerd.services.tasks.v1.MetricsRequest") proto.RegisterType((*MetricsRequest)(nil), "containerd.services.tasks.v1.MetricsRequest")
proto.RegisterType((*MetricsResponse)(nil), "containerd.services.tasks.v1.MetricsResponse") proto.RegisterType((*MetricsResponse)(nil), "containerd.services.tasks.v1.MetricsResponse")
proto.RegisterType((*WaitRequest)(nil), "containerd.services.tasks.v1.WaitRequest")
proto.RegisterType((*WaitResponse)(nil), "containerd.services.tasks.v1.WaitResponse")
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -375,6 +397,7 @@ type TasksClient interface {
Checkpoint(ctx context.Context, in *CheckpointTaskRequest, opts ...grpc.CallOption) (*CheckpointTaskResponse, error) Checkpoint(ctx context.Context, in *CheckpointTaskRequest, opts ...grpc.CallOption) (*CheckpointTaskResponse, error)
Update(ctx context.Context, in *UpdateTaskRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) Update(ctx context.Context, in *UpdateTaskRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error)
Metrics(ctx context.Context, in *MetricsRequest, opts ...grpc.CallOption) (*MetricsResponse, error) Metrics(ctx context.Context, in *MetricsRequest, opts ...grpc.CallOption) (*MetricsResponse, error)
Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*WaitResponse, error)
} }
type tasksClient struct { type tasksClient struct {
@ -529,6 +552,15 @@ func (c *tasksClient) Metrics(ctx context.Context, in *MetricsRequest, opts ...g
return out, nil return out, nil
} }
func (c *tasksClient) Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*WaitResponse, error) {
out := new(WaitResponse)
err := grpc.Invoke(ctx, "/containerd.services.tasks.v1.Tasks/Wait", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Tasks service // Server API for Tasks service
type TasksServer interface { type TasksServer interface {
@ -552,6 +584,7 @@ type TasksServer interface {
Checkpoint(context.Context, *CheckpointTaskRequest) (*CheckpointTaskResponse, error) Checkpoint(context.Context, *CheckpointTaskRequest) (*CheckpointTaskResponse, error)
Update(context.Context, *UpdateTaskRequest) (*google_protobuf.Empty, error) Update(context.Context, *UpdateTaskRequest) (*google_protobuf.Empty, error)
Metrics(context.Context, *MetricsRequest) (*MetricsResponse, error) Metrics(context.Context, *MetricsRequest) (*MetricsResponse, error)
Wait(context.Context, *WaitRequest) (*WaitResponse, error)
} }
func RegisterTasksServer(s *grpc.Server, srv TasksServer) { func RegisterTasksServer(s *grpc.Server, srv TasksServer) {
@ -846,6 +879,24 @@ func _Tasks_Metrics_Handler(srv interface{}, ctx context.Context, dec func(inter
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Tasks_Wait_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(WaitRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(TasksServer).Wait(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/containerd.services.tasks.v1.Tasks/Wait",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(TasksServer).Wait(ctx, req.(*WaitRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Tasks_serviceDesc = grpc.ServiceDesc{ var _Tasks_serviceDesc = grpc.ServiceDesc{
ServiceName: "containerd.services.tasks.v1.Tasks", ServiceName: "containerd.services.tasks.v1.Tasks",
HandlerType: (*TasksServer)(nil), HandlerType: (*TasksServer)(nil),
@ -914,6 +965,10 @@ var _Tasks_serviceDesc = grpc.ServiceDesc{
MethodName: "Metrics", MethodName: "Metrics",
Handler: _Tasks_Metrics_Handler, Handler: _Tasks_Metrics_Handler,
}, },
{
MethodName: "Wait",
Handler: _Tasks_Wait_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "github.com/containerd/containerd/api/services/tasks/v1/tasks.proto", Metadata: "github.com/containerd/containerd/api/services/tasks/v1/tasks.proto",
@ -1778,6 +1833,67 @@ func (m *MetricsResponse) MarshalTo(dAtA []byte) (int, error) {
return i, nil return i, nil
} }
func (m *WaitRequest) 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 *WaitRequest) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.ContainerID) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintTasks(dAtA, i, uint64(len(m.ContainerID)))
i += copy(dAtA[i:], m.ContainerID)
}
if len(m.ExecID) > 0 {
dAtA[i] = 0x12
i++
i = encodeVarintTasks(dAtA, i, uint64(len(m.ExecID)))
i += copy(dAtA[i:], m.ExecID)
}
return i, nil
}
func (m *WaitResponse) 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 *WaitResponse) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.ExitStatus != 0 {
dAtA[i] = 0x8
i++
i = encodeVarintTasks(dAtA, i, uint64(m.ExitStatus))
}
dAtA[i] = 0x12
i++
i = encodeVarintTasks(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt)))
n10, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:])
if err != nil {
return 0, err
}
i += n10
return i, nil
}
func encodeFixed64Tasks(dAtA []byte, offset int, v uint64) int { func encodeFixed64Tasks(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v) dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8) dAtA[offset+1] = uint8(v >> 8)
@ -2175,6 +2291,31 @@ func (m *MetricsResponse) Size() (n int) {
return n return n
} }
func (m *WaitRequest) Size() (n int) {
var l int
_ = l
l = len(m.ContainerID)
if l > 0 {
n += 1 + l + sovTasks(uint64(l))
}
l = len(m.ExecID)
if l > 0 {
n += 1 + l + sovTasks(uint64(l))
}
return n
}
func (m *WaitResponse) Size() (n int) {
var l int
_ = l
if m.ExitStatus != 0 {
n += 1 + sovTasks(uint64(m.ExitStatus))
}
l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt)
n += 1 + l + sovTasks(uint64(l))
return n
}
func sovTasks(x uint64) (n int) { func sovTasks(x uint64) (n int) {
for { for {
n++ n++
@ -2468,6 +2609,28 @@ func (this *MetricsResponse) String() string {
}, "") }, "")
return s return s
} }
func (this *WaitRequest) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&WaitRequest{`,
`ContainerID:` + fmt.Sprintf("%v", this.ContainerID) + `,`,
`ExecID:` + fmt.Sprintf("%v", this.ExecID) + `,`,
`}`,
}, "")
return s
}
func (this *WaitResponse) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&WaitResponse{`,
`ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`,
`ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`,
`}`,
}, "")
return s
}
func valueToStringTasks(v interface{}) string { func valueToStringTasks(v interface{}) string {
rv := reflect.ValueOf(v) rv := reflect.ValueOf(v)
if rv.IsNil() { if rv.IsNil() {
@ -5281,6 +5444,213 @@ func (m *MetricsResponse) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *WaitRequest) 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 ErrIntOverflowTasks
}
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: WaitRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: WaitRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ContainerID", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTasks
}
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 ErrInvalidLengthTasks
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ContainerID = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ExecID", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTasks
}
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 ErrInvalidLengthTasks
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ExecID = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipTasks(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthTasks
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *WaitResponse) 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 ErrIntOverflowTasks
}
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: WaitResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: WaitResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ExitStatus", wireType)
}
m.ExitStatus = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTasks
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ExitStatus |= (uint32(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ExitedAt", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowTasks
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthTasks
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipTasks(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthTasks
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipTasks(dAtA []byte) (n int, err error) { func skipTasks(dAtA []byte) (n int, err error) {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
@ -5391,85 +5761,88 @@ func init() {
} }
var fileDescriptorTasks = []byte{ var fileDescriptorTasks = []byte{
// 1274 bytes of a gzipped FileDescriptorProto // 1313 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x4f, 0x6f, 0x1b, 0x45, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0x5b, 0x6f, 0x1b, 0x45,
0x14, 0xef, 0xc6, 0xf6, 0xda, 0x7e, 0x6e, 0xda, 0x64, 0x48, 0xc3, 0xb2, 0x54, 0x71, 0x58, 0x24, 0x14, 0xee, 0xfa, 0xee, 0xe3, 0xa4, 0x4d, 0x86, 0x34, 0x98, 0xa5, 0x8a, 0xc3, 0x22, 0x21, 0x13,
0x64, 0x02, 0xdd, 0xa5, 0x2e, 0xaa, 0x10, 0xad, 0x90, 0x9a, 0x3f, 0x44, 0x16, 0x54, 0x4d, 0xb7, 0xe8, 0x2e, 0x75, 0x51, 0x85, 0x68, 0x85, 0xd4, 0x5c, 0x88, 0x2c, 0xa8, 0x9a, 0x6e, 0xcb, 0x45,
0x05, 0xa1, 0x5e, 0xc2, 0x76, 0x77, 0xe2, 0x8c, 0x62, 0xef, 0x6e, 0x77, 0xc6, 0x69, 0x03, 0x07, 0x95, 0x50, 0xd8, 0xee, 0x4e, 0x9c, 0x51, 0xec, 0xdd, 0xed, 0xce, 0x38, 0x6d, 0xe0, 0x01, 0x7e,
0xf8, 0x08, 0xbd, 0x72, 0xe1, 0xf3, 0xe4, 0xc8, 0x11, 0x21, 0x14, 0xa8, 0xbf, 0x05, 0x07, 0x24, 0x42, 0x5f, 0x79, 0xe1, 0xf7, 0xe4, 0x91, 0x47, 0x84, 0xaa, 0x40, 0xfd, 0x2f, 0x78, 0x43, 0x73,
0x34, 0x7f, 0x76, 0xb3, 0xb1, 0xe3, 0xd8, 0xa9, 0x1b, 0x2e, 0xed, 0xcc, 0xec, 0xef, 0xbd, 0x79, 0xd9, 0xcd, 0xc6, 0x8e, 0xbd, 0x4e, 0xd3, 0xf0, 0xd2, 0xce, 0xcc, 0x7e, 0xe7, 0xcc, 0x99, 0x6f,
0xf3, 0x9b, 0x37, 0xef, 0xfd, 0x1c, 0x58, 0x6d, 0x13, 0xb6, 0xdb, 0x7b, 0x6a, 0xfb, 0x51, 0xd7, 0xce, 0x9c, 0xf3, 0x39, 0xb0, 0xda, 0x21, 0x6c, 0xb7, 0xff, 0xc4, 0x74, 0x83, 0x9e, 0xe5, 0x06,
0xf1, 0xa3, 0x90, 0x79, 0x24, 0xc4, 0x49, 0x90, 0x1f, 0x7a, 0x31, 0x71, 0x28, 0x4e, 0xf6, 0x89, 0x3e, 0x73, 0x88, 0x8f, 0x23, 0x2f, 0x3d, 0x74, 0x42, 0x62, 0x51, 0x1c, 0xed, 0x13, 0x17, 0x53,
0x8f, 0xa9, 0xc3, 0x3c, 0xba, 0x47, 0x9d, 0xfd, 0x9b, 0x72, 0x60, 0xc7, 0x49, 0xc4, 0x22, 0x74, 0x8b, 0x39, 0x74, 0x8f, 0x5a, 0xfb, 0x37, 0xe4, 0xc0, 0x0c, 0xa3, 0x80, 0x05, 0xe8, 0xda, 0x31,
0xfd, 0x18, 0x6d, 0xa7, 0x48, 0x5b, 0x02, 0xf6, 0x6f, 0x9a, 0xef, 0xb6, 0xa3, 0xa8, 0xdd, 0xc1, 0xda, 0x8c, 0x91, 0xa6, 0x04, 0xec, 0xdf, 0xd0, 0xdf, 0xed, 0x04, 0x41, 0xa7, 0x8b, 0x2d, 0x81,
0x8e, 0xc0, 0x3e, 0xed, 0xed, 0x38, 0xb8, 0x1b, 0xb3, 0x03, 0x69, 0x6a, 0xbe, 0x33, 0xf8, 0xd1, 0x7d, 0xd2, 0xdf, 0xb1, 0x70, 0x2f, 0x64, 0x07, 0xd2, 0x54, 0x7f, 0x67, 0xf8, 0xa3, 0xe3, 0xc7,
0x0b, 0xd3, 0x4f, 0x0b, 0xed, 0xa8, 0x1d, 0x89, 0xa1, 0xc3, 0x47, 0x6a, 0xf5, 0xf6, 0x44, 0xf1, 0x9f, 0x16, 0x3a, 0x41, 0x27, 0x10, 0x43, 0x8b, 0x8f, 0xd4, 0xea, 0xad, 0xa9, 0xe2, 0x65, 0x07,
0xb2, 0x83, 0x18, 0x53, 0xa7, 0x1b, 0xf5, 0x42, 0xa6, 0xec, 0x3e, 0x3b, 0x8f, 0x1d, 0x66, 0x09, 0x21, 0xa6, 0x56, 0x2f, 0xe8, 0xfb, 0x4c, 0xd9, 0x7d, 0x76, 0x16, 0x3b, 0xcc, 0x22, 0xe2, 0xaa,
0xf1, 0xd5, 0xe9, 0xcc, 0x3b, 0xe7, 0xb0, 0x0c, 0x30, 0xf5, 0x13, 0x12, 0xb3, 0x28, 0x51, 0xc6, 0xd3, 0xe9, 0xb7, 0xcf, 0x60, 0xe9, 0x61, 0xea, 0x46, 0x24, 0x64, 0x41, 0xa4, 0x8c, 0x3f, 0x3f,
0x9f, 0x9f, 0xc3, 0x98, 0x33, 0x26, 0xfe, 0x51, 0xb6, 0xf5, 0x41, 0x6e, 0x18, 0xe9, 0x62, 0xca, 0x83, 0x31, 0x67, 0x4c, 0xfc, 0xa3, 0x6c, 0x1b, 0xc3, 0xdc, 0x30, 0xd2, 0xc3, 0x94, 0x39, 0xbd,
0xbc, 0x6e, 0x2c, 0x01, 0xd6, 0xe1, 0x0c, 0xcc, 0xaf, 0x25, 0xd8, 0x63, 0xf8, 0xb1, 0x47, 0xf7, 0x50, 0x02, 0x8c, 0xc3, 0x1c, 0xcc, 0xaf, 0x45, 0xd8, 0x61, 0xf8, 0x91, 0x43, 0xf7, 0x6c, 0xfc,
0x5c, 0xfc, 0xac, 0x87, 0x29, 0x43, 0x4d, 0xb8, 0x9c, 0xb9, 0xdf, 0x26, 0x81, 0xa1, 0x2d, 0x6b, 0xb4, 0x8f, 0x29, 0x43, 0x2d, 0x98, 0x49, 0xdc, 0x6f, 0x13, 0xaf, 0xae, 0x2d, 0x6b, 0xcd, 0xea,
0x8d, 0xea, 0xea, 0xd5, 0xfe, 0x51, 0xbd, 0xb6, 0x96, 0xae, 0xb7, 0xd6, 0xdd, 0x5a, 0x06, 0x6a, 0xea, 0x95, 0xc1, 0x51, 0xa3, 0xb6, 0x16, 0xaf, 0xb7, 0xd7, 0xed, 0x5a, 0x02, 0x6a, 0x7b, 0xc8,
0x05, 0xc8, 0x01, 0x3d, 0x89, 0x22, 0xb6, 0x43, 0x8d, 0xc2, 0x72, 0xa1, 0x51, 0x6b, 0xbe, 0x6d, 0x82, 0x52, 0x14, 0x04, 0x6c, 0x87, 0xd6, 0xf3, 0xcb, 0xf9, 0x66, 0xad, 0xf5, 0xb6, 0x99, 0xba,
0xe7, 0xae, 0x54, 0x44, 0x67, 0xdf, 0xe7, 0x64, 0xba, 0x0a, 0x86, 0x16, 0xa0, 0x44, 0x59, 0x40, 0x52, 0x11, 0x9d, 0x79, 0x8f, 0x93, 0x69, 0x2b, 0x18, 0x5a, 0x80, 0x22, 0x65, 0x1e, 0xf1, 0xeb,
0x42, 0xa3, 0xc8, 0xbd, 0xbb, 0x72, 0x82, 0x16, 0x41, 0xa7, 0x2c, 0x88, 0x7a, 0xcc, 0x28, 0x89, 0x05, 0xee, 0xdd, 0x96, 0x13, 0xb4, 0x08, 0x25, 0xca, 0xbc, 0xa0, 0xcf, 0xea, 0x45, 0xb1, 0xac,
0x65, 0x35, 0x53, 0xeb, 0x38, 0x49, 0x0c, 0x3d, 0x5b, 0xc7, 0x49, 0x82, 0x4c, 0xa8, 0x30, 0x9c, 0x66, 0x6a, 0x1d, 0x47, 0x51, 0xbd, 0x94, 0xac, 0xe3, 0x28, 0x42, 0x3a, 0x54, 0x18, 0x8e, 0x7a,
0x74, 0x49, 0xe8, 0x75, 0x8c, 0xf2, 0xb2, 0xd6, 0xa8, 0xb8, 0xd9, 0x1c, 0xdd, 0x05, 0xf0, 0x77, 0xc4, 0x77, 0xba, 0xf5, 0xf2, 0xb2, 0xd6, 0xac, 0xd8, 0xc9, 0x1c, 0xdd, 0x01, 0x70, 0x77, 0xb1,
0xb1, 0xbf, 0x17, 0x47, 0x24, 0x64, 0x46, 0x65, 0x59, 0x6b, 0xd4, 0x9a, 0xd7, 0x87, 0xc3, 0x5a, 0xbb, 0x17, 0x06, 0xc4, 0x67, 0xf5, 0xca, 0xb2, 0xd6, 0xac, 0xb5, 0xae, 0x8d, 0x86, 0xb5, 0x9e,
0xcf, 0x18, 0x77, 0x73, 0x78, 0x64, 0x43, 0x39, 0x8a, 0x19, 0x89, 0x42, 0x6a, 0x54, 0x85, 0xe9, 0x30, 0x6e, 0xa7, 0xf0, 0xc8, 0x84, 0x72, 0x10, 0x32, 0x12, 0xf8, 0xb4, 0x5e, 0x15, 0xa6, 0x0b,
0x82, 0x2d, 0xd9, 0xb4, 0x53, 0x36, 0xed, 0x7b, 0xe1, 0x81, 0x9b, 0x82, 0xac, 0x27, 0x80, 0xf2, 0xa6, 0x64, 0xd3, 0x8c, 0xd9, 0x34, 0xef, 0xfa, 0x07, 0x76, 0x0c, 0x32, 0x1e, 0x03, 0x4a, 0x33,
0x4c, 0xd2, 0x38, 0x0a, 0x29, 0x7e, 0x2d, 0x2a, 0xe7, 0xa0, 0x10, 0x93, 0xc0, 0x98, 0x59, 0xd6, 0x49, 0xc3, 0xc0, 0xa7, 0xf8, 0xb5, 0xa8, 0x9c, 0x83, 0x7c, 0x48, 0xbc, 0x7a, 0x6e, 0x59, 0x6b,
0x1a, 0xb3, 0x2e, 0x1f, 0x5a, 0x6d, 0xb8, 0xfc, 0x88, 0x79, 0x09, 0x9b, 0xe6, 0x82, 0xde, 0x87, 0xce, 0xda, 0x7c, 0x68, 0x74, 0x60, 0xe6, 0x21, 0x73, 0x22, 0x76, 0x9e, 0x0b, 0x7a, 0x1f, 0xca,
0x32, 0x7e, 0x81, 0xfd, 0x6d, 0xe5, 0xb9, 0xba, 0x0a, 0xfd, 0xa3, 0xba, 0xbe, 0xf1, 0x02, 0xfb, 0xf8, 0x39, 0x76, 0xb7, 0x95, 0xe7, 0xea, 0x2a, 0x0c, 0x8e, 0x1a, 0xa5, 0x8d, 0xe7, 0xd8, 0x6d,
0xad, 0x75, 0x57, 0xe7, 0x9f, 0x5a, 0x81, 0xf5, 0x1e, 0xcc, 0xaa, 0x8d, 0x54, 0xfc, 0x2a, 0x16, 0xaf, 0xdb, 0x25, 0xfe, 0xa9, 0xed, 0x19, 0xef, 0xc1, 0xac, 0xda, 0x48, 0xc5, 0xaf, 0x62, 0xd1,
0xed, 0x38, 0x96, 0x4d, 0x98, 0x5f, 0xc7, 0x1d, 0x3c, 0x75, 0xc6, 0x58, 0xbf, 0x6a, 0x70, 0x45, 0x8e, 0x63, 0xd9, 0x84, 0xf9, 0x75, 0xdc, 0xc5, 0xe7, 0xce, 0x18, 0xe3, 0x77, 0x0d, 0x2e, 0x4b,
0x7a, 0xca, 0x76, 0x5b, 0x84, 0x99, 0xcc, 0x58, 0xef, 0x1f, 0xd5, 0x67, 0x5a, 0xeb, 0xee, 0x0c, 0x4f, 0xc9, 0x6e, 0x8b, 0x90, 0x4b, 0x8c, 0x4b, 0x83, 0xa3, 0x46, 0xae, 0xbd, 0x6e, 0xe7, 0xc8,
0x39, 0x85, 0x11, 0x54, 0x87, 0x1a, 0x7e, 0x41, 0xd8, 0x36, 0x65, 0x1e, 0xeb, 0xf1, 0x9c, 0xe3, 0x29, 0x8c, 0xa0, 0x06, 0xd4, 0xf0, 0x73, 0xc2, 0xb6, 0x29, 0x73, 0x58, 0x9f, 0xe7, 0x1c, 0xff,
0x5f, 0x80, 0x2f, 0x3d, 0x12, 0x2b, 0xe8, 0x1e, 0x54, 0xf9, 0x0c, 0x07, 0xdb, 0x1e, 0x13, 0x29, 0x02, 0x7c, 0xe9, 0xa1, 0x58, 0x41, 0x77, 0xa1, 0xca, 0x67, 0xd8, 0xdb, 0x76, 0x98, 0x48, 0xb1,
0x56, 0x6b, 0x9a, 0x43, 0x17, 0xf8, 0x38, 0x7d, 0x0e, 0xab, 0x95, 0xc3, 0xa3, 0xfa, 0xa5, 0x97, 0x5a, 0x4b, 0x1f, 0xb9, 0xc0, 0x47, 0xf1, 0x73, 0x58, 0xad, 0x1c, 0x1e, 0x35, 0x2e, 0xbd, 0xf8,
0x7f, 0xd5, 0x35, 0xb7, 0x22, 0xcd, 0xee, 0x31, 0x2b, 0x82, 0x05, 0x19, 0xdf, 0x56, 0x12, 0xf9, 0xbb, 0xa1, 0xd9, 0x15, 0x69, 0x76, 0x97, 0x19, 0x01, 0x2c, 0xc8, 0xf8, 0xb6, 0xa2, 0xc0, 0xc5,
0x98, 0xd2, 0x0b, 0x67, 0x1f, 0x03, 0x6c, 0xe2, 0x8b, 0xbf, 0xe4, 0x0d, 0xa8, 0x89, 0x6d, 0x14, 0x94, 0x5e, 0x38, 0xfb, 0x18, 0x60, 0x13, 0x5f, 0xfc, 0x25, 0x6f, 0x40, 0x4d, 0x6c, 0xa3, 0x48,
0xe9, 0xb7, 0xa1, 0x1c, 0xcb, 0x03, 0x8a, 0x2d, 0x06, 0xde, 0xc8, 0xfe, 0x4d, 0xf5, 0x4c, 0x52, 0xbf, 0x05, 0xe5, 0x50, 0x1e, 0x50, 0x6c, 0x31, 0xf4, 0x46, 0xf6, 0x6f, 0xa8, 0x67, 0x12, 0x93,
0x12, 0x52, 0xb0, 0xb5, 0x02, 0x73, 0x5f, 0x13, 0xca, 0x78, 0x1a, 0x64, 0xd4, 0x2c, 0x82, 0xbe, 0x10, 0x83, 0x8d, 0x15, 0x98, 0xfb, 0x9a, 0x50, 0xc6, 0xd3, 0x20, 0xa1, 0x66, 0x11, 0x4a, 0x3b,
0x43, 0x3a, 0x0c, 0x27, 0x32, 0x5a, 0x57, 0xcd, 0x78, 0xd2, 0xe4, 0xb0, 0xd9, 0xdb, 0x28, 0x89, 0xa4, 0xcb, 0x70, 0x24, 0xa3, 0xb5, 0xd5, 0x8c, 0x27, 0x4d, 0x0a, 0x9b, 0xbc, 0x8d, 0xa2, 0x28,
0x12, 0x6f, 0x68, 0xa2, 0x62, 0x9c, 0xbd, 0xad, 0x84, 0x5a, 0x2f, 0x35, 0xa8, 0x7d, 0x45, 0x3a, 0xf1, 0x75, 0x4d, 0x54, 0x8c, 0xc9, 0xdb, 0x4a, 0xa8, 0xf1, 0x42, 0x83, 0xda, 0x57, 0xa4, 0xdb,
0x9d, 0x8b, 0x26, 0x49, 0x14, 0x1c, 0xd2, 0xe6, 0x65, 0x45, 0xe6, 0x96, 0x9a, 0xf1, 0x54, 0xf4, 0xbd, 0x68, 0x92, 0x44, 0xc1, 0x21, 0x1d, 0x5e, 0x56, 0x64, 0x6e, 0xa9, 0x19, 0x4f, 0x45, 0xa7,
0x3a, 0x1d, 0x91, 0x51, 0x15, 0x97, 0x0f, 0xad, 0x7f, 0x34, 0x40, 0xdc, 0xf8, 0x0d, 0x64, 0x49, 0xdb, 0x15, 0x19, 0x55, 0xb1, 0xf9, 0xd0, 0xf8, 0x57, 0x03, 0xc4, 0x8d, 0xdf, 0x40, 0x96, 0x24,
0x56, 0x13, 0x67, 0x4e, 0xaf, 0x89, 0x85, 0x11, 0x35, 0xb1, 0x38, 0xb2, 0x26, 0x96, 0x06, 0x6a, 0x35, 0x31, 0x77, 0x7a, 0x4d, 0xcc, 0x8f, 0xa9, 0x89, 0x85, 0xb1, 0x35, 0xb1, 0x38, 0x54, 0x13,
0x62, 0x03, 0x8a, 0x34, 0xc6, 0xbe, 0xa8, 0xa2, 0xa3, 0x4a, 0x9a, 0x40, 0xe4, 0x59, 0x2a, 0x8f, 0x9b, 0x50, 0xa0, 0x21, 0x76, 0x45, 0x15, 0x1d, 0x57, 0xd2, 0x04, 0x22, 0xcd, 0x52, 0x79, 0x6c,
0x4c, 0xa5, 0x6b, 0xf0, 0xd6, 0x89, 0xa3, 0xcb, 0x9b, 0xb5, 0x7e, 0xd1, 0x60, 0xce, 0xc5, 0x94, 0x2a, 0x5d, 0x85, 0xb7, 0x4e, 0x1c, 0x5d, 0xde, 0xac, 0xf1, 0x9b, 0x06, 0x73, 0x36, 0xa6, 0xe4,
0xfc, 0x80, 0xb7, 0xd8, 0xc1, 0x85, 0x5f, 0xd5, 0x02, 0x94, 0x9e, 0x93, 0x80, 0xed, 0xaa, 0x9b, 0x27, 0xbc, 0xc5, 0x0e, 0x2e, 0xfc, 0xaa, 0x16, 0xa0, 0xf8, 0x8c, 0x78, 0x6c, 0x57, 0xdd, 0x94,
0x92, 0x13, 0xce, 0xce, 0x2e, 0x26, 0xed, 0x5d, 0xf9, 0xfa, 0x67, 0x5d, 0x35, 0xb3, 0x7e, 0x82, 0x9c, 0x70, 0x76, 0x76, 0x31, 0xe9, 0xec, 0xca, 0xd7, 0x3f, 0x6b, 0xab, 0x99, 0xf1, 0x0b, 0x5c,
0x2b, 0x6b, 0x9d, 0x88, 0xe2, 0xd6, 0x83, 0xff, 0x23, 0x30, 0x79, 0x9d, 0x05, 0x71, 0x0b, 0x72, 0x5e, 0xeb, 0x06, 0x14, 0xb7, 0xef, 0xff, 0x1f, 0x81, 0xc9, 0xeb, 0xcc, 0x8b, 0x5b, 0x90, 0x13,
0x62, 0x7d, 0x09, 0x73, 0x5b, 0x5e, 0x8f, 0x4e, 0x5d, 0x3f, 0x37, 0x61, 0xde, 0xc5, 0xb4, 0xd7, 0xe3, 0x4b, 0x98, 0xdb, 0x72, 0xfa, 0xf4, 0xdc, 0xf5, 0x73, 0x13, 0xe6, 0x6d, 0x4c, 0xfb, 0xbd,
0x9d, 0xda, 0xd1, 0x06, 0x5c, 0xe5, 0x8f, 0x73, 0x8b, 0x04, 0xd3, 0x24, 0xaf, 0xf5, 0x81, 0xac, 0x73, 0x3b, 0xda, 0x80, 0x2b, 0xfc, 0x71, 0x6e, 0x11, 0xef, 0x3c, 0xc9, 0x6b, 0x7c, 0x20, 0xeb,
0x07, 0xd2, 0x8d, 0x7a, 0xe2, 0x08, 0x8a, 0x31, 0x09, 0xe4, 0x0b, 0x9f, 0x75, 0xc5, 0xd8, 0xfa, 0x81, 0x74, 0xa3, 0x9e, 0x38, 0x82, 0x42, 0x48, 0x3c, 0xf9, 0xc2, 0x67, 0x6d, 0x31, 0x36, 0x5e,
0x53, 0x83, 0x6b, 0x6b, 0x59, 0x9f, 0x9d, 0x56, 0x77, 0x6c, 0xc3, 0x7c, 0xec, 0x25, 0x38, 0x64, 0x6a, 0x70, 0x75, 0x2d, 0xe9, 0xb3, 0xe7, 0xd5, 0x1d, 0xdb, 0x30, 0x1f, 0x3a, 0x11, 0xf6, 0xd9,
0xdb, 0xb9, 0x5e, 0x2f, 0xaf, 0xa4, 0xc9, 0x6b, 0xfa, 0x1f, 0x47, 0xf5, 0x95, 0x9c, 0x82, 0x8a, 0x76, 0xaa, 0xd7, 0xcb, 0x2b, 0x69, 0xf1, 0x9a, 0xfe, 0xd7, 0x51, 0x63, 0x25, 0xa5, 0xa0, 0x82,
0x62, 0x1c, 0x66, 0xe6, 0xd4, 0x69, 0x47, 0x37, 0x02, 0xd2, 0xc6, 0x94, 0xd9, 0xeb, 0xe2, 0x3f, 0x10, 0xfb, 0x89, 0x39, 0xb5, 0x3a, 0xc1, 0x75, 0x8f, 0x74, 0x30, 0x65, 0xe6, 0xba, 0xf8, 0xcf,
0x77, 0x4e, 0x3a, 0x5b, 0x3b, 0x55, 0x07, 0x14, 0x26, 0xd1, 0x01, 0xdf, 0xc1, 0xe2, 0xe0, 0xe9, 0x9e, 0x93, 0xce, 0xd6, 0x4e, 0xd5, 0x01, 0xf9, 0x69, 0x74, 0xc0, 0xf7, 0xb0, 0x38, 0x7c, 0x3a,
0x14, 0x19, 0x5f, 0x40, 0xed, 0x58, 0xdd, 0x9d, 0x5a, 0xf5, 0x86, 0x04, 0x49, 0xde, 0xc0, 0xfa, 0x45, 0xc6, 0x17, 0x50, 0x3b, 0x56, 0x77, 0xa7, 0x56, 0xbd, 0x11, 0x41, 0x92, 0x36, 0x30, 0x7e,
0x11, 0xe6, 0xbf, 0x89, 0x83, 0x37, 0xa0, 0xd5, 0x9a, 0x50, 0x4d, 0x30, 0x8d, 0x7a, 0x89, 0x8f, 0x86, 0xf9, 0x6f, 0x42, 0xef, 0x0d, 0x68, 0xb5, 0x16, 0x54, 0x23, 0x4c, 0x83, 0x7e, 0xe4, 0x62,
0xa9, 0xe0, 0x6a, 0xd4, 0xa1, 0x8e, 0x61, 0xd6, 0x0a, 0x5c, 0xb9, 0x2f, 0x45, 0x6d, 0xba, 0xb3, 0x2a, 0xb8, 0x1a, 0x77, 0xa8, 0x63, 0x98, 0xb1, 0x02, 0x97, 0xef, 0x49, 0x51, 0x1b, 0xef, 0x5c,
0x01, 0x65, 0x59, 0xdd, 0xe5, 0x51, 0xaa, 0x6e, 0x3a, 0xe5, 0x09, 0x95, 0x61, 0xb3, 0x5a, 0x5f, 0x87, 0xb2, 0xac, 0xee, 0xf2, 0x28, 0x55, 0x3b, 0x9e, 0xf2, 0x84, 0x4a, 0xb0, 0x49, 0xad, 0x2f,
0x56, 0x9a, 0x58, 0x9d, 0xdb, 0x38, 0x45, 0x1f, 0x0a, 0x80, 0x9b, 0x02, 0x9b, 0xff, 0xd6, 0xa0, 0x2b, 0x4d, 0xac, 0xce, 0x5d, 0x3f, 0x45, 0x1f, 0x0a, 0x80, 0x1d, 0x03, 0x8d, 0x1d, 0xa8, 0x7d,
0x24, 0x3a, 0x06, 0xda, 0x03, 0x5d, 0x6a, 0x2b, 0xe4, 0xd8, 0x67, 0xfd, 0x52, 0xb0, 0x87, 0xb4, 0xe7, 0x90, 0x8b, 0xef, 0x87, 0x11, 0xcc, 0xc8, 0x7d, 0x54, 0xac, 0x43, 0xda, 0x42, 0x9b, 0xac,
0xac, 0xf9, 0xc9, 0xe4, 0x06, 0x2a, 0xd4, 0xef, 0xa1, 0x24, 0x34, 0x10, 0x5a, 0x39, 0xdb, 0x34, 0x2d, 0x72, 0xaf, 0xa3, 0x2d, 0x5a, 0x2f, 0x67, 0xa0, 0x28, 0xba, 0x21, 0xda, 0x83, 0x92, 0xd4,
0xaf, 0xc8, 0xcc, 0x8f, 0x26, 0xc2, 0xaa, 0x1d, 0xda, 0xa0, 0x4b, 0x61, 0x31, 0xee, 0x38, 0x43, 0x8d, 0xc8, 0x32, 0x27, 0xfd, 0x0a, 0x32, 0x47, 0x74, 0xba, 0xfe, 0xc9, 0xf4, 0x06, 0xea, 0x68,
0x42, 0xcb, 0xfc, 0x78, 0x12, 0x83, 0x6c, 0xa3, 0x67, 0x30, 0x7b, 0x42, 0xc1, 0xa0, 0xe6, 0x24, 0x3f, 0x42, 0x51, 0xe8, 0x3b, 0xb4, 0x32, 0xd9, 0x34, 0xad, 0x36, 0xf5, 0x8f, 0xa6, 0xc2, 0xaa,
0xe6, 0x27, 0x1b, 0xd9, 0x39, 0xb7, 0x7c, 0x02, 0x85, 0x4d, 0xcc, 0x50, 0xe3, 0x6c, 0xa3, 0x63, 0x1d, 0x3a, 0x50, 0x92, 0xa2, 0x29, 0xeb, 0x38, 0x23, 0x22, 0x52, 0xff, 0x78, 0x1a, 0x83, 0x64,
0x99, 0x63, 0x7e, 0x38, 0x01, 0x32, 0xe3, 0xad, 0xc8, 0x2b, 0x0c, 0xb2, 0xcf, 0x36, 0x19, 0x54, 0xa3, 0xa7, 0x30, 0x7b, 0x42, 0x9d, 0xa1, 0xd6, 0x34, 0xe6, 0x27, 0x9b, 0xf4, 0x19, 0xb7, 0x7c,
0x25, 0xa6, 0x33, 0x31, 0x5e, 0x6d, 0xd4, 0x82, 0x22, 0x17, 0x19, 0x68, 0x4c, 0x6c, 0x39, 0x21, 0x0c, 0xf9, 0x4d, 0xcc, 0x50, 0x73, 0xb2, 0xd1, 0xb1, 0x84, 0xd3, 0x3f, 0x9c, 0x02, 0x99, 0xf0,
0x62, 0x2e, 0x0e, 0x3d, 0xa0, 0x0d, 0xfe, 0x23, 0x15, 0x6d, 0x41, 0x91, 0x77, 0x05, 0x34, 0x26, 0x56, 0xe0, 0xd5, 0x13, 0x99, 0x93, 0x4d, 0x86, 0x15, 0x97, 0x6e, 0x4d, 0x8d, 0x57, 0x1b, 0xb5,
0x0f, 0x87, 0x05, 0xc4, 0x48, 0x8f, 0x8f, 0xa0, 0x9a, 0xf5, 0xd6, 0x71, 0x54, 0x0c, 0x36, 0xe1, 0xa1, 0xc0, 0x05, 0x14, 0xca, 0x88, 0x2d, 0x25, 0xb2, 0xf4, 0xc5, 0x91, 0xe4, 0xde, 0xe0, 0x3f,
0x91, 0x4e, 0x1f, 0x40, 0x59, 0x75, 0x45, 0x34, 0xe6, 0xbe, 0x4f, 0x36, 0xcf, 0x33, 0x1c, 0x96, 0xc0, 0xd1, 0x16, 0x14, 0xf8, 0x53, 0x42, 0x19, 0x79, 0x38, 0x2a, 0x8e, 0xc6, 0x7a, 0x7c, 0x08,
0x44, 0x97, 0x1b, 0x17, 0xe1, 0x60, 0x2b, 0x1c, 0xe9, 0xf0, 0x21, 0xe8, 0xb2, 0xdd, 0x8d, 0x7b, 0xd5, 0x44, 0x37, 0x64, 0x51, 0x31, 0x2c, 0x30, 0xc6, 0x3a, 0xbd, 0x0f, 0x65, 0xd5, 0xf1, 0x51,
0x34, 0x43, 0x4d, 0x71, 0xa4, 0x4b, 0x02, 0x95, 0xb4, 0x63, 0xa1, 0x1b, 0xe3, 0x73, 0x24, 0xd7, 0xc6, 0x7d, 0x9f, 0x14, 0x06, 0x13, 0x1c, 0x16, 0x45, 0x07, 0xcf, 0x8a, 0x70, 0xb8, 0xcd, 0x8f,
0x20, 0x4d, 0x7b, 0x52, 0xb8, 0xca, 0xa8, 0xe7, 0x00, 0xb9, 0x9e, 0x72, 0x6b, 0x0c, 0xc5, 0xa7, 0x75, 0xf8, 0x00, 0x4a, 0xb2, 0x95, 0x67, 0x3d, 0x9a, 0x91, 0x86, 0x3f, 0xd6, 0x25, 0x81, 0x4a,
0x75, 0x47, 0xf3, 0xd3, 0xf3, 0x19, 0xa9, 0x8d, 0x1f, 0x82, 0x2e, 0x9b, 0xc6, 0x38, 0xda, 0x86, 0xdc, 0x8d, 0xd1, 0xf5, 0xec, 0x1c, 0x49, 0x35, 0x7f, 0xdd, 0x9c, 0x16, 0xae, 0x32, 0xea, 0x19,
0x5a, 0xcb, 0x48, 0xda, 0x76, 0xa0, 0xac, 0xca, 0xfb, 0xb8, 0x5c, 0x39, 0xd9, 0x31, 0xcc, 0x1b, 0x40, 0xaa, 0x5f, 0xde, 0xcc, 0xa0, 0xf8, 0xb4, 0xce, 0xaf, 0x7f, 0x7a, 0x36, 0x23, 0xb5, 0xf1,
0x13, 0xa2, 0x65, 0xe8, 0xab, 0xdf, 0x1e, 0xbe, 0x5a, 0xba, 0xf4, 0xfb, 0xab, 0xa5, 0x4b, 0x3f, 0x03, 0x28, 0xc9, 0x86, 0x98, 0x45, 0xdb, 0x48, 0xdb, 0x1c, 0x4b, 0xdb, 0x0e, 0x94, 0x55, 0xeb,
0xf7, 0x97, 0xb4, 0xc3, 0xfe, 0x92, 0xf6, 0x5b, 0x7f, 0x49, 0xfb, 0xbb, 0xbf, 0xa4, 0x3d, 0xb9, 0xca, 0xca, 0x95, 0x93, 0xdd, 0x50, 0xbf, 0x3e, 0x25, 0x5a, 0x85, 0xfe, 0x03, 0x14, 0x78, 0xcf,
0xfb, 0x7a, 0x7f, 0x72, 0xba, 0x23, 0x06, 0x4f, 0x75, 0x71, 0x9e, 0x5b, 0xff, 0x05, 0x00, 0x00, 0xc9, 0x7a, 0x85, 0xa9, 0xfe, 0xa7, 0xaf, 0x4c, 0x03, 0x95, 0xee, 0x57, 0xbf, 0x3d, 0x7c, 0xb5,
0xff, 0xff, 0x5e, 0xf6, 0xae, 0x85, 0xb9, 0x12, 0x00, 0x00, 0x74, 0xe9, 0xcf, 0x57, 0x4b, 0x97, 0x7e, 0x1d, 0x2c, 0x69, 0x87, 0x83, 0x25, 0xed, 0x8f, 0xc1,
0x92, 0xf6, 0xcf, 0x60, 0x49, 0x7b, 0x7c, 0xe7, 0xf5, 0xfe, 0x5a, 0x77, 0x5b, 0x0c, 0x9e, 0x94,
0x04, 0x5d, 0x37, 0xff, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xe9, 0xe2, 0x8e, 0x41, 0xf4, 0x13, 0x00,
0x00,
} }

View File

@ -49,6 +49,8 @@ service Tasks {
rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty); rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty);
rpc Metrics(MetricsRequest) returns (MetricsResponse); rpc Metrics(MetricsRequest) returns (MetricsResponse);
rpc Wait(WaitRequest) returns (WaitResponse);
} }
message CreateTaskRequest { message CreateTaskRequest {
@ -194,3 +196,13 @@ message MetricsRequest {
message MetricsResponse { message MetricsResponse {
repeated types.Metric metrics = 1; repeated types.Metric metrics = 1;
} }
message WaitRequest {
string container_id = 1;
string exec_id = 2;
}
message WaitResponse {
uint32 exit_status = 1;
google.protobuf.Timestamp exited_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
}

View File

@ -229,15 +229,12 @@ func (c *container) NewTask(ctx context.Context, ioCreate IOCreation, opts ...Ne
} }
if info.Checkpoint != nil { if info.Checkpoint != nil {
request.Checkpoint = info.Checkpoint request.Checkpoint = info.Checkpoint
// we need to defer the create call to start
t.deferred = request
} else {
response, err := c.client.TaskService().Create(ctx, request)
if err != nil {
return nil, errdefs.FromGRPC(err)
}
t.pid = response.Pid
} }
response, err := c.client.TaskService().Create(ctx, request)
if err != nil {
return nil, errdefs.FromGRPC(err)
}
t.pid = response.Pid
return t, nil return t, nil
} }

View File

@ -769,9 +769,16 @@ func TestShimSigkilled(t *testing.T) {
<-statusC <-statusC
for i := 0; i < 10; i++ {
if err := unix.Kill(int(pid), 0); err == unix.ESRCH {
break
}
time.Sleep(10 * time.Millisecond)
}
if err := unix.Kill(int(pid), 0); err != unix.ESRCH { if err := unix.Kill(int(pid), 0); err != unix.ESRCH {
t.Errorf("pid %d still exists", pid) t.Errorf("pid %d still exists", pid)
} }
} }
func TestDaemonRestartWithRunningShim(t *testing.T) { func TestDaemonRestartWithRunningShim(t *testing.T) {

View File

@ -95,3 +95,16 @@ func (p *Process) Start(ctx context.Context) error {
} }
return nil return nil
} }
func (p *Process) Wait(ctx context.Context) (*runtime.Exit, error) {
r, err := p.t.shim.Wait(ctx, &shim.WaitRequest{
ID: p.id,
})
if err != nil {
return nil, err
}
return &runtime.Exit{
Timestamp: r.ExitedAt,
Status: r.ExitStatus,
}, nil
}

View File

@ -245,19 +245,22 @@ func (r *Runtime) Create(ctx context.Context, id string, opts runtime.CreateOpts
if err != nil { if err != nil {
return nil, errdefs.FromGRPC(err) return nil, errdefs.FromGRPC(err)
} }
t, err := newTask(id, namespace, int(cr.Pid), s) t, err := newTask(id, namespace, int(cr.Pid), s, r.monitor)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if err := r.tasks.Add(ctx, t); err != nil { if err := r.tasks.Add(ctx, t); err != nil {
return nil, err return nil, err
} }
// after the task is created, add it to the monitor // after the task is created, add it to the monitor if it has a cgroup
if err = r.monitor.Monitor(t); err != nil { // this can be different on a checkpoint/restore
if _, err := r.Delete(ctx, t); err != nil { if t.cg != nil {
log.G(ctx).WithError(err).Error("deleting task after failed monitor") 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
} }
return nil, err
} }
return t, nil return t, nil
} }
@ -274,9 +277,17 @@ func (r *Runtime) Delete(ctx context.Context, c runtime.Task) (*runtime.Exit, er
if err := r.monitor.Stop(lc); err != nil { if err := r.monitor.Stop(lc); err != nil {
return nil, err 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) rsp, err := lc.shim.Delete(ctx, empty)
if err != nil { if err != nil {
if cerr := r.cleanupAfterDeadShim(ctx, bundle, namespace, c.ID(), lc.pid, nil); cerr != nil {
log.G(ctx).WithError(err).Error("unable to cleanup task")
}
return nil, errdefs.FromGRPC(err) return nil, errdefs.FromGRPC(err)
} }
r.tasks.Delete(ctx, lc) r.tasks.Delete(ctx, lc)
@ -284,11 +295,6 @@ func (r *Runtime) Delete(ctx context.Context, c runtime.Task) (*runtime.Exit, er
log.G(ctx).WithError(err).Error("failed to kill shim") log.G(ctx).WithError(err).Error("failed to kill shim")
} }
bundle := loadBundle(
lc.id,
filepath.Join(r.state, namespace, lc.id),
filepath.Join(r.root, namespace, lc.id),
)
if err := bundle.Delete(); err != nil { if err := bundle.Delete(); err != nil {
log.G(ctx).WithError(err).Error("failed to delete bundle") log.G(ctx).WithError(err).Error("failed to delete bundle")
} }
@ -359,7 +365,7 @@ func (r *Runtime) loadTasks(ctx context.Context, ns string) ([]*Task, error) {
continue continue
} }
t, err := newTask(id, ns, pid, s) t, err := newTask(id, ns, pid, s, r.monitor)
if err != nil { if err != nil {
log.G(ctx).WithError(err).Error("loading task type") log.G(ctx).WithError(err).Error("loading task type")
continue continue

View File

@ -24,7 +24,7 @@ import (
) )
type execProcess struct { type execProcess struct {
sync.WaitGroup wg sync.WaitGroup
processState processState
@ -41,7 +41,8 @@ type execProcess struct {
path string path string
spec specs.Process spec specs.Process
parent *initProcess parent *initProcess
waitBlock chan struct{}
} }
func newExecProcess(context context.Context, path string, r *shimapi.ExecProcessRequest, parent *initProcess, id string) (process, error) { func newExecProcess(context context.Context, path string, r *shimapi.ExecProcessRequest, parent *initProcess, id string) (process, error) {
@ -66,11 +67,16 @@ func newExecProcess(context context.Context, path string, r *shimapi.ExecProcess
stderr: r.Stderr, stderr: r.Stderr,
terminal: r.Terminal, terminal: r.Terminal,
}, },
waitBlock: make(chan struct{}),
} }
e.processState = &execCreatedState{p: e} e.processState = &execCreatedState{p: e}
return e, nil return e, nil
} }
func (e *execProcess) Wait() {
<-e.waitBlock
}
func (e *execProcess) ID() string { func (e *execProcess) ID() string {
return e.id return e.id
} }
@ -97,13 +103,18 @@ func (e *execProcess) setExited(status int) {
e.status = status e.status = status
e.exited = time.Now() e.exited = time.Now()
e.parent.platform.shutdownConsole(context.Background(), e.console) e.parent.platform.shutdownConsole(context.Background(), e.console)
e.Wait() close(e.waitBlock)
}
func (e *execProcess) delete(ctx context.Context) error {
e.wg.Wait()
if e.io != nil { if e.io != nil {
for _, c := range e.closers { for _, c := range e.closers {
c.Close() c.Close()
} }
e.io.Close() e.io.Close()
} }
return nil
} }
func (e *execProcess) resize(ws console.WinSize) error { func (e *execProcess) resize(ws console.WinSize) error {
@ -175,11 +186,11 @@ func (e *execProcess) start(ctx context.Context) (err error) {
if err != nil { if err != nil {
return errors.Wrap(err, "failed to retrieve console master") return errors.Wrap(err, "failed to retrieve console master")
} }
if e.console, err = e.parent.platform.copyConsole(ctx, console, e.stdio.stdin, e.stdio.stdout, e.stdio.stderr, &e.WaitGroup, &copyWaitGroup); err != nil { if e.console, err = e.parent.platform.copyConsole(ctx, console, e.stdio.stdin, e.stdio.stdout, e.stdio.stderr, &e.wg, &copyWaitGroup); err != nil {
return errors.Wrap(err, "failed to start console copy") return errors.Wrap(err, "failed to start console copy")
} }
} else if !e.stdio.isNull() { } else if !e.stdio.isNull() {
if err := copyPipes(ctx, e.io, e.stdio.stdin, e.stdio.stdout, e.stdio.stderr, &e.WaitGroup, &copyWaitGroup); err != nil { if err := copyPipes(ctx, e.io, e.stdio.stdin, e.stdio.stdout, e.stdio.stderr, &e.wg, &copyWaitGroup); err != nil {
return errors.Wrap(err, "failed to start io pipe copy") return errors.Wrap(err, "failed to start io pipe copy")
} }
} }

View File

@ -46,6 +46,9 @@ func (s *execCreatedState) Start(ctx context.Context) error {
func (s *execCreatedState) Delete(ctx context.Context) error { func (s *execCreatedState) Delete(ctx context.Context) error {
s.p.mu.Lock() s.p.mu.Lock()
defer s.p.mu.Unlock() defer s.p.mu.Unlock()
if err := s.p.delete(ctx); err != nil {
return err
}
return s.transition("deleted") return s.transition("deleted")
} }
@ -151,6 +154,9 @@ func (s *execStoppedState) Start(ctx context.Context) error {
func (s *execStoppedState) Delete(ctx context.Context) error { func (s *execStoppedState) Delete(ctx context.Context) error {
s.p.mu.Lock() s.p.mu.Lock()
defer s.p.mu.Unlock() defer s.p.mu.Unlock()
if err := s.p.delete(ctx); err != nil {
return err
}
return s.transition("deleted") return s.transition("deleted")
} }

View File

@ -30,7 +30,7 @@ import (
const InitPidFile = "init.pid" const InitPidFile = "init.pid"
type initProcess struct { type initProcess struct {
sync.WaitGroup wg sync.WaitGroup
initState initState
// mu is used to ensure that `Start()` and `Exited()` calls return in // mu is used to ensure that `Start()` and `Exited()` calls return in
@ -39,6 +39,8 @@ type initProcess struct {
// the reaper interface. // the reaper interface.
mu sync.Mutex mu sync.Mutex
waitBlock chan struct{}
workDir string workDir string
id string id string
@ -113,8 +115,10 @@ func (s *Service) newInitProcess(context context.Context, r *shimapi.CreateTaskR
stderr: r.Stderr, stderr: r.Stderr,
terminal: r.Terminal, terminal: r.Terminal,
}, },
rootfs: rootfs, rootfs: rootfs,
workDir: s.config.WorkDir, workDir: s.config.WorkDir,
status: 0,
waitBlock: make(chan struct{}),
} }
p.initState = &createdState{p: p} p.initState = &createdState{p: p}
var ( var (
@ -149,22 +153,24 @@ func (s *Service) newInitProcess(context context.Context, r *shimapi.CreateTaskR
Detach: true, Detach: true,
NoSubreaper: true, NoSubreaper: true,
} }
if _, err := p.runtime.Restore(context, r.ID, r.Bundle, opts); err != nil { p.initState = &createdCheckpointState{
return nil, p.runtimeError(err, "OCI runtime restore failed") p: p,
} opts: opts,
} else {
opts := &runc.CreateOpts{
PidFile: pidFile,
IO: p.io,
NoPivot: options.NoPivotRoot,
NoNewKeyring: options.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")
} }
success = true
return p, nil
}
opts := &runc.CreateOpts{
PidFile: pidFile,
IO: p.io,
NoPivot: options.NoPivotRoot,
NoNewKeyring: options.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 r.Stdin != "" { if r.Stdin != "" {
sc, err := fifo.OpenFifo(context, r.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0) sc, err := fifo.OpenFifo(context, r.Stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
@ -180,13 +186,13 @@ func (s *Service) newInitProcess(context context.Context, r *shimapi.CreateTaskR
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to retrieve console master") return nil, errors.Wrap(err, "failed to retrieve console master")
} }
console, err = s.platform.copyConsole(context, console, r.Stdin, r.Stdout, r.Stderr, &p.WaitGroup, &copyWaitGroup) console, err = s.platform.copyConsole(context, console, r.Stdin, r.Stdout, r.Stderr, &p.wg, &copyWaitGroup)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "failed to start console copy") return nil, errors.Wrap(err, "failed to start console copy")
} }
p.console = console p.console = console
} else if !hasNoIO(r) { } else if !hasNoIO(r) {
if err := copyPipes(context, p.io, r.Stdin, r.Stdout, r.Stderr, &p.WaitGroup, &copyWaitGroup); err != nil { 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") return nil, errors.Wrap(err, "failed to start io pipe copy")
} }
} }
@ -201,6 +207,10 @@ func (s *Service) newInitProcess(context context.Context, r *shimapi.CreateTaskR
return p, nil return p, nil
} }
func (p *initProcess) Wait() {
<-p.waitBlock
}
func (p *initProcess) ID() string { func (p *initProcess) ID() string {
return p.id return p.id
} }
@ -240,14 +250,15 @@ func (p *initProcess) start(context context.Context) error {
} }
func (p *initProcess) setExited(status int) { func (p *initProcess) setExited(status int) {
p.status = status
p.exited = time.Now() p.exited = time.Now()
p.status = status
p.platform.shutdownConsole(context.Background(), p.console) p.platform.shutdownConsole(context.Background(), p.console)
close(p.waitBlock)
} }
func (p *initProcess) delete(context context.Context) error { func (p *initProcess) delete(context context.Context) error {
p.killAll(context) p.killAll(context)
p.Wait() p.wg.Wait()
err := p.runtime.Delete(context, p.id, nil) err := p.runtime.Delete(context, p.id, nil)
// ignore errors if a runtime has already deleted the process // ignore errors if a runtime has already deleted the process
// but we still hold metadata and pipes // but we still hold metadata and pipes

View File

@ -4,10 +4,14 @@ package shim
import ( import (
"context" "context"
"sync"
"syscall"
"github.com/containerd/console" "github.com/containerd/console"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
shimapi "github.com/containerd/containerd/linux/shim/v1" shimapi "github.com/containerd/containerd/linux/shim/v1"
"github.com/containerd/fifo"
runc "github.com/containerd/go-runc"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -109,6 +113,120 @@ func (s *createdState) SetExited(status int) {
} }
} }
type createdCheckpointState struct {
p *initProcess
opts *runc.RestoreOpts
}
func (s *createdCheckpointState) transition(name string) error {
switch name {
case "running":
s.p.initState = &runningState{p: s.p}
case "stopped":
s.p.initState = &stoppedState{p: s.p}
case "deleted":
s.p.initState = &deletedState{}
default:
return errors.Errorf("invalid state transition %q to %q", stateName(s), name)
}
return nil
}
func (s *createdCheckpointState) Pause(ctx context.Context) error {
s.p.mu.Lock()
defer s.p.mu.Unlock()
return errors.Errorf("cannot pause task in created state")
}
func (s *createdCheckpointState) Resume(ctx context.Context) error {
s.p.mu.Lock()
defer s.p.mu.Unlock()
return errors.Errorf("cannot resume task in created state")
}
func (s *createdCheckpointState) Update(context context.Context, r *shimapi.UpdateTaskRequest) error {
s.p.mu.Lock()
defer s.p.mu.Unlock()
return s.p.update(context, r)
}
func (s *createdCheckpointState) Checkpoint(context context.Context, r *shimapi.CheckpointTaskRequest) error {
s.p.mu.Lock()
defer s.p.mu.Unlock()
return errors.Errorf("cannot checkpoint a task in created state")
}
func (s *createdCheckpointState) Resize(ws console.WinSize) error {
s.p.mu.Lock()
defer s.p.mu.Unlock()
return s.p.resize(ws)
}
func (s *createdCheckpointState) Start(ctx context.Context) error {
s.p.mu.Lock()
defer s.p.mu.Unlock()
p := s.p
if _, err := s.p.runtime.Restore(ctx, p.id, p.bundle, s.opts); err != nil {
return p.runtimeError(err, "OCI runtime restore failed")
}
sio := p.stdio
if sio.stdin != "" {
sc, err := fifo.OpenFifo(ctx, sio.stdin, syscall.O_WRONLY|syscall.O_NONBLOCK, 0)
if err != nil {
return errors.Wrapf(err, "failed to open stdin fifo %s", sio.stdin)
}
p.stdin = sc
p.closers = append(p.closers, sc)
}
var copyWaitGroup sync.WaitGroup
if !sio.isNull() {
if err := copyPipes(ctx, p.io, sio.stdin, sio.stdout, sio.stderr, &p.wg, &copyWaitGroup); err != nil {
return errors.Wrap(err, "failed to start io pipe copy")
}
}
copyWaitGroup.Wait()
pid, err := runc.ReadPidFile(s.opts.PidFile)
if err != nil {
return errors.Wrap(err, "failed to retrieve OCI runtime container pid")
}
p.pid = pid
return s.transition("running")
}
func (s *createdCheckpointState) Delete(ctx context.Context) error {
s.p.mu.Lock()
defer s.p.mu.Unlock()
if err := s.p.delete(ctx); err != nil {
return err
}
return s.transition("deleted")
}
func (s *createdCheckpointState) Kill(ctx context.Context, sig uint32, all bool) error {
s.p.mu.Lock()
defer s.p.mu.Unlock()
return s.p.kill(ctx, sig, all)
}
func (s *createdCheckpointState) SetExited(status int) {
s.p.mu.Lock()
defer s.p.mu.Unlock()
s.p.setExited(status)
if err := s.transition("stopped"); err != nil {
panic(err)
}
}
type runningState struct { type runningState struct {
p *initProcess p *initProcess
} }
@ -278,6 +396,7 @@ func (s *pausedState) SetExited(status int) {
if err := s.transition("stopped"); err != nil { if err := s.transition("stopped"); err != nil {
panic(err) panic(err)
} }
} }
type stoppedState struct { type stoppedState struct {

View File

@ -86,3 +86,7 @@ func (c *local) ShimInfo(ctx context.Context, in *google_protobuf.Empty, opts ..
func (c *local) Update(ctx context.Context, in *shimapi.UpdateTaskRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) { func (c *local) Update(ctx context.Context, in *shimapi.UpdateTaskRequest, opts ...grpc.CallOption) (*google_protobuf.Empty, error) {
return c.s.Update(ctx, in) return c.s.Update(ctx, in)
} }
func (c *local) Wait(ctx context.Context, in *shimapi.WaitRequest, opts ...grpc.CallOption) (*shimapi.WaitResponse, error) {
return c.s.Wait(ctx, in)
}

View File

@ -38,7 +38,9 @@ type process interface {
// Stdio returns io information for the container // Stdio returns io information for the container
Stdio() stdio Stdio() stdio
// Status returns the process status // Status returns the process status
Status(ctx context.Context) (string, error) Status(context.Context) (string, error)
// Wait blocks until the process has exited
Wait()
} }
type processState interface { type processState interface {
@ -58,7 +60,7 @@ func stateName(v interface{}) string {
switch v.(type) { switch v.(type) {
case *runningState, *execRunningState: case *runningState, *execRunningState:
return "running" return "running"
case *createdState, *execCreatedState: case *createdState, *execCreatedState, *createdCheckpointState:
return "created" return "created"
case *pausedState: case *pausedState:
return "paused" return "paused"

View File

@ -46,7 +46,7 @@ func NewService(config Config, publisher events.Publisher) (*Service, error) {
config: config, config: config,
context: context, context: context,
processes: make(map[string]process), processes: make(map[string]process),
events: make(chan interface{}, 4096), events: make(chan interface{}, 128),
ec: reaper.Default.Subscribe(), ec: reaper.Default.Subscribe(),
} }
go s.processExits() go s.processExits()
@ -111,7 +111,9 @@ func (s *Service) Create(ctx context.Context, r *shimapi.CreateTaskRequest) (*sh
} }
func (s *Service) Start(ctx context.Context, r *shimapi.StartRequest) (*shimapi.StartResponse, error) { func (s *Service) Start(ctx context.Context, r *shimapi.StartRequest) (*shimapi.StartResponse, error) {
p := s.getProcess(r.ID) s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[r.ID]
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process %s not found", r.ID) return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process %s not found", r.ID)
} }
@ -138,7 +140,9 @@ func (s *Service) Start(ctx context.Context, r *shimapi.StartRequest) (*shimapi.
} }
func (s *Service) Delete(ctx context.Context, r *google_protobuf.Empty) (*shimapi.DeleteResponse, error) { func (s *Service) Delete(ctx context.Context, r *google_protobuf.Empty) (*shimapi.DeleteResponse, error) {
p := s.getProcess(s.id) s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[s.id]
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
} }
@ -146,7 +150,7 @@ func (s *Service) Delete(ctx context.Context, r *google_protobuf.Empty) (*shimap
if err := p.Delete(ctx); err != nil { if err := p.Delete(ctx); err != nil {
return nil, err return nil, err
} }
s.deleteProcess(p.ID()) delete(s.processes, s.id)
s.platform.close() s.platform.close()
s.events <- &eventsapi.TaskDelete{ s.events <- &eventsapi.TaskDelete{
ContainerID: s.id, ContainerID: s.id,
@ -162,17 +166,19 @@ func (s *Service) Delete(ctx context.Context, r *google_protobuf.Empty) (*shimap
} }
func (s *Service) DeleteProcess(ctx context.Context, r *shimapi.DeleteProcessRequest) (*shimapi.DeleteResponse, error) { func (s *Service) DeleteProcess(ctx context.Context, r *shimapi.DeleteProcessRequest) (*shimapi.DeleteResponse, error) {
s.mu.Lock()
defer s.mu.Unlock()
if r.ID == s.id { if r.ID == s.id {
return nil, grpc.Errorf(codes.InvalidArgument, "cannot delete init process with DeleteProcess") return nil, grpc.Errorf(codes.InvalidArgument, "cannot delete init process with DeleteProcess")
} }
p := s.getProcess(r.ID) p := s.processes[r.ID]
if p == nil { if p == nil {
return nil, errors.Wrapf(errdefs.ErrNotFound, "process %s", r.ID) return nil, errors.Wrapf(errdefs.ErrNotFound, "process %s", r.ID)
} }
if err := p.Delete(ctx); err != nil { if err := p.Delete(ctx); err != nil {
return nil, err return nil, err
} }
s.deleteProcess(r.ID) delete(s.processes, r.ID)
return &shimapi.DeleteResponse{ return &shimapi.DeleteResponse{
ExitStatus: uint32(p.ExitStatus()), ExitStatus: uint32(p.ExitStatus()),
ExitedAt: p.ExitedAt(), ExitedAt: p.ExitedAt(),
@ -207,6 +213,8 @@ func (s *Service) Exec(ctx context.Context, r *shimapi.ExecProcessRequest) (*goo
} }
func (s *Service) ResizePty(ctx context.Context, r *shimapi.ResizePtyRequest) (*google_protobuf.Empty, error) { func (s *Service) ResizePty(ctx context.Context, r *shimapi.ResizePtyRequest) (*google_protobuf.Empty, error) {
s.mu.Lock()
defer s.mu.Unlock()
if r.ID == "" { if r.ID == "" {
return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "id not provided") return nil, errdefs.ToGRPCf(errdefs.ErrInvalidArgument, "id not provided")
} }
@ -214,7 +222,7 @@ func (s *Service) ResizePty(ctx context.Context, r *shimapi.ResizePtyRequest) (*
Width: uint16(r.Width), Width: uint16(r.Width),
Height: uint16(r.Height), Height: uint16(r.Height),
} }
p := s.getProcess(r.ID) p := s.processes[r.ID]
if p == nil { if p == nil {
return nil, errors.Errorf("process does not exist %s", r.ID) return nil, errors.Errorf("process does not exist %s", r.ID)
} }
@ -225,7 +233,9 @@ func (s *Service) ResizePty(ctx context.Context, r *shimapi.ResizePtyRequest) (*
} }
func (s *Service) State(ctx context.Context, r *shimapi.StateRequest) (*shimapi.StateResponse, error) { func (s *Service) State(ctx context.Context, r *shimapi.StateRequest) (*shimapi.StateResponse, error) {
p := s.getProcess(r.ID) s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[r.ID]
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process id %s not found", r.ID) return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process id %s not found", r.ID)
} }
@ -262,7 +272,9 @@ func (s *Service) State(ctx context.Context, r *shimapi.StateRequest) (*shimapi.
} }
func (s *Service) Pause(ctx context.Context, r *google_protobuf.Empty) (*google_protobuf.Empty, error) { func (s *Service) Pause(ctx context.Context, r *google_protobuf.Empty) (*google_protobuf.Empty, error) {
p := s.getProcess(s.id) s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[s.id]
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
} }
@ -276,7 +288,9 @@ func (s *Service) Pause(ctx context.Context, r *google_protobuf.Empty) (*google_
} }
func (s *Service) Resume(ctx context.Context, r *google_protobuf.Empty) (*google_protobuf.Empty, error) { func (s *Service) Resume(ctx context.Context, r *google_protobuf.Empty) (*google_protobuf.Empty, error) {
p := s.getProcess(s.id) s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[s.id]
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
} }
@ -290,8 +304,10 @@ func (s *Service) Resume(ctx context.Context, r *google_protobuf.Empty) (*google
} }
func (s *Service) Kill(ctx context.Context, r *shimapi.KillRequest) (*google_protobuf.Empty, error) { func (s *Service) Kill(ctx context.Context, r *shimapi.KillRequest) (*google_protobuf.Empty, error) {
s.mu.Lock()
defer s.mu.Unlock()
if r.ID == "" { if r.ID == "" {
p := s.getProcess(s.id) p := s.processes[s.id]
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
} }
@ -301,7 +317,7 @@ func (s *Service) Kill(ctx context.Context, r *shimapi.KillRequest) (*google_pro
return empty, nil return empty, nil
} }
p := s.getProcess(r.ID) p := s.processes[r.ID]
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process id %s not found", r.ID) return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process id %s not found", r.ID)
} }
@ -322,7 +338,9 @@ func (s *Service) ListPids(ctx context.Context, r *shimapi.ListPidsRequest) (*sh
} }
func (s *Service) CloseIO(ctx context.Context, r *shimapi.CloseIORequest) (*google_protobuf.Empty, error) { func (s *Service) CloseIO(ctx context.Context, r *shimapi.CloseIORequest) (*google_protobuf.Empty, error) {
p := s.getProcess(r.ID) s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[r.ID]
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process does not exist %s", r.ID) return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "process does not exist %s", r.ID)
} }
@ -335,7 +353,9 @@ func (s *Service) CloseIO(ctx context.Context, r *shimapi.CloseIORequest) (*goog
} }
func (s *Service) Checkpoint(ctx context.Context, r *shimapi.CheckpointTaskRequest) (*google_protobuf.Empty, error) { func (s *Service) Checkpoint(ctx context.Context, r *shimapi.CheckpointTaskRequest) (*google_protobuf.Empty, error) {
p := s.getProcess(s.id) s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[s.id]
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
} }
@ -355,7 +375,9 @@ func (s *Service) ShimInfo(ctx context.Context, r *google_protobuf.Empty) (*shim
} }
func (s *Service) Update(ctx context.Context, r *shimapi.UpdateTaskRequest) (*google_protobuf.Empty, error) { func (s *Service) Update(ctx context.Context, r *shimapi.UpdateTaskRequest) (*google_protobuf.Empty, error) {
p := s.getProcess(s.id) s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[s.id]
if p == nil { if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created") return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
} }
@ -365,23 +387,19 @@ func (s *Service) Update(ctx context.Context, r *shimapi.UpdateTaskRequest) (*go
return empty, nil return empty, nil
} }
func (s *Service) addProcess(id string, p process) { func (s *Service) Wait(ctx context.Context, r *shimapi.WaitRequest) (*shimapi.WaitResponse, error) {
s.mu.Lock() s.mu.Lock()
s.processes[id] = p p := s.processes[r.ID]
s.mu.Unlock() s.mu.Unlock()
} if p == nil {
return nil, errdefs.ToGRPCf(errdefs.ErrFailedPrecondition, "container must be created")
}
p.Wait()
func (s *Service) getProcess(id string) process { return &shimapi.WaitResponse{
s.mu.Lock() ExitStatus: uint32(p.ExitStatus()),
p := s.processes[id] ExitedAt: p.ExitedAt(),
s.mu.Unlock() }, nil
return p
}
func (s *Service) deleteProcess(id string) {
s.mu.Lock()
delete(s.processes, id)
s.mu.Unlock()
} }
func (s *Service) processExits() { func (s *Service) processExits() {
@ -402,7 +420,6 @@ func (s *Service) checkProcesses(e runc.Exit) {
Error("failed to kill init's children") Error("failed to kill init's children")
} }
} }
p.SetExited(e.Status) p.SetExited(e.Status)
s.events <- &eventsapi.TaskExit{ s.events <- &eventsapi.TaskExit{
ContainerID: s.id, ContainerID: s.id,
@ -417,7 +434,9 @@ func (s *Service) checkProcesses(e runc.Exit) {
} }
func (s *Service) getContainerPids(ctx context.Context, id string) ([]uint32, error) { func (s *Service) getContainerPids(ctx context.Context, id string) ([]uint32, error) {
p := s.getProcess(s.id) s.mu.Lock()
defer s.mu.Unlock()
p := s.processes[s.id]
if p == nil { if p == nil {
return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "container must be created") return nil, errors.Wrapf(errdefs.ErrFailedPrecondition, "container must be created")
} }
@ -436,7 +455,7 @@ func (s *Service) getContainerPids(ctx context.Context, id string) ([]uint32, er
func (s *Service) forward(publisher events.Publisher) { func (s *Service) forward(publisher events.Publisher) {
for e := range s.events { for e := range s.events {
if err := publisher.Publish(s.context, getTopic(s.context, e), e); err != nil { if err := publisher.Publish(s.context, getTopic(s.context, e), e); err != nil {
log.G(s.context).WithError(err).Error("post event") logrus.WithError(err).Error("post event")
} }
} }
} }
@ -464,7 +483,7 @@ func getTopic(ctx context.Context, e interface{}) string {
case *eventsapi.TaskCheckpointed: case *eventsapi.TaskCheckpointed:
return runtime.TaskCheckpointedEventTopic return runtime.TaskCheckpointedEventTopic
default: default:
log.G(ctx).Warnf("no topic for type %#v", e) logrus.Warnf("no topic for type %#v", e)
} }
return runtime.TaskUnknownTopic return runtime.TaskUnknownTopic
} }

View File

@ -27,6 +27,8 @@
UpdateTaskRequest UpdateTaskRequest
StartRequest StartRequest
StartResponse StartResponse
WaitRequest
WaitResponse
*/ */
package shim package shim
@ -242,6 +244,23 @@ func (m *StartResponse) Reset() { *m = StartResponse{} }
func (*StartResponse) ProtoMessage() {} func (*StartResponse) ProtoMessage() {}
func (*StartResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{17} } func (*StartResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{17} }
type WaitRequest struct {
ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
}
func (m *WaitRequest) Reset() { *m = WaitRequest{} }
func (*WaitRequest) ProtoMessage() {}
func (*WaitRequest) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{18} }
type WaitResponse struct {
ExitStatus uint32 `protobuf:"varint,1,opt,name=exit_status,json=exitStatus,proto3" json:"exit_status,omitempty"`
ExitedAt time.Time `protobuf:"bytes,2,opt,name=exited_at,json=exitedAt,stdtime" json:"exited_at"`
}
func (m *WaitResponse) Reset() { *m = WaitResponse{} }
func (*WaitResponse) ProtoMessage() {}
func (*WaitResponse) Descriptor() ([]byte, []int) { return fileDescriptorShim, []int{19} }
func init() { func init() {
proto.RegisterType((*CreateTaskRequest)(nil), "containerd.runtime.linux.shim.v1.CreateTaskRequest") proto.RegisterType((*CreateTaskRequest)(nil), "containerd.runtime.linux.shim.v1.CreateTaskRequest")
proto.RegisterType((*CreateTaskResponse)(nil), "containerd.runtime.linux.shim.v1.CreateTaskResponse") proto.RegisterType((*CreateTaskResponse)(nil), "containerd.runtime.linux.shim.v1.CreateTaskResponse")
@ -261,6 +280,8 @@ func init() {
proto.RegisterType((*UpdateTaskRequest)(nil), "containerd.runtime.linux.shim.v1.UpdateTaskRequest") proto.RegisterType((*UpdateTaskRequest)(nil), "containerd.runtime.linux.shim.v1.UpdateTaskRequest")
proto.RegisterType((*StartRequest)(nil), "containerd.runtime.linux.shim.v1.StartRequest") proto.RegisterType((*StartRequest)(nil), "containerd.runtime.linux.shim.v1.StartRequest")
proto.RegisterType((*StartResponse)(nil), "containerd.runtime.linux.shim.v1.StartResponse") proto.RegisterType((*StartResponse)(nil), "containerd.runtime.linux.shim.v1.StartResponse")
proto.RegisterType((*WaitRequest)(nil), "containerd.runtime.linux.shim.v1.WaitRequest")
proto.RegisterType((*WaitResponse)(nil), "containerd.runtime.linux.shim.v1.WaitResponse")
} }
// Reference imports to suppress errors if they are not otherwise used. // Reference imports to suppress errors if they are not otherwise used.
@ -291,6 +312,7 @@ type ShimClient interface {
// ShimInfo returns information about the shim. // ShimInfo returns information about the shim.
ShimInfo(ctx context.Context, in *google_protobuf1.Empty, opts ...grpc.CallOption) (*ShimInfoResponse, error) ShimInfo(ctx context.Context, in *google_protobuf1.Empty, opts ...grpc.CallOption) (*ShimInfoResponse, error)
Update(ctx context.Context, in *UpdateTaskRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error) Update(ctx context.Context, in *UpdateTaskRequest, opts ...grpc.CallOption) (*google_protobuf1.Empty, error)
Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*WaitResponse, error)
} }
type shimClient struct { type shimClient struct {
@ -436,6 +458,15 @@ func (c *shimClient) Update(ctx context.Context, in *UpdateTaskRequest, opts ...
return out, nil return out, nil
} }
func (c *shimClient) Wait(ctx context.Context, in *WaitRequest, opts ...grpc.CallOption) (*WaitResponse, error) {
out := new(WaitResponse)
err := grpc.Invoke(ctx, "/containerd.runtime.linux.shim.v1.Shim/Wait", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Shim service // Server API for Shim service
type ShimServer interface { type ShimServer interface {
@ -456,6 +487,7 @@ type ShimServer interface {
// ShimInfo returns information about the shim. // ShimInfo returns information about the shim.
ShimInfo(context.Context, *google_protobuf1.Empty) (*ShimInfoResponse, error) ShimInfo(context.Context, *google_protobuf1.Empty) (*ShimInfoResponse, error)
Update(context.Context, *UpdateTaskRequest) (*google_protobuf1.Empty, error) Update(context.Context, *UpdateTaskRequest) (*google_protobuf1.Empty, error)
Wait(context.Context, *WaitRequest) (*WaitResponse, error)
} }
func RegisterShimServer(s *grpc.Server, srv ShimServer) { func RegisterShimServer(s *grpc.Server, srv ShimServer) {
@ -732,6 +764,24 @@ func _Shim_Update_Handler(srv interface{}, ctx context.Context, dec func(interfa
return interceptor(ctx, in, info, handler) return interceptor(ctx, in, info, handler)
} }
func _Shim_Wait_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(WaitRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ShimServer).Wait(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/containerd.runtime.linux.shim.v1.Shim/Wait",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ShimServer).Wait(ctx, req.(*WaitRequest))
}
return interceptor(ctx, in, info, handler)
}
var _Shim_serviceDesc = grpc.ServiceDesc{ var _Shim_serviceDesc = grpc.ServiceDesc{
ServiceName: "containerd.runtime.linux.shim.v1.Shim", ServiceName: "containerd.runtime.linux.shim.v1.Shim",
HandlerType: (*ShimServer)(nil), HandlerType: (*ShimServer)(nil),
@ -796,6 +846,10 @@ var _Shim_serviceDesc = grpc.ServiceDesc{
MethodName: "Update", MethodName: "Update",
Handler: _Shim_Update_Handler, Handler: _Shim_Update_Handler,
}, },
{
MethodName: "Wait",
Handler: _Shim_Wait_Handler,
},
}, },
Streams: []grpc.StreamDesc{}, Streams: []grpc.StreamDesc{},
Metadata: "github.com/containerd/containerd/linux/shim/v1/shim.proto", Metadata: "github.com/containerd/containerd/linux/shim/v1/shim.proto",
@ -1471,6 +1525,61 @@ func (m *StartResponse) MarshalTo(dAtA []byte) (int, error) {
return i, nil return i, nil
} }
func (m *WaitRequest) 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 *WaitRequest) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if len(m.ID) > 0 {
dAtA[i] = 0xa
i++
i = encodeVarintShim(dAtA, i, uint64(len(m.ID)))
i += copy(dAtA[i:], m.ID)
}
return i, nil
}
func (m *WaitResponse) 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 *WaitResponse) MarshalTo(dAtA []byte) (int, error) {
var i int
_ = i
var l int
_ = l
if m.ExitStatus != 0 {
dAtA[i] = 0x8
i++
i = encodeVarintShim(dAtA, i, uint64(m.ExitStatus))
}
dAtA[i] = 0x12
i++
i = encodeVarintShim(dAtA, i, uint64(github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt)))
n9, err := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.ExitedAt, dAtA[i:])
if err != nil {
return 0, err
}
i += n9
return i, nil
}
func encodeFixed64Shim(dAtA []byte, offset int, v uint64) int { func encodeFixed64Shim(dAtA []byte, offset int, v uint64) int {
dAtA[offset] = uint8(v) dAtA[offset] = uint8(v)
dAtA[offset+1] = uint8(v >> 8) dAtA[offset+1] = uint8(v >> 8)
@ -1791,6 +1900,27 @@ func (m *StartResponse) Size() (n int) {
return n return n
} }
func (m *WaitRequest) Size() (n int) {
var l int
_ = l
l = len(m.ID)
if l > 0 {
n += 1 + l + sovShim(uint64(l))
}
return n
}
func (m *WaitResponse) Size() (n int) {
var l int
_ = l
if m.ExitStatus != 0 {
n += 1 + sovShim(uint64(m.ExitStatus))
}
l = github_com_gogo_protobuf_types.SizeOfStdTime(m.ExitedAt)
n += 1 + l + sovShim(uint64(l))
return n
}
func sovShim(x uint64) (n int) { func sovShim(x uint64) (n int) {
for { for {
n++ n++
@ -2016,6 +2146,27 @@ func (this *StartResponse) String() string {
}, "") }, "")
return s return s
} }
func (this *WaitRequest) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&WaitRequest{`,
`ID:` + fmt.Sprintf("%v", this.ID) + `,`,
`}`,
}, "")
return s
}
func (this *WaitResponse) String() string {
if this == nil {
return "nil"
}
s := strings.Join([]string{`&WaitResponse{`,
`ExitStatus:` + fmt.Sprintf("%v", this.ExitStatus) + `,`,
`ExitedAt:` + strings.Replace(strings.Replace(this.ExitedAt.String(), "Timestamp", "google_protobuf3.Timestamp", 1), `&`, ``, 1) + `,`,
`}`,
}, "")
return s
}
func valueToStringShim(v interface{}) string { func valueToStringShim(v interface{}) string {
rv := reflect.ValueOf(v) rv := reflect.ValueOf(v)
if rv.IsNil() { if rv.IsNil() {
@ -4272,6 +4423,184 @@ func (m *StartResponse) Unmarshal(dAtA []byte) error {
} }
return nil return nil
} }
func (m *WaitRequest) 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 ErrIntOverflowShim
}
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: WaitRequest: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: WaitRequest: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ID", wireType)
}
var stringLen uint64
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowShim
}
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 ErrInvalidLengthShim
}
postIndex := iNdEx + intStringLen
if postIndex > l {
return io.ErrUnexpectedEOF
}
m.ID = string(dAtA[iNdEx:postIndex])
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipShim(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthShim
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func (m *WaitResponse) 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 ErrIntOverflowShim
}
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: WaitResponse: wiretype end group for non-group")
}
if fieldNum <= 0 {
return fmt.Errorf("proto: WaitResponse: illegal tag %d (wire type %d)", fieldNum, wire)
}
switch fieldNum {
case 1:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field ExitStatus", wireType)
}
m.ExitStatus = 0
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowShim
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
m.ExitStatus |= (uint32(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
case 2:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field ExitedAt", wireType)
}
var msglen int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowShim
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := dAtA[iNdEx]
iNdEx++
msglen |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
if msglen < 0 {
return ErrInvalidLengthShim
}
postIndex := iNdEx + msglen
if postIndex > l {
return io.ErrUnexpectedEOF
}
if err := github_com_gogo_protobuf_types.StdTimeUnmarshal(&m.ExitedAt, dAtA[iNdEx:postIndex]); err != nil {
return err
}
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipShim(dAtA[iNdEx:])
if err != nil {
return err
}
if skippy < 0 {
return ErrInvalidLengthShim
}
if (iNdEx + skippy) > l {
return io.ErrUnexpectedEOF
}
iNdEx += skippy
}
}
if iNdEx > l {
return io.ErrUnexpectedEOF
}
return nil
}
func skipShim(dAtA []byte) (n int, err error) { func skipShim(dAtA []byte) (n int, err error) {
l := len(dAtA) l := len(dAtA)
iNdEx := 0 iNdEx := 0
@ -4382,73 +4711,76 @@ func init() {
} }
var fileDescriptorShim = []byte{ var fileDescriptorShim = []byte{
// 1083 bytes of a gzipped FileDescriptorProto // 1123 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0x4f, 0x6f, 0x1b, 0x45, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x57, 0xcd, 0x6e, 0xdb, 0x46,
0x14, 0xef, 0xfa, 0xbf, 0x9f, 0x71, 0x48, 0x86, 0x34, 0x6c, 0x5d, 0xc9, 0xb1, 0xf6, 0x10, 0x19, 0x10, 0x36, 0xf5, 0xaf, 0x51, 0xe4, 0xda, 0x5b, 0xc7, 0x65, 0x14, 0x40, 0x16, 0x08, 0xd4, 0x50,
0xa1, 0xae, 0x89, 0x83, 0x5a, 0x0a, 0x12, 0x52, 0x92, 0x56, 0x28, 0x82, 0xa8, 0xd1, 0x26, 0x05, 0x51, 0x84, 0xaa, 0xe5, 0x22, 0x69, 0x5a, 0xa0, 0x80, 0xed, 0x04, 0x85, 0xd1, 0x1a, 0x31, 0x68,
0x04, 0x42, 0xd1, 0xc6, 0x3b, 0xb1, 0x47, 0xb1, 0x77, 0xb6, 0x3b, 0xb3, 0x21, 0xe1, 0xc4, 0x89, 0xa7, 0x29, 0x5a, 0x14, 0x06, 0x2d, 0xae, 0xa5, 0x85, 0x29, 0x92, 0xe1, 0x2e, 0x5d, 0xbb, 0xa7,
0x33, 0x1f, 0x87, 0x8f, 0x90, 0x03, 0x07, 0x8e, 0x9c, 0x0a, 0x8d, 0xc4, 0x91, 0xef, 0x80, 0xe6, 0x9e, 0x7a, 0xee, 0xe3, 0xf4, 0x11, 0x7c, 0xc8, 0xa1, 0xc7, 0x9e, 0xd2, 0xc6, 0xf7, 0xbe, 0x43,
0x8f, 0xed, 0xb5, 0x9d, 0x65, 0xd7, 0xbd, 0xc4, 0xf3, 0x66, 0x7e, 0xef, 0xcd, 0x9b, 0xf7, 0x7b, 0xb1, 0x3f, 0x92, 0x28, 0xc9, 0x0c, 0x29, 0x5f, 0xac, 0x9d, 0xe5, 0x37, 0xbb, 0xb3, 0xf3, 0x7d,
0x7f, 0x36, 0xf0, 0xb4, 0x4f, 0xf8, 0x20, 0x3a, 0xb3, 0x7b, 0x74, 0xd4, 0xe9, 0x51, 0x9f, 0xbb, 0x3b, 0xb3, 0x86, 0xa7, 0x7d, 0xc2, 0x06, 0xd1, 0xa9, 0xd9, 0xf3, 0x87, 0x9d, 0x9e, 0xef, 0x31,
0xc4, 0xc7, 0xa1, 0x17, 0x5f, 0x0e, 0x89, 0x1f, 0x5d, 0x75, 0xd8, 0x80, 0x8c, 0x3a, 0x97, 0xdb, 0x9b, 0x78, 0x38, 0x74, 0xe2, 0x43, 0x97, 0x78, 0xd1, 0x65, 0x87, 0x0e, 0xc8, 0xb0, 0x73, 0xb1,
0xf2, 0xd7, 0x0e, 0x42, 0xca, 0x29, 0x6a, 0x4d, 0x41, 0x76, 0x18, 0xf9, 0x9c, 0x8c, 0xb0, 0x2d, 0x25, 0x7e, 0xcd, 0x20, 0xf4, 0x99, 0x8f, 0x5a, 0x13, 0x90, 0x19, 0x46, 0x1e, 0x23, 0x43, 0x6c,
0xc1, 0xb6, 0x04, 0x5d, 0x6e, 0x37, 0x1e, 0xf4, 0x29, 0xed, 0x0f, 0x71, 0x47, 0xe2, 0xcf, 0xa2, 0x0a, 0xb0, 0x29, 0x40, 0x17, 0x5b, 0x8d, 0x07, 0x7d, 0xdf, 0xef, 0xbb, 0xb8, 0x23, 0xf0, 0xa7,
0xf3, 0x8e, 0xeb, 0x5f, 0x2b, 0xe5, 0xc6, 0xc3, 0xf9, 0x23, 0x3c, 0x0a, 0xf8, 0xf8, 0x70, 0xbd, 0xd1, 0x59, 0xc7, 0xf6, 0xae, 0xa4, 0x73, 0xe3, 0xe1, 0xec, 0x27, 0x3c, 0x0c, 0xd8, 0xe8, 0xe3,
0x4f, 0xfb, 0x54, 0x2e, 0x3b, 0x62, 0xa5, 0x77, 0x37, 0xe7, 0x55, 0xc4, 0x8d, 0x8c, 0xbb, 0xa3, 0x5a, 0xdf, 0xef, 0xfb, 0x62, 0xd8, 0xe1, 0x23, 0x35, 0xbb, 0x31, 0xeb, 0xc2, 0x77, 0xa4, 0xcc,
0x40, 0x03, 0x1e, 0xa7, 0xbe, 0xc5, 0x0d, 0x48, 0x87, 0x5f, 0x07, 0x98, 0x75, 0x46, 0x34, 0xf2, 0x1e, 0x06, 0x0a, 0xf0, 0x38, 0xf5, 0x2c, 0x76, 0x40, 0x3a, 0xec, 0x2a, 0xc0, 0xb4, 0x33, 0xf4,
0xb9, 0xd6, 0xfb, 0x74, 0x09, 0x3d, 0xee, 0xb2, 0x0b, 0xf9, 0x47, 0xe9, 0x5a, 0xff, 0xe6, 0x60, 0x23, 0x8f, 0x29, 0xbf, 0x2f, 0x17, 0xf0, 0x63, 0x36, 0x3d, 0x17, 0x7f, 0xa4, 0xaf, 0xf1, 0x5f,
0x6d, 0x3f, 0xc4, 0x2e, 0xc7, 0x27, 0x2e, 0xbb, 0x70, 0xf0, 0xab, 0x08, 0x33, 0x8e, 0x36, 0x20, 0x0e, 0x56, 0xf7, 0x42, 0x6c, 0x33, 0x7c, 0x6c, 0xd3, 0x73, 0x0b, 0xbf, 0x8e, 0x30, 0x65, 0x68,
0x47, 0x3c, 0xd3, 0x68, 0x19, 0xed, 0xea, 0x5e, 0xe9, 0xf6, 0xf5, 0x66, 0xee, 0xe0, 0x99, 0x93, 0x1d, 0x72, 0xc4, 0xd1, 0xb5, 0x96, 0xd6, 0xae, 0xee, 0x96, 0x6e, 0xde, 0x6e, 0xe4, 0xf6, 0x9f,
0x23, 0x1e, 0xda, 0x80, 0xd2, 0x59, 0xe4, 0x7b, 0x43, 0x6c, 0xe6, 0xc4, 0x99, 0xa3, 0x25, 0x64, 0x59, 0x39, 0xe2, 0xa0, 0x75, 0x28, 0x9d, 0x46, 0x9e, 0xe3, 0x62, 0x3d, 0xc7, 0xbf, 0x59, 0xca,
0x42, 0x59, 0x47, 0xd0, 0xcc, 0xcb, 0x83, 0xb1, 0x88, 0x3a, 0x50, 0x0a, 0x29, 0xe5, 0xe7, 0xcc, 0x42, 0x3a, 0x94, 0x55, 0x06, 0xf5, 0xbc, 0xf8, 0x30, 0x32, 0x51, 0x07, 0x4a, 0xa1, 0xef, 0xb3,
0x2c, 0xb4, 0xf2, 0xed, 0x5a, 0xf7, 0x7d, 0x3b, 0x16, 0x75, 0xe9, 0x92, 0x7d, 0x28, 0x9e, 0xe2, 0x33, 0xaa, 0x17, 0x5a, 0xf9, 0x76, 0xad, 0xfb, 0x91, 0x19, 0xcb, 0xba, 0x08, 0xc9, 0x3c, 0xe0,
0x68, 0x18, 0x6a, 0x40, 0x85, 0xe3, 0x70, 0x44, 0x7c, 0x77, 0x68, 0x16, 0x5b, 0x46, 0xbb, 0xe2, 0x47, 0xb1, 0x14, 0x0c, 0x35, 0xa0, 0xc2, 0x70, 0x38, 0x24, 0x9e, 0xed, 0xea, 0xc5, 0x96, 0xd6,
0x4c, 0x64, 0xb4, 0x0e, 0x45, 0xc6, 0x3d, 0xe2, 0x9b, 0x25, 0x79, 0x89, 0x12, 0x84, 0x53, 0x8c, 0xae, 0x58, 0x63, 0x1b, 0xad, 0x41, 0x91, 0x32, 0x87, 0x78, 0x7a, 0x49, 0x6c, 0x22, 0x0d, 0x1e,
0x7b, 0x34, 0xe2, 0x66, 0x59, 0x39, 0xa5, 0x24, 0xbd, 0x8f, 0xc3, 0xd0, 0xac, 0x4c, 0xf6, 0x71, 0x14, 0x65, 0x8e, 0x1f, 0x31, 0xbd, 0x2c, 0x83, 0x92, 0x96, 0x9a, 0xc7, 0x61, 0xa8, 0x57, 0xc6,
0x18, 0xa2, 0x26, 0x40, 0x6f, 0x80, 0x7b, 0x17, 0x01, 0x25, 0x3e, 0x37, 0xab, 0xf2, 0x2c, 0xb6, 0xf3, 0x38, 0x0c, 0x51, 0x13, 0xa0, 0x37, 0xc0, 0xbd, 0xf3, 0xc0, 0x27, 0x1e, 0xd3, 0xab, 0xe2,
0x83, 0x3e, 0x84, 0xb5, 0xc0, 0x0d, 0xb1, 0xcf, 0x4f, 0x63, 0x30, 0x90, 0xb0, 0x55, 0x75, 0xb0, 0x5b, 0x6c, 0x06, 0x7d, 0x0a, 0xab, 0x81, 0x1d, 0x62, 0x8f, 0x9d, 0xc4, 0x60, 0x20, 0x60, 0x2b,
0x3f, 0x05, 0xdb, 0x50, 0xa6, 0x01, 0x27, 0xd4, 0x67, 0x66, 0xad, 0x65, 0xb4, 0x6b, 0xdd, 0x75, 0xf2, 0xc3, 0xde, 0x04, 0x6c, 0x42, 0xd9, 0x0f, 0x18, 0xf1, 0x3d, 0xaa, 0xd7, 0x5a, 0x5a, 0xbb,
0x5b, 0xd1, 0x6c, 0x8f, 0x69, 0xb6, 0x77, 0xfd, 0x6b, 0x67, 0x0c, 0xb2, 0xb6, 0x00, 0xc5, 0xc3, 0xd6, 0x5d, 0x33, 0x25, 0xcd, 0xe6, 0x88, 0x66, 0x73, 0xc7, 0xbb, 0xb2, 0x46, 0x20, 0x63, 0x13,
0xcd, 0x02, 0xea, 0x33, 0x8c, 0x56, 0x21, 0x1f, 0xe8, 0x80, 0xd7, 0x1d, 0xb1, 0xb4, 0x7e, 0x31, 0x50, 0x3c, 0xdd, 0x34, 0xf0, 0x3d, 0x8a, 0xd1, 0x0a, 0xe4, 0x03, 0x95, 0xf0, 0xba, 0xc5, 0x87,
0x60, 0xe5, 0x19, 0x1e, 0x62, 0x8e, 0x93, 0x41, 0x68, 0x13, 0x6a, 0xf8, 0x8a, 0xf0, 0x53, 0xc6, 0xc6, 0xef, 0x1a, 0x2c, 0x3f, 0xc3, 0x2e, 0x66, 0x38, 0x19, 0x84, 0x36, 0xa0, 0x86, 0x2f, 0x09,
0x5d, 0x1e, 0x31, 0xc9, 0x49, 0xdd, 0x01, 0xb1, 0x75, 0x2c, 0x77, 0xd0, 0x2e, 0x54, 0x85, 0x84, 0x3b, 0xa1, 0xcc, 0x66, 0x11, 0x15, 0x9c, 0xd4, 0x2d, 0xe0, 0x53, 0x47, 0x62, 0x06, 0xed, 0x40,
0xbd, 0x53, 0x97, 0x4b, 0x66, 0x6a, 0xdd, 0xc6, 0x82, 0x7f, 0x27, 0xe3, 0x34, 0xdc, 0xab, 0xdc, 0x95, 0x5b, 0xd8, 0x39, 0xb1, 0x99, 0x60, 0xa6, 0xd6, 0x6d, 0xcc, 0xc5, 0x77, 0x3c, 0x92, 0xe1,
0xbc, 0xde, 0xbc, 0xf7, 0xeb, 0x5f, 0x9b, 0x86, 0x53, 0x51, 0x6a, 0xbb, 0xdc, 0xb2, 0x61, 0x5d, 0x6e, 0xe5, 0xfa, 0xed, 0xc6, 0xd2, 0x1f, 0xff, 0x6c, 0x68, 0x56, 0x45, 0xba, 0xed, 0x30, 0xc3,
0xf9, 0x71, 0x14, 0xd2, 0x1e, 0x66, 0x2c, 0x25, 0x45, 0xac, 0xdf, 0x0c, 0x40, 0xcf, 0xaf, 0x70, 0x84, 0x35, 0x19, 0xc7, 0x61, 0xe8, 0xf7, 0x30, 0xa5, 0x29, 0x12, 0x31, 0xfe, 0xd4, 0x00, 0x3d,
0x2f, 0x1b, 0x7c, 0x86, 0xee, 0x5c, 0x12, 0xdd, 0xf9, 0xbb, 0xe9, 0x2e, 0x24, 0xd0, 0x5d, 0x9c, 0xbf, 0xc4, 0xbd, 0x6c, 0xf0, 0x29, 0xba, 0x73, 0x49, 0x74, 0xe7, 0x6f, 0xa7, 0xbb, 0x90, 0x40,
0xa1, 0xbb, 0x0d, 0x05, 0x16, 0xe0, 0x9e, 0xcc, 0x99, 0x24, 0x7a, 0x24, 0xc2, 0xba, 0x0f, 0xef, 0x77, 0x71, 0x8a, 0xee, 0x36, 0x14, 0x68, 0x80, 0x7b, 0x42, 0x33, 0x49, 0xf4, 0x08, 0x84, 0x71,
0xcd, 0x78, 0xae, 0xe2, 0x6e, 0x7d, 0x0b, 0xab, 0x0e, 0x66, 0xe4, 0x27, 0x7c, 0xc4, 0xaf, 0xd3, 0x1f, 0x3e, 0x9c, 0x8a, 0x5c, 0xe6, 0xdd, 0xf8, 0x01, 0x56, 0x2c, 0x4c, 0xc9, 0xaf, 0xf8, 0x90,
0x9e, 0xb3, 0x0e, 0xc5, 0x1f, 0x89, 0xc7, 0x07, 0x9a, 0x0b, 0x25, 0x08, 0xd7, 0x06, 0x98, 0xf4, 0x5d, 0xa5, 0x1d, 0x67, 0x0d, 0x8a, 0xbf, 0x10, 0x87, 0x0d, 0x14, 0x17, 0xd2, 0xe0, 0xa1, 0x0d,
0x07, 0x8a, 0x83, 0xba, 0xa3, 0x25, 0x6b, 0x0b, 0xde, 0x11, 0x44, 0xe1, 0xb4, 0x98, 0xfe, 0x9e, 0x30, 0xe9, 0x0f, 0x24, 0x07, 0x75, 0x4b, 0x59, 0xc6, 0x26, 0xdc, 0xe3, 0x44, 0xe1, 0xb4, 0x9c,
0x83, 0xba, 0x06, 0xea, 0x5c, 0x58, 0xb6, 0x40, 0x75, 0xee, 0xe4, 0xa7, 0xb9, 0xb3, 0x23, 0xc2, 0xbe, 0xc9, 0x41, 0x5d, 0x01, 0x95, 0x16, 0x16, 0xbd, 0xa0, 0x4a, 0x3b, 0xf9, 0x89, 0x76, 0xb6,
0x25, 0xd3, 0x46, 0x84, 0x71, 0xa5, 0xfb, 0x30, 0x5e, 0x98, 0x97, 0xdb, 0xba, 0x36, 0x55, 0x1e, 0x79, 0xba, 0x84, 0x6c, 0x78, 0x1a, 0x97, 0xbb, 0x0f, 0xe3, 0x17, 0xf3, 0x62, 0x4b, 0xdd, 0x4d,
0x39, 0x1a, 0x3a, 0x65, 0xa4, 0x78, 0x37, 0x23, 0xa5, 0x04, 0x46, 0xca, 0x33, 0x8c, 0xc4, 0x39, 0xa9, 0x23, 0x4b, 0x41, 0x27, 0x8c, 0x14, 0x6f, 0x67, 0xa4, 0x94, 0xc0, 0x48, 0x79, 0x8a, 0x91,
0xaf, 0xcc, 0x71, 0x3e, 0x97, 0xd2, 0xd5, 0xff, 0x4f, 0x69, 0x78, 0xab, 0x94, 0x7e, 0x01, 0xb5, 0x38, 0xe7, 0x95, 0x19, 0xce, 0x67, 0x24, 0x5d, 0x7d, 0xbf, 0xa4, 0xe1, 0x4e, 0x92, 0x7e, 0x01,
0x2f, 0xc9, 0x70, 0x98, 0xa1, 0xd9, 0x31, 0xd2, 0x1f, 0x27, 0x66, 0xdd, 0xd1, 0x92, 0x88, 0xa5, 0xb5, 0x6f, 0x89, 0xeb, 0x66, 0x28, 0x76, 0x94, 0xf4, 0x47, 0xc2, 0xac, 0x5b, 0xca, 0xe2, 0xb9,
0x3b, 0x1c, 0xca, 0x58, 0x56, 0x1c, 0xb1, 0xb4, 0x3e, 0x87, 0x95, 0xfd, 0x21, 0x65, 0xf8, 0xe0, 0xb4, 0x5d, 0x57, 0xe4, 0xb2, 0x62, 0xf1, 0xa1, 0xf1, 0x35, 0x2c, 0xef, 0xb9, 0x3e, 0xc5, 0xfb,
0x45, 0x86, 0xfc, 0x50, 0x01, 0x54, 0xb9, 0xae, 0x04, 0xeb, 0x03, 0x78, 0xf7, 0x2b, 0xc2, 0xf8, 0x2f, 0x32, 0xe8, 0x43, 0x26, 0x50, 0x6a, 0x5d, 0x1a, 0xc6, 0x27, 0xf0, 0xc1, 0x77, 0x84, 0xb2,
0x11, 0xf1, 0x52, 0xcb, 0x6b, 0x0b, 0x56, 0xa7, 0x50, 0x9d, 0x0c, 0x08, 0x0a, 0x01, 0xf1, 0x98, 0x43, 0xe2, 0xa4, 0x5e, 0xaf, 0x4d, 0x58, 0x99, 0x40, 0x95, 0x18, 0x10, 0x14, 0x02, 0xe2, 0x50,
0x69, 0xb4, 0xf2, 0xed, 0xba, 0x23, 0xd7, 0xd6, 0xf7, 0x70, 0x7f, 0xda, 0xa5, 0xe2, 0xad, 0x5d, 0x5d, 0x6b, 0xe5, 0xdb, 0x75, 0x4b, 0x8c, 0x8d, 0x9f, 0xe0, 0xfe, 0xa4, 0x4a, 0xc5, 0x4b, 0x3b,
0x80, 0x5d, 0x3e, 0x50, 0xa6, 0x1d, 0xb9, 0x8e, 0x37, 0xb1, 0x5c, 0x96, 0x26, 0xf6, 0x08, 0x56, 0x07, 0xdb, 0x6c, 0x20, 0x97, 0xb6, 0xc4, 0x38, 0x5e, 0xc4, 0x72, 0x59, 0x8a, 0xd8, 0x23, 0x58,
0x8f, 0x07, 0x64, 0x74, 0xe0, 0x9f, 0xd3, 0x89, 0x13, 0x0f, 0xa0, 0x22, 0xc6, 0xe6, 0xe9, 0xb4, 0x39, 0x1a, 0x90, 0xe1, 0xbe, 0x77, 0xe6, 0x8f, 0x83, 0x78, 0x00, 0x15, 0xde, 0x36, 0x4f, 0x26,
0x45, 0x95, 0x85, 0x7c, 0x44, 0x3c, 0xeb, 0x0b, 0x58, 0x7b, 0x19, 0x78, 0x73, 0x23, 0xa6, 0x0b, 0x25, 0xaa, 0xcc, 0xed, 0x43, 0xe2, 0x18, 0xdf, 0xc0, 0xea, 0xcb, 0xc0, 0x99, 0x69, 0x31, 0x5d,
0xd5, 0x10, 0x33, 0x1a, 0x85, 0x3d, 0xcc, 0xa4, 0x42, 0xd2, 0xad, 0x53, 0x98, 0xae, 0x97, 0x90, 0xa8, 0x86, 0x98, 0xfa, 0x51, 0xd8, 0xc3, 0x54, 0x38, 0x24, 0xed, 0x3a, 0x81, 0xa9, 0xfb, 0x12,
0xa7, 0x05, 0xe9, 0xa9, 0x2c, 0x17, 0x81, 0x4b, 0x29, 0x17, 0x5d, 0x16, 0xb9, 0x49, 0x59, 0x74, 0xb2, 0xb4, 0x24, 0x3d, 0x15, 0xd7, 0x85, 0xe3, 0x52, 0xae, 0x8b, 0xba, 0x16, 0xb9, 0x49, 0xdd,
0xff, 0x01, 0x28, 0x88, 0xb7, 0xa1, 0x01, 0x14, 0x65, 0xc9, 0x21, 0xdb, 0x4e, 0xfb, 0x4e, 0xb0, 0xfd, 0x18, 0x6a, 0xaf, 0x6c, 0x92, 0xba, 0x43, 0x08, 0xf7, 0x24, 0x4c, 0x6d, 0x30, 0x23, 0x5b,
0xe3, 0x45, 0xdc, 0xe8, 0x64, 0xc6, 0x6b, 0xe7, 0x18, 0x94, 0xd4, 0x48, 0x40, 0x3b, 0xe9, 0xaa, 0xed, 0xfd, 0xb2, 0xcd, 0xdd, 0x45, 0xb6, 0xdd, 0x37, 0x35, 0x28, 0xf0, 0xb4, 0xa3, 0x01, 0x14,
0x0b, 0xb3, 0xba, 0xf1, 0xf1, 0x72, 0x4a, 0xfa, 0x52, 0xf5, 0xbc, 0x90, 0x67, 0x7c, 0xde, 0x24, 0x45, 0x35, 0x40, 0xa6, 0x99, 0xf6, 0x84, 0x31, 0xe3, 0xf5, 0xa5, 0xd1, 0xc9, 0x8c, 0x57, 0xc7,
0xe6, 0x19, 0x9f, 0x17, 0x8b, 0xbd, 0x03, 0x25, 0x35, 0x40, 0xd0, 0xc6, 0x02, 0xbf, 0xcf, 0xc5, 0xa2, 0x50, 0x92, 0xdd, 0x0a, 0x6d, 0xa7, 0xbb, 0xce, 0x3d, 0x23, 0x1a, 0x9f, 0x2f, 0xe6, 0xa4,
0x47, 0x53, 0xe3, 0xa3, 0x74, 0x93, 0x73, 0xa3, 0xf0, 0x1a, 0xea, 0x33, 0x43, 0x09, 0x3d, 0xce, 0x36, 0x95, 0xc7, 0x0b, 0x59, 0xc6, 0xe3, 0x8d, 0xe5, 0x90, 0xf1, 0x78, 0x31, 0x59, 0x58, 0x50,
0x6a, 0x62, 0x76, 0x2c, 0xbd, 0xc5, 0xd5, 0xaf, 0xa0, 0x32, 0x2e, 0x40, 0xb4, 0x9d, 0xae, 0x3d, 0x92, 0xbd, 0x0d, 0xad, 0xcf, 0x71, 0xf1, 0x9c, 0xbf, 0xe7, 0x1a, 0x9f, 0xa5, 0x2f, 0x39, 0xd3,
0x57, 0xd7, 0x8d, 0xee, 0x32, 0x2a, 0xfa, 0xca, 0x27, 0x50, 0x3c, 0x72, 0x23, 0x96, 0x1c, 0xc0, 0xa5, 0xaf, 0xa0, 0x3e, 0xd5, 0x2f, 0xd1, 0xe3, 0xac, 0x4b, 0x4c, 0x77, 0xcc, 0x3b, 0x6c, 0xfd,
0x84, 0x7d, 0xf4, 0x09, 0x94, 0x1c, 0xcc, 0xa2, 0xd1, 0xf2, 0x9a, 0x3f, 0x00, 0xc4, 0x3e, 0x72, 0x1a, 0x2a, 0xa3, 0xda, 0x80, 0xb6, 0xd2, 0xbd, 0x67, 0x4a, 0x4e, 0xa3, 0xbb, 0x88, 0x8b, 0xda,
0x9e, 0x64, 0x48, 0xb1, 0xbb, 0x9a, 0x4d, 0xa2, 0xf9, 0x43, 0x28, 0x88, 0x0e, 0x8c, 0x1e, 0xa5, 0xf2, 0x09, 0x14, 0x0f, 0xed, 0x88, 0x26, 0x27, 0x30, 0x61, 0x1e, 0x7d, 0x01, 0x25, 0x0b, 0xd3,
0x1b, 0x8e, 0x75, 0xea, 0x44, 0x73, 0x27, 0x50, 0x10, 0x83, 0x1b, 0x65, 0x28, 0x85, 0xc5, 0x4f, 0x68, 0xb8, 0xb8, 0xe7, 0xcf, 0x00, 0xb1, 0xf7, 0xd7, 0x93, 0x0c, 0x12, 0xbb, 0xad, 0x0e, 0x26,
0x93, 0x44, 0xab, 0xdf, 0x40, 0x75, 0x32, 0xf7, 0x51, 0x06, 0xde, 0xe6, 0x3f, 0x12, 0x12, 0x0d, 0x2e, 0x7f, 0x00, 0x05, 0xde, 0x1c, 0xd0, 0xa3, 0xf4, 0x85, 0x63, 0x4d, 0x24, 0x71, 0xb9, 0x63,
0x1f, 0x43, 0x59, 0x8f, 0x0b, 0x94, 0x21, 0xff, 0x66, 0x27, 0x4b, 0xa2, 0xd1, 0xaf, 0xa1, 0x32, 0x28, 0xf0, 0x37, 0x05, 0xca, 0x70, 0x15, 0xe6, 0x5f, 0x4d, 0x89, 0xab, 0xbe, 0x82, 0xea, 0xf8,
0xee, 0xc9, 0x89, 0x6c, 0x67, 0x78, 0xc4, 0x42, 0x5f, 0x7f, 0x09, 0x25, 0xd5, 0xbc, 0xb3, 0x74, 0x49, 0x82, 0x32, 0xf0, 0x36, 0xfb, 0x7e, 0x49, 0x5c, 0xf8, 0x08, 0xca, 0xaa, 0x93, 0xa1, 0x0c,
0xa7, 0x85, 0x36, 0x9f, 0xe4, 0xee, 0xde, 0xe1, 0xcd, 0x9b, 0xe6, 0xbd, 0x3f, 0xdf, 0x34, 0xef, 0xfa, 0x9b, 0x6e, 0x7a, 0x89, 0x8b, 0x7e, 0x0f, 0x95, 0x51, 0xbb, 0x48, 0x64, 0x3b, 0xc3, 0x21,
0xfd, 0x7c, 0xdb, 0x34, 0x6e, 0x6e, 0x9b, 0xc6, 0x1f, 0xb7, 0x4d, 0xe3, 0xef, 0xdb, 0xa6, 0xf1, 0xe6, 0x5a, 0xce, 0x4b, 0x28, 0xc9, 0xbe, 0x92, 0xa5, 0x3a, 0xcd, 0x75, 0xa0, 0xc4, 0x70, 0x31,
0xdd, 0xce, 0x72, 0xff, 0xd1, 0x7d, 0x26, 0x7e, 0xcf, 0x4a, 0xd2, 0xfc, 0xce, 0x7f, 0x01, 0x00, 0x14, 0x78, 0x6d, 0xcf, 0xa2, 0x80, 0x58, 0xab, 0x68, 0x98, 0x59, 0xe1, 0x32, 0xfa, 0xdd, 0x83,
0x00, 0xff, 0xff, 0xa0, 0x05, 0x00, 0x28, 0x0f, 0x0e, 0x00, 0x00, 0xeb, 0x77, 0xcd, 0xa5, 0xbf, 0xdf, 0x35, 0x97, 0x7e, 0xbb, 0x69, 0x6a, 0xd7, 0x37, 0x4d, 0xed,
0xaf, 0x9b, 0xa6, 0xf6, 0xef, 0x4d, 0x53, 0xfb, 0x71, 0x7b, 0xb1, 0xff, 0x69, 0xbf, 0xe2, 0xbf,
0xa7, 0x25, 0x71, 0x8a, 0xed, 0xff, 0x03, 0x00, 0x00, 0xff, 0xff, 0x70, 0x88, 0x12, 0x32, 0x11,
0x0f, 0x00, 0x00,
} }

View File

@ -47,6 +47,8 @@ service Shim {
rpc ShimInfo(google.protobuf.Empty) returns (ShimInfoResponse); rpc ShimInfo(google.protobuf.Empty) returns (ShimInfoResponse);
rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty); rpc Update(UpdateTaskRequest) returns (google.protobuf.Empty);
rpc Wait(WaitRequest) returns (WaitResponse);
} }
message CreateTaskRequest { message CreateTaskRequest {
@ -152,3 +154,12 @@ message StartResponse {
string id = 1; string id = 1;
uint32 pid = 2; uint32 pid = 2;
} }
message WaitRequest {
string id = 1;
}
message WaitResponse {
uint32 exit_status = 1;
google.protobuf.Timestamp exited_at = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
}

View File

@ -22,12 +22,18 @@ type Task struct {
shim *client.Client shim *client.Client
namespace string namespace string
cg cgroups.Cgroup cg cgroups.Cgroup
monitor runtime.TaskMonitor
} }
func newTask(id, namespace string, pid int, shim *client.Client) (*Task, error) { func newTask(id, namespace string, pid int, shim *client.Client, monitor runtime.TaskMonitor) (*Task, error) {
cg, err := cgroups.Load(cgroups.V1, cgroups.PidPath(pid)) var (
if err != nil { err error
return nil, err cg cgroups.Cgroup
)
if pid > 0 {
if cg, err = cgroups.Load(cgroups.V1, cgroups.PidPath(pid)); err != nil {
return nil, err
}
} }
return &Task{ return &Task{
id: id, id: id,
@ -35,6 +41,7 @@ func newTask(id, namespace string, pid int, shim *client.Client) (*Task, error)
shim: shim, shim: shim,
namespace: namespace, namespace: namespace,
cg: cg, cg: cg,
monitor: monitor,
}, nil }, nil
} }
@ -51,12 +58,24 @@ func (t *Task) Info() runtime.TaskInfo {
} }
func (t *Task) Start(ctx context.Context) error { func (t *Task) Start(ctx context.Context) error {
_, err := t.shim.Start(ctx, &shim.StartRequest{ hasCgroup := t.cg != nil
r, err := t.shim.Start(ctx, &shim.StartRequest{
ID: t.id, ID: t.id,
}) })
if err != nil { if err != nil {
return errdefs.FromGRPC(err) return errdefs.FromGRPC(err)
} }
t.pid = int(r.Pid)
if !hasCgroup {
cg, err := cgroups.Load(cgroups.V1, cgroups.PidPath(t.pid))
if err != nil {
return err
}
t.cg = cg
if err := t.monitor.Monitor(t); err != nil {
return err
}
}
return nil return nil
} }
@ -225,3 +244,16 @@ func (t *Task) Metrics(ctx context.Context) (interface{}, error) {
func (t *Task) Cgroup() cgroups.Cgroup { func (t *Task) Cgroup() cgroups.Cgroup {
return t.cg return t.cg
} }
func (t *Task) Wait(ctx context.Context) (*runtime.Exit, error) {
r, err := t.shim.Wait(ctx, &shim.WaitRequest{
ID: t.id,
})
if err != nil {
return nil, err
}
return &runtime.Exit{
Timestamp: r.ExitedAt,
Status: r.ExitStatus,
}, nil
}

View File

@ -6,11 +6,8 @@ import (
"syscall" "syscall"
"time" "time"
eventsapi "github.com/containerd/containerd/api/services/events/v1"
"github.com/containerd/containerd/api/services/tasks/v1" "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/errdefs" "github.com/containerd/containerd/errdefs"
"github.com/containerd/containerd/runtime"
"github.com/containerd/typeurl"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -121,54 +118,25 @@ func (p *process) Kill(ctx context.Context, s syscall.Signal, opts ...KillOpts)
} }
func (p *process) Wait(ctx context.Context) (<-chan ExitStatus, error) { func (p *process) Wait(ctx context.Context) (<-chan ExitStatus, error) {
cancellable, cancel := context.WithCancel(ctx) c := make(chan ExitStatus, 1)
eventstream, err := p.task.client.EventService().Subscribe(cancellable, &eventsapi.SubscribeRequest{
Filters: []string{"topic==" + runtime.TaskExitEventTopic},
})
if err != nil {
cancel()
return nil, err
}
// first check if the task has exited
status, err := p.Status(ctx)
if err != nil {
cancel()
return nil, errdefs.FromGRPC(err)
}
chStatus := make(chan ExitStatus, 1)
if status.Status == Stopped {
cancel()
chStatus <- ExitStatus{code: status.ExitStatus, exitedAt: status.ExitTime}
return chStatus, nil
}
go func() { go func() {
defer cancel() r, err := p.task.client.TaskService().Wait(ctx, &tasks.WaitRequest{
chStatus <- ExitStatus{} // signal that the goroutine is running ContainerID: p.task.id,
for { ExecID: p.id,
evt, err := eventstream.Recv() })
if err != nil { if err != nil {
chStatus <- ExitStatus{code: UnknownExitStatus, err: err} c <- ExitStatus{
return code: UnknownExitStatus,
} err: err,
if typeurl.Is(evt.Event, &eventsapi.TaskExit{}) {
v, err := typeurl.UnmarshalAny(evt.Event)
if err != nil {
chStatus <- ExitStatus{code: UnknownExitStatus, err: err}
return
}
e := v.(*eventsapi.TaskExit)
if e.ID == p.id && e.ContainerID == p.task.id {
chStatus <- ExitStatus{code: e.ExitStatus, exitedAt: e.ExitedAt}
return
}
} }
return
}
c <- ExitStatus{
code: r.ExitStatus,
exitedAt: r.ExitedAt,
} }
}() }()
return c, nil
<-chStatus // wait for the goroutine to be running
return chStatus, nil
} }
func (p *process) CloseIO(ctx context.Context, opts ...IOCloserOpts) error { func (p *process) CloseIO(ctx context.Context, opts ...IOCloserOpts) error {

View File

@ -26,6 +26,8 @@ type Process interface {
CloseIO(context.Context) error CloseIO(context.Context) error
// Start the container's user defined process // Start the container's user defined process
Start(context.Context) error Start(context.Context) error
// Wait for the process to exit
Wait(context.Context) (*Exit, error)
} }
type Task interface { type Task interface {

View File

@ -478,6 +478,27 @@ func (s *Service) Metrics(ctx context.Context, r *api.MetricsRequest) (*api.Metr
return &resp, nil return &resp, nil
} }
func (s *Service) Wait(ctx context.Context, r *api.WaitRequest) (*api.WaitResponse, error) {
t, err := s.getTask(ctx, r.ContainerID)
if err != nil {
return nil, err
}
p := runtime.Process(t)
if r.ExecID != "" {
if p, err = t.Process(ctx, r.ExecID); err != nil {
return nil, errdefs.ToGRPC(err)
}
}
exit, err := p.Wait(ctx)
if err != nil {
return nil, errdefs.ToGRPC(err)
}
return &api.WaitResponse{
ExitStatus: exit.Status,
ExitedAt: exit.Timestamp,
}, nil
}
func getTasksMetrics(ctx context.Context, filter filters.Filter, tasks []runtime.Task, r *api.MetricsResponse) { func getTasksMetrics(ctx context.Context, filter filters.Filter, tasks []runtime.Task, r *api.MetricsResponse) {
for _, tk := range tasks { for _, tk := range tasks {
if !filter.Match(filters.AdapterFunc(func(fieldpath []string) (string, bool) { if !filter.Match(filters.AdapterFunc(func(fieldpath []string) (string, bool) {

90
task.go
View File

@ -12,7 +12,6 @@ import (
"syscall" "syscall"
"time" "time"
eventsapi "github.com/containerd/containerd/api/services/events/v1"
"github.com/containerd/containerd/api/services/tasks/v1" "github.com/containerd/containerd/api/services/tasks/v1"
"github.com/containerd/containerd/api/types" "github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/content" "github.com/containerd/containerd/content"
@ -20,7 +19,6 @@ import (
"github.com/containerd/containerd/mount" "github.com/containerd/containerd/mount"
"github.com/containerd/containerd/plugin" "github.com/containerd/containerd/plugin"
"github.com/containerd/containerd/rootfs" "github.com/containerd/containerd/rootfs"
"github.com/containerd/containerd/runtime"
"github.com/containerd/typeurl" "github.com/containerd/typeurl"
digest "github.com/opencontainers/go-digest" digest "github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go/v1" "github.com/opencontainers/image-spec/specs-go/v1"
@ -135,8 +133,7 @@ type task struct {
id string id string
pid uint32 pid uint32
mu sync.Mutex mu sync.Mutex
deferred *tasks.CreateTaskRequest
} }
// Pid returns the pid or process id for the task // Pid returns the pid or process id for the task
@ -145,28 +142,15 @@ func (t *task) Pid() uint32 {
} }
func (t *task) Start(ctx context.Context) error { func (t *task) Start(ctx context.Context) error {
t.mu.Lock() r, err := t.client.TaskService().Start(ctx, &tasks.StartRequest{
deferred := t.deferred
t.mu.Unlock()
if deferred != nil {
response, err := t.client.TaskService().Create(ctx, deferred)
t.mu.Lock()
t.deferred = nil
t.mu.Unlock()
if err != nil {
t.io.Close()
return errdefs.FromGRPC(err)
}
t.pid = response.Pid
return nil
}
_, err := t.client.TaskService().Start(ctx, &tasks.StartRequest{
ContainerID: t.id, ContainerID: t.id,
}) })
if err != nil { if err != nil {
t.io.Close() t.io.Close()
return errdefs.FromGRPC(err)
} }
return errdefs.FromGRPC(err) t.pid = r.Pid
return nil
} }
func (t *task) Kill(ctx context.Context, s syscall.Signal, opts ...KillOpts) error { func (t *task) Kill(ctx context.Context, s syscall.Signal, opts ...KillOpts) error {
@ -216,60 +200,24 @@ func (t *task) Status(ctx context.Context) (Status, error) {
} }
func (t *task) Wait(ctx context.Context) (<-chan ExitStatus, error) { func (t *task) Wait(ctx context.Context) (<-chan ExitStatus, error) {
cancellable, cancel := context.WithCancel(ctx) c := make(chan ExitStatus, 1)
eventstream, err := t.client.EventService().Subscribe(cancellable, &eventsapi.SubscribeRequest{
Filters: []string{"topic==" + runtime.TaskExitEventTopic},
})
if err != nil {
cancel()
return nil, errdefs.FromGRPC(err)
}
chStatus := make(chan ExitStatus, 1)
t.mu.Lock()
checkpoint := t.deferred != nil
t.mu.Unlock()
if !checkpoint {
// first check if the task has exited
status, err := t.Status(ctx)
if err != nil {
cancel()
return nil, errdefs.FromGRPC(err)
}
if status.Status == Stopped {
cancel()
chStatus <- ExitStatus{code: status.ExitStatus, exitedAt: status.ExitTime}
return chStatus, nil
}
}
go func() { go func() {
defer cancel() r, err := t.client.TaskService().Wait(ctx, &tasks.WaitRequest{
chStatus <- ExitStatus{} // signal that goroutine is running ContainerID: t.id,
for { })
evt, err := eventstream.Recv() if err != nil {
if err != nil { c <- ExitStatus{
chStatus <- ExitStatus{code: UnknownExitStatus, err: errdefs.FromGRPC(err)} code: UnknownExitStatus,
return err: err,
}
if typeurl.Is(evt.Event, &eventsapi.TaskExit{}) {
v, err := typeurl.UnmarshalAny(evt.Event)
if err != nil {
chStatus <- ExitStatus{code: UnknownExitStatus, err: err}
return
}
e := v.(*eventsapi.TaskExit)
if e.ContainerID == t.id && e.Pid == t.pid {
chStatus <- ExitStatus{code: e.ExitStatus, exitedAt: e.ExitedAt}
return
}
} }
return
}
c <- ExitStatus{
code: r.ExitStatus,
exitedAt: r.ExitedAt,
} }
}() }()
return c, nil
<-chStatus // wait for the goroutine to be running
return chStatus, nil
} }
// Delete deletes the task and its runtime state // Delete deletes the task and its runtime state

View File

@ -178,3 +178,16 @@ func (p *process) Start(ctx context.Context) (err error) {
p.hcs = hp p.hcs = hp
return nil return nil
} }
func (p *process) Wait(ctx context.Context) (*runtime.Exit, error) {
<-p.exitCh
ec, ea, err := p.ExitCode()
if err != nil {
return nil, err
}
return &runtime.Exit{
Status: ec,
Timestamp: ea,
}, nil
}

View File

@ -274,6 +274,14 @@ func (t *task) Metrics(ctx context.Context) (interface{}, error) {
return nil, errors.Wrap(errdefs.ErrUnavailable, "not supported") return nil, errors.Wrap(errdefs.ErrUnavailable, "not supported")
} }
func (t *task) Wait(ctx context.Context) (*runtime.Exit, error) {
p := t.getProcess(t.id)
if p == nil {
return nil, errors.Wrapf(errdefs.ErrNotFound, "no such process %d", t.id)
}
return p.Wait(ctx)
}
func (t *task) newProcess(ctx context.Context, id string, conf *hcsshim.ProcessConfig, pset *pipeSet) (*process, error) { func (t *task) newProcess(ctx context.Context, id string, conf *hcsshim.ProcessConfig, pset *pipeSet) (*process, error) {
var ( var (
err error err error