From 0aad42f5cff587ba5008983efec9fb47aaabf85e Mon Sep 17 00:00:00 2001 From: Kenfe-Mickael Laventure Date: Fri, 9 Dec 2016 09:17:34 -0800 Subject: [PATCH] Add exec and terminal support Signed-off-by: Kenfe-Mickael Laventure --- api/execution/execution.pb.go | 231 +++++++++++++++++++---------- api/execution/execution.proto | 14 +- cmd/ctr/exec.go | 87 +++++++++++ cmd/ctr/main.go | 9 +- cmd/ctr/run.go | 115 +------------- cmd/ctr/utils.go | 122 +++++++++++++++ execution/executor.go | 22 +-- execution/executors/oci/console.go | 54 +++++++ execution/executors/oci/oci.go | 68 +++++++-- execution/service.go | 40 +++-- 10 files changed, 531 insertions(+), 231 deletions(-) create mode 100644 cmd/ctr/exec.go create mode 100644 cmd/ctr/utils.go create mode 100644 execution/executors/oci/console.go diff --git a/api/execution/execution.pb.go b/api/execution/execution.pb.go index 8deddbf..69f60ed 100644 --- a/api/execution/execution.pb.go +++ b/api/execution/execution.pb.go @@ -102,9 +102,10 @@ func (*StartContainerRequest) Descriptor() ([]byte, []int) { return fileDescript type CreateContainerRequest struct { ID string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` BundlePath string `protobuf:"bytes,2,opt,name=bundle_path,json=bundlePath,proto3" json:"bundle_path,omitempty"` - Stdin string `protobuf:"bytes,3,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,4,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,5,opt,name=stderr,proto3" json:"stderr,omitempty"` + Console bool `protobuf:"varint,3,opt,name=console,proto3" json:"console,omitempty"` + Stdin string `protobuf:"bytes,4,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,5,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,6,opt,name=stderr,proto3" json:"stderr,omitempty"` } func (m *CreateContainerRequest) Reset() { *m = CreateContainerRequest{} } @@ -146,9 +147,10 @@ func (*ListContainersResponse) Descriptor() ([]byte, []int) { return fileDescrip type StartProcessRequest struct { ContainerId string `protobuf:"bytes,1,opt,name=container_id,json=containerId,proto3" json:"container_id,omitempty"` Process *Process `protobuf:"bytes,2,opt,name=process" json:"process,omitempty"` - Stdin string `protobuf:"bytes,3,opt,name=stdin,proto3" json:"stdin,omitempty"` - Stdout string `protobuf:"bytes,4,opt,name=stdout,proto3" json:"stdout,omitempty"` - Stderr string `protobuf:"bytes,5,opt,name=stderr,proto3" json:"stderr,omitempty"` + Console bool `protobuf:"varint,3,opt,name=console,proto3" json:"console,omitempty"` + Stdin string `protobuf:"bytes,4,opt,name=stdin,proto3" json:"stdin,omitempty"` + Stdout string `protobuf:"bytes,5,opt,name=stdout,proto3" json:"stdout,omitempty"` + Stderr string `protobuf:"bytes,6,opt,name=stderr,proto3" json:"stderr,omitempty"` } func (m *StartProcessRequest) Reset() { *m = StartProcessRequest{} } @@ -330,10 +332,11 @@ func (this *CreateContainerRequest) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 9) + s := make([]string, 0, 10) s = append(s, "&execution.CreateContainerRequest{") s = append(s, "ID: "+fmt.Sprintf("%#v", this.ID)+",\n") s = append(s, "BundlePath: "+fmt.Sprintf("%#v", this.BundlePath)+",\n") + s = append(s, "Console: "+fmt.Sprintf("%#v", this.Console)+",\n") s = append(s, "Stdin: "+fmt.Sprintf("%#v", this.Stdin)+",\n") s = append(s, "Stdout: "+fmt.Sprintf("%#v", this.Stdout)+",\n") s = append(s, "Stderr: "+fmt.Sprintf("%#v", this.Stderr)+",\n") @@ -388,12 +391,13 @@ func (this *StartProcessRequest) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 9) + s := make([]string, 0, 10) s = append(s, "&execution.StartProcessRequest{") s = append(s, "ContainerId: "+fmt.Sprintf("%#v", this.ContainerId)+",\n") if this.Process != nil { s = append(s, "Process: "+fmt.Sprintf("%#v", this.Process)+",\n") } + s = append(s, "Console: "+fmt.Sprintf("%#v", this.Console)+",\n") s = append(s, "Stdin: "+fmt.Sprintf("%#v", this.Stdin)+",\n") s = append(s, "Stdout: "+fmt.Sprintf("%#v", this.Stdout)+",\n") s = append(s, "Stderr: "+fmt.Sprintf("%#v", this.Stderr)+",\n") @@ -1136,20 +1140,30 @@ func (m *CreateContainerRequest) MarshalTo(dAtA []byte) (int, error) { i = encodeVarintExecution(dAtA, i, uint64(len(m.BundlePath))) i += copy(dAtA[i:], m.BundlePath) } + if m.Console { + dAtA[i] = 0x18 + i++ + if m.Console { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } if len(m.Stdin) > 0 { - dAtA[i] = 0x1a + dAtA[i] = 0x22 i++ i = encodeVarintExecution(dAtA, i, uint64(len(m.Stdin))) i += copy(dAtA[i:], m.Stdin) } if len(m.Stdout) > 0 { - dAtA[i] = 0x22 + dAtA[i] = 0x2a i++ i = encodeVarintExecution(dAtA, i, uint64(len(m.Stdout))) i += copy(dAtA[i:], m.Stdout) } if len(m.Stderr) > 0 { - dAtA[i] = 0x2a + dAtA[i] = 0x32 i++ i = encodeVarintExecution(dAtA, i, uint64(len(m.Stderr))) i += copy(dAtA[i:], m.Stderr) @@ -1303,20 +1317,30 @@ func (m *StartProcessRequest) MarshalTo(dAtA []byte) (int, error) { } i += n2 } + if m.Console { + dAtA[i] = 0x18 + i++ + if m.Console { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } if len(m.Stdin) > 0 { - dAtA[i] = 0x1a + dAtA[i] = 0x22 i++ i = encodeVarintExecution(dAtA, i, uint64(len(m.Stdin))) i += copy(dAtA[i:], m.Stdin) } if len(m.Stdout) > 0 { - dAtA[i] = 0x22 + dAtA[i] = 0x2a i++ i = encodeVarintExecution(dAtA, i, uint64(len(m.Stdout))) i += copy(dAtA[i:], m.Stdout) } if len(m.Stderr) > 0 { - dAtA[i] = 0x2a + dAtA[i] = 0x32 i++ i = encodeVarintExecution(dAtA, i, uint64(len(m.Stderr))) i += copy(dAtA[i:], m.Stderr) @@ -1905,6 +1929,9 @@ func (m *CreateContainerRequest) Size() (n int) { if l > 0 { n += 1 + l + sovExecution(uint64(l)) } + if m.Console { + n += 2 + } l = len(m.Stdin) if l > 0 { n += 1 + l + sovExecution(uint64(l)) @@ -1975,6 +2002,9 @@ func (m *StartProcessRequest) Size() (n int) { l = m.Process.Size() n += 1 + l + sovExecution(uint64(l)) } + if m.Console { + n += 2 + } l = len(m.Stdin) if l > 0 { n += 1 + l + sovExecution(uint64(l)) @@ -2236,6 +2266,7 @@ func (this *CreateContainerRequest) String() string { s := strings.Join([]string{`&CreateContainerRequest{`, `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `BundlePath:` + fmt.Sprintf("%v", this.BundlePath) + `,`, + `Console:` + fmt.Sprintf("%v", this.Console) + `,`, `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, @@ -2290,6 +2321,7 @@ func (this *StartProcessRequest) String() string { s := strings.Join([]string{`&StartProcessRequest{`, `ContainerId:` + fmt.Sprintf("%v", this.ContainerId) + `,`, `Process:` + strings.Replace(fmt.Sprintf("%v", this.Process), "Process", "Process", 1) + `,`, + `Console:` + fmt.Sprintf("%v", this.Console) + `,`, `Stdin:` + fmt.Sprintf("%v", this.Stdin) + `,`, `Stdout:` + fmt.Sprintf("%v", this.Stdout) + `,`, `Stderr:` + fmt.Sprintf("%v", this.Stderr) + `,`, @@ -2638,6 +2670,26 @@ func (m *CreateContainerRequest) Unmarshal(dAtA []byte) error { m.BundlePath = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Console", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowExecution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Console = bool(v != 0) + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Stdin", wireType) } @@ -2666,7 +2718,7 @@ func (m *CreateContainerRequest) Unmarshal(dAtA []byte) error { } m.Stdin = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) } @@ -2695,7 +2747,7 @@ func (m *CreateContainerRequest) Unmarshal(dAtA []byte) error { } m.Stdout = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) } @@ -3159,6 +3211,26 @@ func (m *StartProcessRequest) Unmarshal(dAtA []byte) error { } iNdEx = postIndex case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Console", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowExecution + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Console = bool(v != 0) + case 4: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Stdin", wireType) } @@ -3187,7 +3259,7 @@ func (m *StartProcessRequest) Unmarshal(dAtA []byte) error { } m.Stdin = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Stdout", wireType) } @@ -3216,7 +3288,7 @@ func (m *StartProcessRequest) Unmarshal(dAtA []byte) error { } m.Stdout = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 5: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Stderr", wireType) } @@ -5033,66 +5105,67 @@ var ( func init() { proto.RegisterFile("execution.proto", fileDescriptorExecution) } var fileDescriptorExecution = []byte{ - // 967 bytes of a gzipped FileDescriptorProto + // 980 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xc4, 0x56, 0xdd, 0x6e, 0x1b, 0x45, - 0x14, 0xce, 0xfa, 0x2f, 0xf1, 0x71, 0xb7, 0x98, 0x89, 0xb3, 0xac, 0x0c, 0xb8, 0xe9, 0xd2, 0x96, - 0x08, 0x29, 0x4e, 0x30, 0x08, 0x21, 0x71, 0xd5, 0xc6, 0xc6, 0x44, 0x0a, 0xc6, 0x1d, 0xd7, 0xaa, - 0xc4, 0x4d, 0xb4, 0xf1, 0x0e, 0xee, 0x22, 0x67, 0xd7, 0xdd, 0x99, 0x4d, 0xcb, 0x5d, 0xdf, 0x02, - 0x6e, 0x78, 0x07, 0x1e, 0xa3, 0x97, 0x70, 0xc7, 0x15, 0x22, 0x7e, 0x02, 0x1e, 0x01, 0xcd, 0xcf, - 0xfa, 0x67, 0x77, 0xed, 0x18, 0xb7, 0x12, 0x77, 0x33, 0x67, 0xbf, 0xf9, 0xe6, 0x3b, 0x73, 0x66, - 0xbf, 0x39, 0xf0, 0x0e, 0x79, 0x49, 0x06, 0x21, 0x73, 0x7d, 0xaf, 0x3e, 0x0e, 0x7c, 0xe6, 0x23, - 0x7d, 0xe0, 0x7b, 0xcc, 0x76, 0x3d, 0x12, 0x38, 0xf5, 0xab, 0x4f, 0xab, 0xef, 0x0f, 0x7d, 0x7f, - 0x38, 0x22, 0x47, 0xe2, 0xe3, 0x45, 0xf8, 0xc3, 0x11, 0xb9, 0x1c, 0xb3, 0x9f, 0x24, 0xb6, 0x5a, - 0x19, 0xfa, 0x43, 0x5f, 0x0c, 0x8f, 0xf8, 0x48, 0x46, 0xad, 0x23, 0xd8, 0xeb, 0x31, 0x3b, 0x60, - 0x27, 0x11, 0x11, 0x26, 0xcf, 0x43, 0x42, 0x19, 0x32, 0x20, 0xe3, 0x3a, 0xa6, 0xb6, 0xaf, 0x1d, - 0x14, 0x1f, 0x15, 0x26, 0x7f, 0xdd, 0xc9, 0x9c, 0x36, 0x71, 0xc6, 0x75, 0xac, 0x9f, 0x35, 0x30, - 0x4e, 0x02, 0x62, 0x33, 0xb2, 0xee, 0x12, 0x74, 0x07, 0x4a, 0x17, 0xa1, 0xe7, 0x8c, 0xc8, 0xf9, - 0xd8, 0x66, 0xcf, 0xcc, 0x0c, 0x07, 0x60, 0x90, 0xa1, 0xae, 0xcd, 0x9e, 0xa1, 0x0a, 0xe4, 0x29, - 0x73, 0x5c, 0xcf, 0xcc, 0x8a, 0x4f, 0x72, 0x82, 0x0c, 0x28, 0x50, 0xe6, 0xf8, 0x21, 0x33, 0x73, - 0x22, 0xac, 0x66, 0x2a, 0x4e, 0x82, 0xc0, 0xcc, 0x4f, 0xe3, 0x24, 0x08, 0xac, 0xc7, 0xf0, 0x5e, - 0x42, 0x18, 0x1d, 0xfb, 0x1e, 0x25, 0xe8, 0x0b, 0x28, 0x4e, 0x4f, 0x4a, 0x08, 0x2c, 0x35, 0xcc, - 0xfa, 0xc2, 0xd9, 0xd5, 0x67, 0x8b, 0x66, 0x50, 0xeb, 0x18, 0x8c, 0x26, 0x19, 0x91, 0xf5, 0x73, - 0xb5, 0x0e, 0x61, 0xef, 0xcc, 0xa5, 0xb3, 0xe3, 0xa4, 0xd1, 0x82, 0x0a, 0xe4, 0xfd, 0x17, 0x72, - 0xfb, 0x2c, 0xcf, 0x51, 0x4c, 0x2c, 0x0c, 0x46, 0x1c, 0xae, 0x24, 0x7f, 0x09, 0x30, 0xd5, 0x41, - 0xc5, 0xa2, 0x55, 0x9a, 0xe7, 0xb0, 0xd6, 0x6f, 0x1a, 0xec, 0x8a, 0x9a, 0x76, 0x03, 0x7f, 0x40, - 0xe8, 0x54, 0xc1, 0x5d, 0xb8, 0x35, 0x45, 0x9d, 0x47, 0xe2, 0x71, 0x69, 0x1a, 0x3b, 0x75, 0xd0, - 0x31, 0x6c, 0x8f, 0xe5, 0x22, 0x51, 0xa5, 0x52, 0xc3, 0x88, 0xed, 0x18, 0x51, 0x46, 0xb0, 0xb7, - 0x54, 0xba, 0x6f, 0xa0, 0xb2, 0xa8, 0x58, 0x1d, 0xc2, 0x9c, 0x1e, 0x6d, 0x2d, 0x3d, 0x16, 0x85, - 0xe2, 0xf4, 0x54, 0x36, 0xbf, 0x90, 0x87, 0x5c, 0xa7, 0xcd, 0x42, 0x2a, 0xf4, 0xdf, 0x6e, 0xec, - 0xc5, 0xb6, 0xed, 0x89, 0x8f, 0x58, 0x81, 0xac, 0x3f, 0x34, 0xd8, 0x56, 0x4a, 0x96, 0xee, 0x59, - 0x86, 0xec, 0xd8, 0x75, 0xc4, 0x5e, 0x59, 0xcc, 0x87, 0x08, 0x41, 0xce, 0x0e, 0x86, 0xd4, 0xcc, - 0x8a, 0x0b, 0x21, 0xc6, 0x1c, 0x45, 0xbc, 0x2b, 0x33, 0x27, 0x42, 0x7c, 0x88, 0x3e, 0x86, 0x5c, - 0x48, 0x89, 0x3c, 0xb0, 0x52, 0x63, 0x37, 0x26, 0xa4, 0x4f, 0x49, 0x80, 0x05, 0x80, 0x2f, 0x1d, - 0xbc, 0x70, 0xcc, 0x82, 0x48, 0x86, 0x0f, 0x51, 0x15, 0x76, 0x18, 0x09, 0x2e, 0x5d, 0xcf, 0x1e, - 0x99, 0xdb, 0xfb, 0xda, 0xc1, 0x0e, 0x9e, 0xce, 0xf9, 0x11, 0x90, 0x97, 0x2e, 0x3b, 0x57, 0x69, - 0xee, 0xec, 0x6b, 0x07, 0x3a, 0x06, 0x1e, 0x92, 0xb9, 0x59, 0x18, 0x72, 0x7d, 0x45, 0x1b, 0xaa, - 0x84, 0x74, 0xcc, 0x87, 0x3c, 0x32, 0x54, 0x99, 0xe8, 0x98, 0x0f, 0xd1, 0x03, 0xb8, 0x6d, 0x3b, - 0x8e, 0xcb, 0x8d, 0xc9, 0x1e, 0xb5, 0x5d, 0x47, 0xe6, 0xa4, 0xe3, 0x58, 0xd4, 0x3a, 0x84, 0xdd, - 0x36, 0x59, 0xdf, 0x6a, 0x3a, 0x50, 0x59, 0x84, 0xbf, 0xe1, 0xdf, 0xfc, 0x1c, 0x8c, 0xfe, 0xd8, - 0x49, 0x73, 0xae, 0x0d, 0x19, 0x6f, 0xbc, 0x48, 0xdc, 0x5e, 0xbb, 0x76, 0x48, 0xd7, 0xf7, 0x8f, - 0x63, 0x30, 0x30, 0xa1, 0xe1, 0xe5, 0xfa, 0x2b, 0x7e, 0x84, 0x77, 0xdb, 0x24, 0xfe, 0xaf, 0x6f, - 0x9a, 0xd0, 0x87, 0x00, 0xea, 0x4f, 0x3a, 0x57, 0x25, 0x2e, 0xe2, 0xa2, 0x8a, 0x9c, 0x3a, 0xd6, - 0xd7, 0x80, 0xe6, 0xf7, 0xda, 0xf8, 0x2f, 0xfd, 0x45, 0x83, 0x4a, 0xcf, 0x1d, 0x7a, 0xf6, 0xe8, - 0x2d, 0xe9, 0xfe, 0xef, 0xc6, 0xc5, 0xad, 0x48, 0x28, 0x10, 0xce, 0xa5, 0x63, 0x35, 0xb3, 0x5e, - 0x69, 0x50, 0x91, 0x9e, 0xff, 0x7f, 0x49, 0xe3, 0xf7, 0x9e, 0x3f, 0x0a, 0x2a, 0x4e, 0xde, 0x54, - 0x81, 0xf5, 0xad, 0x7c, 0x93, 0xe6, 0xf8, 0x54, 0xe1, 0x3e, 0x87, 0xa8, 0xb6, 0x24, 0x7a, 0x62, - 0x96, 0x89, 0x9b, 0x01, 0x3f, 0xf9, 0x0a, 0x0a, 0xd2, 0x23, 0x50, 0x09, 0xb6, 0x4f, 0x70, 0xeb, - 0xe1, 0x93, 0x56, 0xb3, 0xbc, 0xc5, 0x27, 0xb8, 0xdf, 0xe9, 0x9c, 0x76, 0xda, 0x65, 0x8d, 0x4f, - 0x7a, 0x4f, 0xbe, 0xeb, 0x76, 0x5b, 0xcd, 0x72, 0x06, 0x01, 0x14, 0xba, 0x0f, 0xfb, 0xbd, 0x56, - 0xb3, 0x9c, 0x6d, 0xfc, 0xba, 0x03, 0xe5, 0x56, 0xd4, 0xc5, 0xf4, 0x48, 0x70, 0xe5, 0x0e, 0x08, - 0x7a, 0x0a, 0x05, 0xf9, 0x72, 0xa3, 0xfb, 0xf1, 0x7c, 0x52, 0x3b, 0x8d, 0xea, 0x83, 0x9b, 0x60, - 0x2a, 0xc1, 0x16, 0xe4, 0xc5, 0xbb, 0x82, 0xee, 0x25, 0x0d, 0x3c, 0xd9, 0xf3, 0x54, 0x8d, 0xba, - 0x6c, 0xa0, 0xea, 0x51, 0x03, 0x55, 0x6f, 0xf1, 0x06, 0x0a, 0xb5, 0xa1, 0x20, 0x8d, 0x23, 0xa1, - 0x2f, 0xdd, 0x4f, 0x96, 0x12, 0xb5, 0x20, 0x2f, 0xec, 0x20, 0xa1, 0x27, 0xd5, 0x24, 0x56, 0xe9, - 0x91, 0x26, 0x91, 0xd0, 0x93, 0xee, 0x1d, 0xab, 0x88, 0xe4, 0x5d, 0x4f, 0x10, 0xa5, 0xb7, 0x3d, - 0x4b, 0x89, 0x3a, 0x90, 0x6d, 0x13, 0x86, 0xac, 0x18, 0x4b, 0x8a, 0xdb, 0x57, 0x3f, 0x5a, 0x89, - 0x51, 0x85, 0xeb, 0x41, 0x8e, 0x5f, 0xd9, 0xc4, 0x39, 0xa5, 0xf6, 0x56, 0xd5, 0xfb, 0x37, 0xa0, - 0x14, 0xe9, 0x53, 0xb8, 0x35, 0xdf, 0x65, 0x24, 0xd4, 0xa6, 0x34, 0x4d, 0x09, 0xb5, 0xa9, 0x6d, - 0xca, 0x63, 0x80, 0x99, 0x2d, 0xa2, 0xfd, 0x64, 0x82, 0x31, 0xd2, 0xbb, 0x2b, 0x10, 0x8a, 0xf2, - 0x0c, 0xf4, 0x05, 0x83, 0x44, 0x09, 0x21, 0x29, 0xf6, 0xb9, 0xb4, 0x3c, 0x67, 0xa0, 0x2f, 0x78, - 0x5a, 0x82, 0x2d, 0xcd, 0xf1, 0x96, 0xb2, 0x7d, 0x0f, 0xfa, 0x82, 0x9f, 0x24, 0xd8, 0xd2, 0xdc, - 0xab, 0x7a, 0x6f, 0x35, 0x48, 0xe6, 0xfd, 0xe8, 0x83, 0xd7, 0xd7, 0xb5, 0xad, 0x3f, 0xaf, 0x6b, - 0x5b, 0xff, 0x5c, 0xd7, 0xb4, 0x57, 0x93, 0x9a, 0xf6, 0x7a, 0x52, 0xd3, 0x7e, 0x9f, 0xd4, 0xb4, - 0xbf, 0x27, 0x35, 0xed, 0xa2, 0x20, 0x94, 0x7c, 0xf6, 0x6f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x47, - 0xce, 0x74, 0xfe, 0x09, 0x0d, 0x00, 0x00, + 0x14, 0xce, 0xfa, 0x37, 0x3e, 0xee, 0x16, 0x33, 0x71, 0x96, 0x95, 0x01, 0x37, 0x5d, 0xda, 0x12, + 0x21, 0xc5, 0x09, 0x06, 0x21, 0x24, 0xae, 0xda, 0xd8, 0x98, 0x48, 0xc1, 0xb8, 0xe3, 0x5a, 0x95, + 0xb8, 0x89, 0x36, 0xde, 0xc1, 0x5d, 0xe4, 0xec, 0xba, 0x3b, 0xb3, 0x69, 0xb9, 0xeb, 0x63, 0x70, + 0xc3, 0x43, 0xf0, 0x16, 0x15, 0x57, 0x70, 0xc7, 0x15, 0x22, 0x7e, 0x02, 0x1e, 0x01, 0xcd, 0xcf, + 0xfa, 0x67, 0x77, 0xed, 0x98, 0x14, 0xc4, 0xdd, 0xcc, 0xd9, 0xef, 0x9c, 0xf9, 0xce, 0x39, 0xb3, + 0xdf, 0x1c, 0x78, 0x8b, 0xbc, 0x24, 0xc3, 0x90, 0xb9, 0xbe, 0xd7, 0x98, 0x04, 0x3e, 0xf3, 0x91, + 0x3e, 0xf4, 0x3d, 0x66, 0xbb, 0x1e, 0x09, 0x9c, 0xc6, 0xe5, 0xc7, 0xb5, 0x77, 0x47, 0xbe, 0x3f, + 0x1a, 0x93, 0x43, 0xf1, 0xf1, 0x3c, 0xfc, 0xee, 0x90, 0x5c, 0x4c, 0xd8, 0x0f, 0x12, 0x5b, 0xab, + 0x8e, 0xfc, 0x91, 0x2f, 0x96, 0x87, 0x7c, 0x25, 0xad, 0xd6, 0x21, 0xec, 0xf6, 0x99, 0x1d, 0xb0, + 0xe3, 0x28, 0x10, 0x26, 0xcf, 0x43, 0x42, 0x19, 0x32, 0x20, 0xe3, 0x3a, 0xa6, 0xb6, 0xa7, 0xed, + 0x97, 0x1e, 0x15, 0xa6, 0x7f, 0xdc, 0xc9, 0x9c, 0xb4, 0x70, 0xc6, 0x75, 0xac, 0x9f, 0x35, 0x30, + 0x8e, 0x03, 0x62, 0x33, 0xb2, 0xa9, 0x0b, 0xba, 0x03, 0xe5, 0xf3, 0xd0, 0x73, 0xc6, 0xe4, 0x6c, + 0x62, 0xb3, 0x67, 0x66, 0x86, 0x03, 0x30, 0x48, 0x53, 0xcf, 0x66, 0xcf, 0x90, 0x09, 0xc5, 0xa1, + 0xef, 0x51, 0x7f, 0x4c, 0xcc, 0xec, 0x9e, 0xb6, 0xbf, 0x8d, 0xa3, 0x2d, 0xaa, 0x42, 0x9e, 0x32, + 0xc7, 0xf5, 0xcc, 0x9c, 0x70, 0x92, 0x1b, 0x64, 0x40, 0x81, 0x32, 0xc7, 0x0f, 0x99, 0x99, 0x17, + 0x66, 0xb5, 0x53, 0x76, 0x12, 0x04, 0x66, 0x61, 0x66, 0x27, 0x41, 0x60, 0x3d, 0x86, 0x77, 0x12, + 0x94, 0xe9, 0xc4, 0xf7, 0x28, 0x41, 0x9f, 0x41, 0x69, 0x56, 0x43, 0x41, 0xbd, 0xdc, 0x34, 0x1b, + 0x4b, 0x55, 0x6d, 0xcc, 0x9d, 0xe6, 0x50, 0xeb, 0x08, 0x8c, 0x16, 0x19, 0x93, 0xcd, 0xab, 0x60, + 0x1d, 0xc0, 0xee, 0xa9, 0x4b, 0xe7, 0x85, 0xa6, 0x91, 0x43, 0x15, 0xf2, 0xfe, 0x0b, 0x79, 0x7c, + 0x96, 0xe7, 0x28, 0x36, 0x16, 0x06, 0x23, 0x0e, 0x57, 0x94, 0x3f, 0x07, 0x98, 0xf1, 0xa0, 0xc2, + 0x69, 0x1d, 0xe7, 0x05, 0xac, 0xf5, 0x8b, 0x06, 0x3b, 0xa2, 0xdb, 0xbd, 0xc0, 0x1f, 0x12, 0x3a, + 0x63, 0x70, 0x17, 0x6e, 0xcd, 0x50, 0x67, 0x11, 0x79, 0x5c, 0x9e, 0xd9, 0x4e, 0x1c, 0x74, 0x04, + 0xc5, 0x89, 0x74, 0x12, 0xfd, 0x2b, 0x37, 0x8d, 0xd8, 0x89, 0x51, 0xc8, 0x08, 0xf6, 0x9f, 0x37, + 0xf5, 0x2b, 0xa8, 0x2e, 0xe7, 0xa2, 0xca, 0xb3, 0xc0, 0x54, 0xdb, 0x88, 0xa9, 0x45, 0xa1, 0x34, + 0xab, 0xd7, 0xcd, 0x2f, 0xf1, 0x01, 0xe7, 0x69, 0xb3, 0x90, 0x8a, 0xb4, 0x6e, 0x37, 0x77, 0x63, + 0xc7, 0xf6, 0xc5, 0x47, 0xac, 0x40, 0xd6, 0x6f, 0x1a, 0x14, 0x15, 0x93, 0x95, 0x67, 0x56, 0x20, + 0x3b, 0x71, 0x1d, 0x71, 0x56, 0x16, 0xf3, 0x25, 0x42, 0x90, 0xb3, 0x83, 0x11, 0x35, 0xb3, 0xe2, + 0xaa, 0x88, 0x35, 0x47, 0x11, 0xef, 0xd2, 0xcc, 0x09, 0x13, 0x5f, 0xa2, 0x0f, 0x21, 0x17, 0x52, + 0x12, 0x88, 0x42, 0x96, 0x9b, 0x3b, 0x31, 0x22, 0x03, 0x4a, 0x02, 0x2c, 0x00, 0xdc, 0x75, 0xf8, + 0xc2, 0x51, 0x85, 0xe5, 0x4b, 0x54, 0x83, 0x6d, 0x46, 0x82, 0x0b, 0xd7, 0xb3, 0xc7, 0x66, 0x51, + 0xb4, 0x6d, 0xb6, 0xe7, 0x25, 0x20, 0x2f, 0x5d, 0x76, 0xa6, 0xd2, 0xdc, 0xde, 0xd3, 0xf6, 0x75, + 0x0c, 0xdc, 0x24, 0x73, 0xb3, 0x30, 0xe4, 0x06, 0x2a, 0x6c, 0xa8, 0x12, 0xd2, 0x31, 0x5f, 0x72, + 0xcb, 0x48, 0x65, 0xa2, 0x63, 0xbe, 0x44, 0x0f, 0xe0, 0xb6, 0xed, 0x38, 0x2e, 0x17, 0x33, 0x7b, + 0xdc, 0x71, 0x1d, 0x99, 0x93, 0x8e, 0x63, 0x56, 0xeb, 0x00, 0x76, 0x3a, 0x64, 0x73, 0x79, 0xea, + 0x42, 0x75, 0x19, 0xfe, 0x86, 0xff, 0xf9, 0x73, 0x30, 0x06, 0x13, 0x27, 0x4d, 0xed, 0x6e, 0x18, + 0xf1, 0xda, 0x8b, 0xc4, 0x25, 0xb9, 0x67, 0x87, 0x74, 0x73, 0x65, 0x39, 0x02, 0x03, 0x13, 0x1a, + 0x5e, 0x6c, 0xee, 0xf1, 0x3d, 0xbc, 0xdd, 0x21, 0x71, 0x15, 0xb8, 0x69, 0x42, 0xef, 0x03, 0xa8, + 0x3f, 0xe9, 0x4c, 0xb5, 0xb8, 0x84, 0x4b, 0xca, 0x72, 0xe2, 0x58, 0x5f, 0x02, 0x5a, 0x3c, 0xeb, + 0xc6, 0x7f, 0xe9, 0x8f, 0x1a, 0x54, 0xfb, 0xee, 0xc8, 0xb3, 0xc7, 0xff, 0x12, 0xef, 0x7f, 0x2e, + 0x69, 0x5c, 0x8a, 0x04, 0x03, 0xa1, 0x68, 0x3a, 0x56, 0x3b, 0xeb, 0x95, 0x06, 0x55, 0xf9, 0x1a, + 0xfc, 0x5f, 0xd4, 0xf8, 0xbd, 0xe7, 0xcf, 0x85, 0xb2, 0x93, 0x37, 0x65, 0x60, 0x7d, 0x2d, 0x5f, + 0xab, 0x85, 0x78, 0xaa, 0x71, 0x9f, 0x42, 0xd4, 0x5b, 0x12, 0x3d, 0x3e, 0xab, 0xc8, 0xcd, 0x81, + 0x1f, 0x7d, 0x01, 0x05, 0xa9, 0x11, 0xa8, 0x0c, 0xc5, 0x63, 0xdc, 0x7e, 0xf8, 0xa4, 0xdd, 0xaa, + 0x6c, 0xf1, 0x0d, 0x1e, 0x74, 0xbb, 0x27, 0xdd, 0x4e, 0x45, 0xe3, 0x9b, 0xfe, 0x93, 0x6f, 0x7a, + 0xbd, 0x76, 0xab, 0x92, 0x41, 0x00, 0x85, 0xde, 0xc3, 0x41, 0xbf, 0xdd, 0xaa, 0x64, 0x9b, 0x3f, + 0x6d, 0x43, 0xa5, 0x1d, 0x4d, 0x3e, 0x7d, 0x12, 0x5c, 0xba, 0x43, 0x82, 0x9e, 0x42, 0x41, 0xbe, + 0xe9, 0xe8, 0x7e, 0x3c, 0x9f, 0xd4, 0xe9, 0xa4, 0xf6, 0xe0, 0x3a, 0x98, 0x4a, 0xb0, 0x0d, 0x79, + 0xf1, 0xae, 0xa0, 0x7b, 0x49, 0x01, 0x4f, 0xce, 0x49, 0x35, 0xa3, 0x21, 0x87, 0xae, 0x46, 0x34, + 0x74, 0x35, 0xda, 0x7c, 0xe8, 0x42, 0x1d, 0x28, 0x48, 0xe1, 0x48, 0xf0, 0x4b, 0xd7, 0x93, 0x95, + 0x81, 0xda, 0x90, 0x17, 0x72, 0x90, 0xe0, 0x93, 0x2a, 0x12, 0xeb, 0xf8, 0x48, 0x91, 0x48, 0xf0, + 0x49, 0xd7, 0x8e, 0x75, 0x81, 0xe4, 0x5d, 0x4f, 0x04, 0x4a, 0x1f, 0x88, 0x56, 0x06, 0xea, 0x42, + 0xb6, 0x43, 0x18, 0xb2, 0x62, 0x51, 0x52, 0xd4, 0xbe, 0xf6, 0xc1, 0x5a, 0x8c, 0x6a, 0x5c, 0x1f, + 0x72, 0xfc, 0xca, 0x26, 0xea, 0x94, 0x3a, 0x75, 0xd5, 0xee, 0x5f, 0x83, 0x52, 0x41, 0x9f, 0xc2, + 0xad, 0xc5, 0x29, 0x23, 0xc1, 0x36, 0x65, 0x9c, 0x4a, 0xb0, 0x4d, 0x1d, 0x53, 0x1e, 0x03, 0xcc, + 0x65, 0x11, 0xed, 0x25, 0x13, 0x8c, 0x05, 0xbd, 0xbb, 0x06, 0xa1, 0x42, 0x9e, 0x82, 0xbe, 0x24, + 0x90, 0x28, 0x41, 0x24, 0x45, 0x3e, 0x57, 0xb6, 0xe7, 0x14, 0xf4, 0x25, 0x4d, 0x4b, 0x44, 0x4b, + 0x53, 0xbc, 0x95, 0xd1, 0xbe, 0x05, 0x7d, 0x49, 0x4f, 0x12, 0xd1, 0xd2, 0xd4, 0xab, 0x76, 0x6f, + 0x3d, 0x48, 0xe6, 0xfd, 0xe8, 0xbd, 0xd7, 0x57, 0xf5, 0xad, 0xdf, 0xaf, 0xea, 0x5b, 0x7f, 0x5d, + 0xd5, 0xb5, 0x57, 0xd3, 0xba, 0xf6, 0x7a, 0x5a, 0xd7, 0x7e, 0x9d, 0xd6, 0xb5, 0x3f, 0xa7, 0x75, + 0xed, 0xbc, 0x20, 0x98, 0x7c, 0xf2, 0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0xeb, 0x07, 0xf1, 0x5d, + 0x3d, 0x0d, 0x00, 0x00, } diff --git a/api/execution/execution.proto b/api/execution/execution.proto index d2d942e..2ee5e76 100644 --- a/api/execution/execution.proto +++ b/api/execution/execution.proto @@ -30,9 +30,10 @@ message StartContainerRequest { message CreateContainerRequest { string id = 1 [(gogoproto.customname) = "ID"]; string bundle_path = 2; - string stdin = 3; - string stdout = 4; - string stderr = 5; + bool console = 3; + string stdin = 4; + string stdout = 5; + string stderr = 6; } message CreateContainerResponse { @@ -54,9 +55,10 @@ message ListContainersResponse { message StartProcessRequest { string container_id = 1; Process process = 2; - string stdin = 3; - string stdout = 4; - string stderr = 5; + bool console = 3; + string stdin = 4; + string stdout = 5; + string stderr = 6; } message StartProcessResponse { diff --git a/cmd/ctr/exec.go b/cmd/ctr/exec.go new file mode 100644 index 0000000..4675403 --- /dev/null +++ b/cmd/ctr/exec.go @@ -0,0 +1,87 @@ +package main + +import ( + "os" + "path/filepath" + + gocontext "context" + + "github.com/docker/containerd/api/execution" + "github.com/urfave/cli" +) + +var execCommand = cli.Command{ + Name: "exec", + Usage: "exec a new process in a running container", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "id, i", + Usage: "target container id", + }, + cli.StringFlag{ + Name: "cwd, c", + Usage: "current working directory for the process", + }, + cli.BoolFlag{ + Name: "tty, t", + Usage: "create a terminal for the process", + }, + cli.StringSliceFlag{ + Name: "env, e", + Value: &cli.StringSlice{}, + Usage: "environment variables for the process", + }, + }, + Action: func(context *cli.Context) error { + executionService, err := getExecutionService(context) + if err != nil { + return err + } + + id := context.String("id") + tmpDir, err := getTempDir(id) + if err != nil { + return err + } + defer os.RemoveAll(tmpDir) + + sOpts := &execution.StartProcessRequest{ + ContainerId: id, + Process: &execution.Process{ + Cwd: context.String("cwd"), + Terminal: context.Bool("tty"), + Args: context.Args(), + Env: context.StringSlice("env"), + }, + Stdin: filepath.Join(tmpDir, "stdin"), + Stdout: filepath.Join(tmpDir, "stdout"), + Stderr: filepath.Join(tmpDir, "stderr"), + Console: context.Bool("tty"), + } + + fwg, err := prepareStdio(sOpts.Stdin, sOpts.Stdout, sOpts.Stderr) + if err != nil { + return err + } + + sr, err := executionService.StartProcess(gocontext.Background(), sOpts) + if err != nil { + return err + } + + _, err = executionService.DeleteProcess(gocontext.Background(), &execution.DeleteProcessRequest{ + Container: &execution.Container{ + ID: id, + }, + Process: sr.Process, + }) + if err != nil { + return err + } + + // Ensure we read all io + fwg.Wait() + + return nil + }, +} diff --git a/cmd/ctr/main.go b/cmd/ctr/main.go index cc142ac..bbddb5b 100644 --- a/cmd/ctr/main.go +++ b/cmd/ctr/main.go @@ -14,12 +14,12 @@ func main() { app.Name = "ctr" app.Version = containerd.Version app.Usage = ` - __ + __ _____/ /______ / ___/ __/ ___/ -/ /__/ /_/ / -\___/\__/_/ - +/ /__/ /_/ / +\___/\__/_/ + containerd client ` app.Flags = []cli.Flag{ @@ -35,6 +35,7 @@ containerd client } app.Commands = []cli.Command{ runCommand, + execCommand, } app.Before = func(context *cli.Context) error { if context.GlobalBool("debug") { diff --git a/cmd/ctr/run.go b/cmd/ctr/run.go index 17ed155..c585a42 100644 --- a/cmd/ctr/run.go +++ b/cmd/ctr/run.go @@ -2,23 +2,13 @@ package main import ( "fmt" - "io" - "io/ioutil" - "log" - "net" "os" "path/filepath" - "sync" - "syscall" "time" - "google.golang.org/grpc" - "google.golang.org/grpc/grpclog" - gocontext "context" "github.com/docker/containerd/api/execution" - "github.com/tonistiigi/fifo" "github.com/urfave/cli" ) @@ -47,6 +37,10 @@ var runCommand = cli.Command{ Name: "bundle, b", Usage: "path to the container's bundle", }, + cli.BoolFlag{ + Name: "tty, t", + Usage: "allocate a TTY for the container", + }, }, Action: func(context *cli.Context) error { // var config runConfig @@ -71,6 +65,7 @@ var runCommand = cli.Command{ crOpts := &execution.CreateContainerRequest{ ID: id, BundlePath: context.String("bundle"), + Console: context.Bool("tty"), Stdin: filepath.Join(tmpDir, "stdin"), Stdout: filepath.Join(tmpDir, "stdout"), Stderr: filepath.Join(tmpDir, "stderr"), @@ -118,103 +113,3 @@ var runCommand = cli.Command{ return nil }, } - -var grpcConn *grpc.ClientConn - -func prepareStdio(in, out, err string) (*sync.WaitGroup, error) { - var ( - wg sync.WaitGroup - - dst io.Writer - src io.Reader - close func() - ) - - for _, f := range []struct { - name string - flags int - src bool - reader io.Reader - writer io.Writer - }{ - {in, syscall.O_WRONLY | syscall.O_CREAT | syscall.O_NONBLOCK, false, os.Stdin, nil}, - {out, syscall.O_RDONLY | syscall.O_CREAT | syscall.O_NONBLOCK, true, nil, os.Stdout}, - {err, syscall.O_RDONLY | syscall.O_CREAT | syscall.O_NONBLOCK, true, nil, os.Stderr}, - } { - ff, err := fifo.OpenFifo(gocontext.Background(), f.name, f.flags, 0700) - if err != nil { - return nil, err - } - defer func(c io.Closer) { - if err != nil { - c.Close() - } - }(ff) - - if f.src { - src = ff - dst = f.writer - close = func() { - ff.Close() - wg.Done() - } - wg.Add(1) - } else { - src = f.reader - dst = ff - close = func() { ff.Close() } - } - - go func(dst io.Writer, src io.Reader, close func()) { - io.Copy(dst, src) - close() - }(dst, src, close) - } - - return &wg, nil -} - -func getGRPCConnection(context *cli.Context) (*grpc.ClientConn, error) { - if grpcConn != nil { - return grpcConn, nil - } - - bindSocket := context.GlobalString("socket") - // reset the logger for grpc to log to dev/null so that it does not mess with our stdio - grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) - dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithTimeout(100 * time.Second)} - dialOpts = append(dialOpts, - grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { - return net.DialTimeout("unix", bindSocket, timeout) - }, - )) - - conn, err := grpc.Dial(fmt.Sprintf("unix://%s", bindSocket), dialOpts...) - if err != nil { - return nil, err - } - - grpcConn = conn - return grpcConn, nil -} - -func getExecutionService(context *cli.Context) (execution.ExecutionServiceClient, error) { - conn, err := getGRPCConnection(context) - if err != nil { - return nil, err - } - return execution.NewExecutionServiceClient(conn), nil -} - -func getTempDir(id string) (string, error) { - err := os.MkdirAll(filepath.Join(os.TempDir(), "ctr"), 0700) - if err != nil { - return "", err - } - - tmpDir, err := ioutil.TempDir(filepath.Join(os.TempDir(), "ctr"), fmt.Sprintf("%s-", id)) - if err != nil { - return "", err - } - return tmpDir, nil -} diff --git a/cmd/ctr/utils.go b/cmd/ctr/utils.go new file mode 100644 index 0000000..8e23c2f --- /dev/null +++ b/cmd/ctr/utils.go @@ -0,0 +1,122 @@ +package main + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "net" + "os" + "path/filepath" + "sync" + "syscall" + "time" + + gocontext "context" + + "github.com/docker/containerd/api/execution" + "github.com/tonistiigi/fifo" + "github.com/urfave/cli" + "google.golang.org/grpc" + "google.golang.org/grpc/grpclog" +) + +var grpcConn *grpc.ClientConn + +func prepareStdio(in, out, err string) (*sync.WaitGroup, error) { + var ( + wg sync.WaitGroup + + dst io.Writer + src io.Reader + close func() + ) + + for _, f := range []struct { + name string + flags int + src bool + reader io.Reader + writer io.Writer + }{ + {in, syscall.O_WRONLY | syscall.O_CREAT | syscall.O_NONBLOCK, false, os.Stdin, nil}, + {out, syscall.O_RDONLY | syscall.O_CREAT | syscall.O_NONBLOCK, true, nil, os.Stdout}, + {err, syscall.O_RDONLY | syscall.O_CREAT | syscall.O_NONBLOCK, true, nil, os.Stderr}, + } { + ff, err := fifo.OpenFifo(gocontext.Background(), f.name, f.flags, 0700) + if err != nil { + return nil, err + } + defer func(c io.Closer) { + if err != nil { + c.Close() + } + }(ff) + + if f.src { + src = ff + dst = f.writer + close = func() { + ff.Close() + wg.Done() + } + wg.Add(1) + } else { + src = f.reader + dst = ff + close = func() { ff.Close() } + } + + go func(dst io.Writer, src io.Reader, close func()) { + io.Copy(dst, src) + close() + }(dst, src, close) + } + + return &wg, nil +} + +func getGRPCConnection(context *cli.Context) (*grpc.ClientConn, error) { + if grpcConn != nil { + return grpcConn, nil + } + + bindSocket := context.GlobalString("socket") + // reset the logger for grpc to log to dev/null so that it does not mess with our stdio + grpclog.SetLogger(log.New(ioutil.Discard, "", log.LstdFlags)) + dialOpts := []grpc.DialOption{grpc.WithInsecure(), grpc.WithTimeout(100 * time.Second)} + dialOpts = append(dialOpts, + grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { + return net.DialTimeout("unix", bindSocket, timeout) + }, + )) + + conn, err := grpc.Dial(fmt.Sprintf("unix://%s", bindSocket), dialOpts...) + if err != nil { + return nil, err + } + + grpcConn = conn + return grpcConn, nil +} + +func getExecutionService(context *cli.Context) (execution.ExecutionServiceClient, error) { + conn, err := getGRPCConnection(context) + if err != nil { + return nil, err + } + return execution.NewExecutionServiceClient(conn), nil +} + +func getTempDir(id string) (string, error) { + err := os.MkdirAll(filepath.Join(os.TempDir(), "ctr"), 0700) + if err != nil { + return "", err + } + + tmpDir, err := ioutil.TempDir(filepath.Join(os.TempDir(), "ctr"), fmt.Sprintf("%s-", id)) + if err != nil { + return "", err + } + return tmpDir, nil +} diff --git a/execution/executor.go b/execution/executor.go index 963d5db..810bf08 100644 --- a/execution/executor.go +++ b/execution/executor.go @@ -7,17 +7,19 @@ import ( ) type CreateOpts struct { - Bundle string - Stdin string - Stdout string - Stderr string + Bundle string + Console bool + Stdin string + Stdout string + Stderr string } -type CreateProcessOpts struct { - Spec specs.Process - Stdin string - Stdout string - Stderr string +type StartProcessOpts struct { + Spec specs.Process + Console bool + Stdin string + Stdout string + Stderr string } type Executor interface { @@ -30,7 +32,7 @@ type Executor interface { Delete(*Container) error Start(*Container) error - StartProcess(*Container, CreateProcessOpts) (Process, error) + StartProcess(*Container, StartProcessOpts) (Process, error) SignalProcess(*Container, string, os.Signal) error DeleteProcess(*Container, string) error } diff --git a/execution/executors/oci/console.go b/execution/executors/oci/console.go new file mode 100644 index 0000000..59f92f4 --- /dev/null +++ b/execution/executors/oci/console.go @@ -0,0 +1,54 @@ +package oci + +import ( + "fmt" + "os" + "syscall" + "unsafe" +) + +// NewConsole returns an initialized console that can be used within a container by copying bytes +// from the master side to the slave that is attached as the tty for the container's init process. +func newConsole(uid, gid int) (*os.File, string, error) { + master, err := os.OpenFile("/dev/ptmx", syscall.O_RDWR|syscall.O_NOCTTY|syscall.O_CLOEXEC, 0) + if err != nil { + return nil, "", err + } + console, err := ptsname(master) + if err != nil { + return nil, "", err + } + if err := unlockpt(master); err != nil { + return nil, "", err + } + if err := os.Chmod(console, 0600); err != nil { + return nil, "", err + } + if err := os.Chown(console, uid, gid); err != nil { + return nil, "", err + } + return master, console, nil +} + +func ioctl(fd uintptr, flag, data uintptr) error { + if _, _, err := syscall.Syscall(syscall.SYS_IOCTL, fd, flag, data); err != 0 { + return err + } + return nil +} + +// unlockpt unlocks the slave pseudoterminal device corresponding to the master pseudoterminal referred to by f. +// unlockpt should be called before opening the slave side of a pty. +func unlockpt(f *os.File) error { + var u int32 + return ioctl(f.Fd(), syscall.TIOCSPTLCK, uintptr(unsafe.Pointer(&u))) +} + +// ptsname retrieves the name of the first available pts for the given master. +func ptsname(f *os.File) (string, error) { + var n int32 + if err := ioctl(f.Fd(), syscall.TIOCGPTN, uintptr(unsafe.Pointer(&n))); err != nil { + return "", err + } + return fmt.Sprintf("/dev/pts/%d", n), nil +} diff --git a/execution/executors/oci/oci.go b/execution/executors/oci/oci.go index f11f27b..b8b52f7 100644 --- a/execution/executors/oci/oci.go +++ b/execution/executors/oci/oci.go @@ -3,6 +3,7 @@ package oci import ( "errors" "fmt" + "io" "os" "path/filepath" "syscall" @@ -62,16 +63,43 @@ func getRuncIO(stdin, stdout, stderr string) (io runc.IO, err error) { return } +func setupConsole(rio runc.IO) (*os.File, string, error) { + master, console, err := newConsole(0, 0) + if err != nil { + return nil, "", err + } + go io.Copy(master, rio.Stdin) + go func() { + io.Copy(rio.Stdout, master) + master.Close() + }() + + return master, console, nil +} + func (r *OCIRuntime) Create(id string, o execution.CreateOpts) (container *execution.Container, err error) { - io, err := getRuncIO(o.Stdin, o.Stdout, o.Stderr) + rio, err := getRuncIO(o.Stdin, o.Stdout, o.Stderr) if err != nil { return nil, err } defer func() { if err != nil { - closeRuncIO(io) + closeRuncIO(rio) } }() + consolePath := "" + if o.Console { + master, console, err := setupConsole(rio) + if err != nil { + return nil, err + } + consolePath = console + defer func() { + if err != nil { + master.Close() + } + }() + } if container, err = execution.NewContainer(r.root, id, o.Bundle, "created"); err != nil { return nil, err @@ -89,7 +117,8 @@ func (r *OCIRuntime) Create(id string, o execution.CreateOpts) (container *execu pidFile := filepath.Join(initDir, "pid") err = r.runc.Create(id, o.Bundle, &runc.CreateOpts{ PidFile: pidFile, - IO: io, + Console: consolePath, + IO: rio, }) if err != nil { return nil, err @@ -112,7 +141,7 @@ func (r *OCIRuntime) Create(id string, o execution.CreateOpts) (container *execu container.AddProcess(process, true) - r.ios[id] = io + r.ios[id] = rio return container, nil } @@ -145,6 +174,10 @@ func (r *OCIRuntime) load(runcC *runc.Container) (*execution.Container, error) { for _, d := range dirs { pid, err := runc.ReadPidFile(filepath.Join(d, "pid")) if err != nil { + if os.IsNotExist(err) { + // Process died in between + continue + } return nil, err } process, err := newProcess(filepath.Base(d), pid) @@ -203,16 +236,29 @@ func (r *OCIRuntime) Resume(c *execution.Container) error { return r.runc.Resume(c.ID()) } -func (r *OCIRuntime) StartProcess(c *execution.Container, o execution.CreateProcessOpts) (p execution.Process, err error) { - io, err := getRuncIO(o.Stdin, o.Stdout, o.Stderr) +func (r *OCIRuntime) StartProcess(c *execution.Container, o execution.StartProcessOpts) (p execution.Process, err error) { + rio, err := getRuncIO(o.Stdin, o.Stdout, o.Stderr) if err != nil { return nil, err } defer func() { if err != nil { - closeRuncIO(io) + closeRuncIO(rio) } }() + consolePath := "" + if o.Console { + master, console, err := setupConsole(rio) + if err != nil { + return nil, err + } + consolePath = console + defer func() { + if err != nil { + master.Close() + } + }() + } processStateDir, err := c.StateDir().NewProcess() if err != nil { @@ -227,8 +273,10 @@ func (r *OCIRuntime) StartProcess(c *execution.Container, o execution.CreateProc pidFile := filepath.Join(processStateDir, "pid") if err := r.runc.ExecProcess(c.ID(), o.Spec, &runc.ExecOpts{ PidFile: pidFile, - Detach: true, - IO: io, + Detach: false, + Console: consolePath, + Cwd: o.Spec.Cwd, + IO: rio, }); err != nil { return nil, err } @@ -244,7 +292,7 @@ func (r *OCIRuntime) StartProcess(c *execution.Container, o execution.CreateProc c.AddProcess(process, false) - r.ios[fmt.Sprintf("%s-%s", c.ID(), process.ID())] = io + r.ios[fmt.Sprintf("%s-%s", c.ID(), process.ID())] = rio return process, nil } diff --git a/execution/service.go b/execution/service.go index b82fd03..6486a4d 100644 --- a/execution/service.go +++ b/execution/service.go @@ -10,7 +10,10 @@ import ( "golang.org/x/net/context" ) -var emptyResponse = &google_protobuf.Empty{} +var ( + emptyResponse = &google_protobuf.Empty{} + ErrProcessNotFound = fmt.Errorf("Process not found") +) func New(executor Executor) (*Service, error) { return &Service{ @@ -28,10 +31,11 @@ func (s *Service) Create(ctx context.Context, r *api.CreateContainerRequest) (*a var err error container, err := s.executor.Create(r.ID, CreateOpts{ - Bundle: r.BundlePath, - Stdin: r.Stdin, - Stdout: r.Stdout, - Stderr: r.Stderr, + Bundle: r.BundlePath, + Console: r.Console, + Stdin: r.Stdin, + Stdout: r.Stdout, + Stderr: r.Stderr, }) if err != nil { return nil, err @@ -112,12 +116,24 @@ func (s *Service) StartProcess(ctx context.Context, r *api.StartProcessRequest) } // TODO: generate spec - var spec specs.Process - process, err := s.executor.StartProcess(container, CreateProcessOpts{ - Spec: spec, - Stdin: r.Stdin, - Stdout: r.Stdout, - Stderr: r.Stderr, + spec := specs.Process{ + Terminal: r.Process.Terminal, + ConsoleSize: specs.Box{ + 80, + 80, + }, + Args: r.Process.Args, + Env: r.Process.Env, + Cwd: r.Process.Cwd, + NoNewPrivileges: true, + } + + process, err := s.executor.StartProcess(container, StartProcessOpts{ + Spec: spec, + Console: r.Console, + Stdin: r.Stdin, + Stdout: r.Stdout, + Stderr: r.Stderr, }) if err != nil { return nil, err @@ -137,7 +153,7 @@ func (s *Service) GetProcess(ctx context.Context, r *api.GetProcessRequest) (*ap } process := container.GetProcess(r.ProcessId) if process == nil { - return nil, fmt.Errorf("Make me a constant! Process not foumd!") + return nil, ErrProcessNotFound } return &api.GetProcessResponse{ Process: toGRPCProcess(process),