diff --git a/Makefile b/Makefile index 35021fd3b..8557d2e1a 100644 --- a/Makefile +++ b/Makefile @@ -116,11 +116,11 @@ AUTHORS: .mailmap .git/HEAD generate: protos @echo "$(WHALE) $@" - @PATH=${ROOTDIR}/bin:${PATH} go generate -x ${PACKAGES} + @PATH="${ROOTDIR}/bin:${PATH}" go generate -x ${PACKAGES} protos: bin/protoc-gen-gogoctrd ## generate protobuf @echo "$(WHALE) $@" - @PATH=${ROOTDIR}/bin:${PATH} protobuild --quiet ${PACKAGES} + @PATH="${ROOTDIR}/bin:${PATH}" protobuild --quiet ${PACKAGES} check-protos: protos ## check if protobufs needs to be generated again @echo "$(WHALE) $@" diff --git a/Protobuild.toml b/Protobuild.toml index 28f310707..005625f38 100644 --- a/Protobuild.toml +++ b/Protobuild.toml @@ -66,6 +66,14 @@ ignore_files = [ "gogoproto/gogo.proto" ] +[[descriptors]] +prefix = "github.com/containerd/containerd/runtime/v2/runhcs/options" +target = "runtime/v2/runhcs/options/next.pb.txt" +ignore_files = [ + "google/protobuf/descriptor.proto", + "gogoproto/gogo.proto" +] + [[descriptors]] prefix = "github.com/containerd/containerd/windows/hcsshimtypes" target = "windows/hcsshimtypes/next.pb.txt" diff --git a/cmd/ctr/commands/run/run_windows.go b/cmd/ctr/commands/run/run_windows.go index ea297aac2..1e2b99a2e 100644 --- a/cmd/ctr/commands/run/run_windows.go +++ b/cmd/ctr/commands/run/run_windows.go @@ -23,6 +23,7 @@ import ( "github.com/containerd/containerd" "github.com/containerd/containerd/cmd/ctr/commands" "github.com/containerd/containerd/oci" + "github.com/containerd/containerd/runtime/v2/runhcs/options" specs "github.com/opencontainers/runtime-spec/specs-go" "github.com/sirupsen/logrus" "github.com/urfave/cli" @@ -100,7 +101,14 @@ func NewContainer(ctx gocontext.Context, client *containerd.Client, context *cli } cOpts = append(cOpts, containerd.WithContainerLabels(commands.LabelArgs(context.StringSlice("label")))) - cOpts = append(cOpts, containerd.WithRuntime(context.String("runtime"), nil)) + runtime := context.String("runtime") + var runtimeOpts interface{} + if runtime == "io.containerd.runhcs.v1" { + runtimeOpts = &options.Options{ + Debug: context.GlobalBool("debug"), + } + } + cOpts = append(cOpts, containerd.WithRuntime(runtime, runtimeOpts)) var s specs.Spec spec = containerd.WithSpec(&s, opts...) diff --git a/runtime/v2/runhcs/options/doc.go b/runtime/v2/runhcs/options/doc.go new file mode 100644 index 000000000..ffff495cb --- /dev/null +++ b/runtime/v2/runhcs/options/doc.go @@ -0,0 +1,17 @@ +/* + Copyright The containerd Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package options diff --git a/runtime/v2/runhcs/options/next.pb.txt b/runtime/v2/runhcs/options/next.pb.txt new file mode 100644 index 000000000..0d8f177f4 --- /dev/null +++ b/runtime/v2/runhcs/options/next.pb.txt @@ -0,0 +1,46 @@ +file { + name: "github.com/containerd/containerd/runtime/v2/runhcs/options/runhcs.proto" + package: "containerd.runhcs.v1" + dependency: "gogoproto/gogo.proto" + message_type { + name: "Options" + field { + name: "debug" + number: 1 + label: LABEL_OPTIONAL + type: TYPE_BOOL + json_name: "debug" + } + field { + name: "debug_type" + number: 2 + label: LABEL_OPTIONAL + type: TYPE_ENUM + type_name: ".containerd.runhcs.v1.Options.DebugType" + json_name: "debugType" + } + field { + name: "registry_root" + number: 3 + label: LABEL_OPTIONAL + type: TYPE_STRING + json_name: "registryRoot" + } + enum_type { + name: "DebugType" + value { + name: "NPIPE" + number: 0 + } + value { + name: "FILE" + number: 1 + } + } + } + options { + go_package: "github.com/containerd/containerd/runtime/v2/runhcs/options;options" + } + weak_dependency: 0 + syntax: "proto3" +} diff --git a/runtime/v2/runhcs/options/runhcs.pb.go b/runtime/v2/runhcs/options/runhcs.pb.go new file mode 100644 index 000000000..0523aafd8 --- /dev/null +++ b/runtime/v2/runhcs/options/runhcs.pb.go @@ -0,0 +1,418 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: github.com/containerd/containerd/runtime/v2/runhcs/options/runhcs.proto + +/* + Package options is a generated protocol buffer package. + + It is generated from these files: + github.com/containerd/containerd/runtime/v2/runhcs/options/runhcs.proto + + It has these top-level messages: + Options +*/ +package options + +import proto "github.com/gogo/protobuf/proto" +import fmt "fmt" +import math "math" + +// skipping weak import gogoproto "github.com/gogo/protobuf/gogoproto" + +import strings "strings" +import reflect "reflect" + +import io "io" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package + +type Options_DebugType int32 + +const ( + Options_NPIPE Options_DebugType = 0 + Options_FILE Options_DebugType = 1 +) + +var Options_DebugType_name = map[int32]string{ + 0: "NPIPE", + 1: "FILE", +} +var Options_DebugType_value = map[string]int32{ + "NPIPE": 0, + "FILE": 1, +} + +func (x Options_DebugType) String() string { + return proto.EnumName(Options_DebugType_name, int32(x)) +} +func (Options_DebugType) EnumDescriptor() ([]byte, []int) { return fileDescriptorRunhcs, []int{0, 0} } + +type Options struct { + // enable debug tracing + Debug bool `protobuf:"varint,1,opt,name=debug,proto3" json:"debug,omitempty"` + // debug tracing output type + DebugType Options_DebugType `protobuf:"varint,2,opt,name=debug_type,json=debugType,proto3,enum=containerd.runhcs.v1.Options_DebugType" json:"debug_type,omitempty"` + // registry key root for storage of the runhcs container state + RegistryRoot string `protobuf:"bytes,3,opt,name=registry_root,json=registryRoot,proto3" json:"registry_root,omitempty"` +} + +func (m *Options) Reset() { *m = Options{} } +func (*Options) ProtoMessage() {} +func (*Options) Descriptor() ([]byte, []int) { return fileDescriptorRunhcs, []int{0} } + +func init() { + proto.RegisterType((*Options)(nil), "containerd.runhcs.v1.Options") + proto.RegisterEnum("containerd.runhcs.v1.Options_DebugType", Options_DebugType_name, Options_DebugType_value) +} +func (m *Options) 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 *Options) MarshalTo(dAtA []byte) (int, error) { + var i int + _ = i + var l int + _ = l + if m.Debug { + dAtA[i] = 0x8 + i++ + if m.Debug { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i++ + } + if m.DebugType != 0 { + dAtA[i] = 0x10 + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(m.DebugType)) + } + if len(m.RegistryRoot) > 0 { + dAtA[i] = 0x1a + i++ + i = encodeVarintRunhcs(dAtA, i, uint64(len(m.RegistryRoot))) + i += copy(dAtA[i:], m.RegistryRoot) + } + return i, nil +} + +func encodeVarintRunhcs(dAtA []byte, offset int, v uint64) int { + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return offset + 1 +} +func (m *Options) Size() (n int) { + var l int + _ = l + if m.Debug { + n += 2 + } + if m.DebugType != 0 { + n += 1 + sovRunhcs(uint64(m.DebugType)) + } + l = len(m.RegistryRoot) + if l > 0 { + n += 1 + l + sovRunhcs(uint64(l)) + } + return n +} + +func sovRunhcs(x uint64) (n int) { + for { + n++ + x >>= 7 + if x == 0 { + break + } + } + return n +} +func sozRunhcs(x uint64) (n int) { + return sovRunhcs(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (this *Options) String() string { + if this == nil { + return "nil" + } + s := strings.Join([]string{`&Options{`, + `Debug:` + fmt.Sprintf("%v", this.Debug) + `,`, + `DebugType:` + fmt.Sprintf("%v", this.DebugType) + `,`, + `RegistryRoot:` + fmt.Sprintf("%v", this.RegistryRoot) + `,`, + `}`, + }, "") + return s +} +func valueToStringRunhcs(v interface{}) string { + rv := reflect.ValueOf(v) + if rv.IsNil() { + return "nil" + } + pv := reflect.Indirect(rv).Interface() + return fmt.Sprintf("*%v", pv) +} +func (m *Options) 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 ErrIntOverflowRunhcs + } + 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: Options: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Options: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Debug", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + m.Debug = bool(v != 0) + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field DebugType", wireType) + } + m.DebugType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.DebugType |= (Options_DebugType(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field RegistryRoot", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowRunhcs + } + 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 ErrInvalidLengthRunhcs + } + postIndex := iNdEx + intStringLen + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.RegistryRoot = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipRunhcs(dAtA[iNdEx:]) + if err != nil { + return err + } + if skippy < 0 { + return ErrInvalidLengthRunhcs + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipRunhcs(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunhcs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunhcs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + return iNdEx, nil + case 1: + iNdEx += 8 + return iNdEx, nil + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunhcs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + iNdEx += length + if length < 0 { + return 0, ErrInvalidLengthRunhcs + } + return iNdEx, nil + case 3: + for { + var innerWire uint64 + var start int = iNdEx + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowRunhcs + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + innerWire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + innerWireType := int(innerWire & 0x7) + if innerWireType == 4 { + break + } + next, err := skipRunhcs(dAtA[start:]) + if err != nil { + return 0, err + } + iNdEx = start + next + } + return iNdEx, nil + case 4: + return iNdEx, nil + case 5: + iNdEx += 4 + return iNdEx, nil + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + } + panic("unreachable") +} + +var ( + ErrInvalidLengthRunhcs = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowRunhcs = fmt.Errorf("proto: integer overflow") +) + +func init() { + proto.RegisterFile("github.com/containerd/containerd/runtime/v2/runhcs/options/runhcs.proto", fileDescriptorRunhcs) +} + +var fileDescriptorRunhcs = []byte{ + // 265 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x72, 0x4f, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x4f, 0xce, 0xcf, 0x2b, 0x49, 0xcc, 0xcc, 0x4b, 0x2d, + 0x4a, 0x41, 0x66, 0x16, 0x95, 0xe6, 0x95, 0x64, 0xe6, 0xa6, 0xea, 0x97, 0x19, 0x81, 0x98, 0x19, + 0xc9, 0xc5, 0xfa, 0xf9, 0x05, 0x25, 0x99, 0xf9, 0x79, 0xc5, 0x50, 0xae, 0x5e, 0x41, 0x51, 0x7e, + 0x49, 0xbe, 0x90, 0x08, 0x42, 0x8b, 0x1e, 0x54, 0xa2, 0xcc, 0x50, 0x4a, 0x24, 0x3d, 0x3f, 0x3d, + 0x1f, 0xac, 0x40, 0x1f, 0xc4, 0x82, 0xa8, 0x55, 0x5a, 0xc7, 0xc8, 0xc5, 0xee, 0x0f, 0x31, 0x44, + 0x48, 0x84, 0x8b, 0x35, 0x25, 0x35, 0xa9, 0x34, 0x5d, 0x82, 0x51, 0x81, 0x51, 0x83, 0x23, 0x08, + 0xc2, 0x11, 0x72, 0xe3, 0xe2, 0x02, 0x33, 0xe2, 0x4b, 0x2a, 0x0b, 0x52, 0x25, 0x98, 0x14, 0x18, + 0x35, 0xf8, 0x8c, 0xd4, 0xf5, 0xb0, 0x59, 0xa1, 0x07, 0x35, 0x48, 0xcf, 0x05, 0xa4, 0x3e, 0xa4, + 0xb2, 0x20, 0x35, 0x88, 0x33, 0x05, 0xc6, 0x14, 0x52, 0xe6, 0xe2, 0x2d, 0x4a, 0x4d, 0xcf, 0x2c, + 0x2e, 0x29, 0xaa, 0x8c, 0x2f, 0xca, 0xcf, 0x2f, 0x91, 0x60, 0x56, 0x60, 0xd4, 0xe0, 0x0c, 0xe2, + 0x81, 0x09, 0x06, 0xe5, 0xe7, 0x97, 0x28, 0x29, 0x70, 0x71, 0xc2, 0x35, 0x0b, 0x71, 0x72, 0xb1, + 0xfa, 0x05, 0x78, 0x06, 0xb8, 0x0a, 0x30, 0x08, 0x71, 0x70, 0xb1, 0xb8, 0x79, 0xfa, 0xb8, 0x0a, + 0x30, 0x3a, 0xc5, 0x9c, 0x78, 0x28, 0xc7, 0x70, 0xe3, 0xa1, 0x1c, 0x43, 0xc3, 0x23, 0x39, 0xc6, + 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x31, 0xca, 0x89, 0xfc, + 0xf0, 0xb3, 0x86, 0xd2, 0x11, 0x0c, 0x49, 0x6c, 0xe0, 0x70, 0x31, 0x06, 0x04, 0x00, 0x00, 0xff, + 0xff, 0x91, 0x7a, 0xda, 0xa5, 0x8e, 0x01, 0x00, 0x00, +} diff --git a/runtime/v2/runhcs/options/runhcs.proto b/runtime/v2/runhcs/options/runhcs.proto new file mode 100644 index 000000000..fbb595e42 --- /dev/null +++ b/runtime/v2/runhcs/options/runhcs.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; + +package containerd.runhcs.v1; + +import weak "gogoproto/gogo.proto"; + +option go_package = "github.com/containerd/containerd/runtime/v2/runhcs/options;options"; + +message Options { + // enable debug tracing + bool debug = 1; + enum DebugType { + NPIPE = 0; + FILE = 1; + } + // debug tracing output type + DebugType debug_type = 2; + // registry key root for storage of the runhcs container state + string registry_root = 3; +} diff --git a/runtime/v2/runhcs/service.go b/runtime/v2/runhcs/service.go index 484dcff13..9f6a68f2d 100644 --- a/runtime/v2/runhcs/service.go +++ b/runtime/v2/runhcs/service.go @@ -28,6 +28,7 @@ import ( "os" "os/exec" "path" + "path/filepath" "strconv" "strings" "sync" @@ -43,9 +44,11 @@ import ( "github.com/containerd/containerd/log" "github.com/containerd/containerd/mount" "github.com/containerd/containerd/namespaces" + "github.com/containerd/containerd/runtime/v2/runhcs/options" "github.com/containerd/containerd/runtime/v2/shim" taskAPI "github.com/containerd/containerd/runtime/v2/task" "github.com/containerd/go-runc" + "github.com/containerd/typeurl" ptypes "github.com/gogo/protobuf/types" oci "github.com/opencontainers/runtime-spec/specs-go" "github.com/pkg/errors" @@ -63,18 +66,6 @@ var ( empty = &ptypes.Empty{} ) -func newRunhcs(debugLog string) *runhcs.Runhcs { - rhs := &runhcs.Runhcs{ - Debug: debugLog != "", - LogFormat: runhcs.JSON, - Owner: "containerd-runhcs-shim-v1", - } - if rhs.Debug { - rhs.Log = debugLog - } - return rhs -} - // forwardRunhcsLogs copies logs from c and writes them to the ctx logger // upstream. func forwardRunhcsLogs(ctx context.Context, c net.Conn, fields logrus.Fields) { @@ -154,16 +145,31 @@ type service struct { context context.Context - // debugLog if not "" indicates the log pipe path for runhcs.exe to write its logs to. - debugLog string + // debugLog if not "" indicates the log file or pipe path for runhcs.exe to + // write its logs to. + debugLog string + // if `shimOpts.DebugType == options.Opitons_NPIPE` will hold the listener + // for the runhcs.exe to connect to for sending logs. debugListener net.Listener + shimOpts options.Options + id string processes map[string]*process publisher events.Publisher } +func (s *service) newRunhcs() *runhcs.Runhcs { + return &runhcs.Runhcs{ + Debug: s.shimOpts.Debug, + Log: s.debugLog, + LogFormat: runhcs.JSON, + Owner: "containerd-runhcs-shim-v1", + Root: s.shimOpts.RegistryRoot, + } +} + // getProcess attempts to get a process by id. // The caller MUST NOT have locked s.mu previous to calling this function. func (s *service) getProcess(id, execID string) (*process, error) { @@ -196,7 +202,7 @@ func (s *service) Cleanup(ctx context.Context) (*taskAPI.DeleteResponse, error) return nil, err } // Forcibly shut down any container in this bundle - rhcs := newRunhcs("") + rhcs := s.newRunhcs() dopts := &runhcs.DeleteOpts{ Force: true, } @@ -290,7 +296,7 @@ func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI. // This is a container if p.cid == p.id { - rhcs := newRunhcs(s.debugLog) + rhcs := s.newRunhcs() cs, err := rhcs.State(ctx, p.id) if err != nil { return nil, err @@ -406,6 +412,19 @@ func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*ta s.mu.Lock() defer s.mu.Unlock() + _, err := namespaces.NamespaceRequired(ctx) + if err != nil { + return nil, errors.Wrap(err, "create namespace") + } + + if r.Options != nil { + v, err := typeurl.UnmarshalAny(r.Options) + if err != nil { + return nil, err + } + s.shimOpts = *v.(*options.Options) + } + if p := s.processes[r.ID]; p != nil { return nil, errdefs.ToGRPCf(errdefs.ErrAlreadyExists, "process %s already exists", r.ID) } @@ -445,38 +464,41 @@ func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*ta pr.close() } }() - // TODO: Parse the real RunHcs Opts r.Options - opts, ok := ctx.Value(shim.OptsKey{}).(shim.Opts) - if ok && opts.Debug { - if s.debugLog == "" { - logPath := safePipePrefix + fmt.Sprintf("runhcs-log-%s", r.ID) - l, err := winio.ListenPipe(logPath, nil) - if err != nil { - return nil, err - } - s.debugLog = logPath - s.debugListener = l - // Accept connections and forward all logs for each runhcs.exe - // invocation - go func() { - for { - c, err := s.debugListener.Accept() - if err != nil { - if err == errorConnectionAborted { - break - } - log.G(ctx).WithError(err).Debug("log accept failure") - // Logrus error locally? - continue - } - fields := map[string]interface{}{ - "log-source": "runhcs", - "task-id": r.ID, - } - go forwardRunhcsLogs(ctx, c, fields) + if s.shimOpts.Debug { + if s.debugLog == "" { + if s.shimOpts.DebugType == options.Options_FILE { + s.debugLog = filepath.Join(r.Bundle, fmt.Sprintf("runhcs-%s.log", r.ID)) + } else { + logPath := safePipePrefix + fmt.Sprintf("runhcs-log-%s", r.ID) + l, err := winio.ListenPipe(logPath, nil) + if err != nil { + return nil, err } - }() + s.debugLog = logPath + s.debugListener = l + + // Accept connections and forward all logs for each runhcs.exe + // invocation + go func() { + for { + c, err := s.debugListener.Accept() + if err != nil { + if err == winio.ErrPipeListenerClosed { + break + } + log.G(ctx).WithError(err).Debug("log accept failure") + // Logrus error locally? + continue + } + fields := map[string]interface{}{ + "log-source": "runhcs", + "task-id": r.ID, + } + go forwardRunhcsLogs(ctx, c, fields) + } + }() + } } } @@ -485,42 +507,47 @@ func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*ta IO: io, PidFile: pidfilePath, } - rhcs := newRunhcs(s.debugLog) + rhcs := s.newRunhcs() if rhcs.Debug { - doForwardLogs := func(source, logPipeFmt string, opt *string) error { - pipeName := fmt.Sprintf(logPipeFmt, r.ID) - *opt = safePipePrefix + pipeName - l, err := winio.ListenPipe(*opt, nil) - if err != nil { - return err - } - go func() { - defer l.Close() - c, err := l.Accept() + if s.shimOpts.DebugType == options.Options_FILE { + copts.ShimLog = filepath.Join(r.Bundle, fmt.Sprintf("runhcs-shim-%s.log", r.ID)) + copts.VMLog = filepath.Join(r.Bundle, fmt.Sprintf("runhcs-vmshim-%s.log", r.ID)) + } else { + doForwardLogs := func(source, logPipeFmt string, opt *string) error { + pipeName := fmt.Sprintf(logPipeFmt, r.ID) + *opt = safePipePrefix + pipeName + l, err := winio.ListenPipe(*opt, nil) if err != nil { - log.G(ctx). - WithField("task-id", r.ID). - WithError(err). - Errorf("failed to accept %s", pipeName) - } else { - fields := map[string]interface{}{ - "log-source": source, - "task-id": r.ID, - } - go forwardRunhcsLogs(ctx, c, fields) + return err } - }() - return nil - } + go func() { + defer l.Close() + c, err := l.Accept() + if err != nil { + log.G(ctx). + WithField("task-id", r.ID). + WithError(err). + Errorf("failed to accept %s", pipeName) + } else { + fields := map[string]interface{}{ + "log-source": source, + "task-id": r.ID, + } + go forwardRunhcsLogs(ctx, c, fields) + } + }() + return nil + } - err = doForwardLogs("runhcs-shim", "runhcs-shim-log-%s", &copts.ShimLog) - if err != nil { - return nil, err - } + err = doForwardLogs("runhcs-shim", "runhcs-shim-log-%s", &copts.ShimLog) + if err != nil { + return nil, err + } - err = doForwardLogs("runhcs--vm-shim", "runhcs-vm-shim-log-%s", &copts.VMLog) - if err != nil { - return nil, err + err = doForwardLogs("runhcs--vm-shim", "runhcs-vm-shim-log-%s", &copts.VMLog) + if err != nil { + return nil, err + } } } err = rhcs.Create(ctx, r.ID, r.Bundle, copts) @@ -571,7 +598,7 @@ func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI. return nil, errors.New("cannot start already started container or process") } - rhcs := newRunhcs(s.debugLog) + rhcs := s.newRunhcs() // This is a start/exec if r.ExecID != "" { @@ -585,36 +612,40 @@ func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI. } if rhcs.Debug { - doForwardLogs := func(source, pipeName string, opt *string) error { - *opt = safePipePrefix + pipeName - l, err := winio.ListenPipe(*opt, nil) - if err != nil { - return err - } - go func() { - defer l.Close() - c, err := l.Accept() + if s.shimOpts.DebugType == options.Options_FILE { + eopts.ShimLog = filepath.Join(p.bundle, fmt.Sprintf("runhcs-shim-%s.log", execFmt)) + } else { + doForwardLogs := func(source, pipeName string, opt *string) error { + *opt = safePipePrefix + pipeName + l, err := winio.ListenPipe(*opt, nil) if err != nil { - log.G(ctx). - WithField("task-id", r.ID). - WithField("exec-id", r.ExecID). - WithError(err). - Errorf("failed to accept %s", pipeName) - } else { - fields := map[string]interface{}{ - "log-source": source, - "task-id": r.ID, - "exec-id": r.ExecID, - } - go forwardRunhcsLogs(ctx, c, fields) + return err } - }() - return nil - } + go func() { + defer l.Close() + c, err := l.Accept() + if err != nil { + log.G(ctx). + WithField("task-id", r.ID). + WithField("exec-id", r.ExecID). + WithError(err). + Errorf("failed to accept %s", pipeName) + } else { + fields := map[string]interface{}{ + "log-source": source, + "task-id": r.ID, + "exec-id": r.ExecID, + } + go forwardRunhcsLogs(ctx, c, fields) + } + }() + return nil + } - err = doForwardLogs("runhcs-shim-exec", "runhcs-shim-log-"+execFmt, &eopts.ShimLog) - if err != nil { - return nil, err + err = doForwardLogs("runhcs-shim-exec", "runhcs-shim-log-"+execFmt, &eopts.ShimLog) + if err != nil { + return nil, err + } } } @@ -683,7 +714,7 @@ func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAP // This is a container if p.cid == p.id { - rhs := newRunhcs(s.debugLog) + rhs := s.newRunhcs() dopts := &runhcs.DeleteOpts{ Force: true, } @@ -729,7 +760,7 @@ func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*ptypes.E return nil, err } - rhcs := newRunhcs(s.debugLog) + rhcs := s.newRunhcs() if err = rhcs.Pause(ctx, p.id); err != nil { return nil, err } @@ -748,7 +779,7 @@ func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (*ptypes return nil, err } - rhcs := newRunhcs(s.debugLog) + rhcs := s.newRunhcs() if err = rhcs.Resume(ctx, p.id); err != nil { return nil, err } @@ -773,9 +804,8 @@ func (s *service) Kill(ctx context.Context, r *taskAPI.KillRequest) (*ptypes.Emp return nil, err } - // TODO: JTERRY75 runhcs needs r.Signal in string form // TODO: JTERRY75 runhcs support for r.All? - rhcs := newRunhcs(s.debugLog) + rhcs := s.newRunhcs() if err = rhcs.Kill(ctx, p.id, strconv.FormatUint(uint64(r.Signal), 10)); err != nil { if !strings.Contains(err.Error(), "container is stopped") { return nil, err @@ -882,7 +912,7 @@ func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (* opts := runhcs.ResizeTTYOpts{ Pid: &pid, } - rhcs := newRunhcs(s.debugLog) + rhcs := s.newRunhcs() if err = rhcs.ResizeTTY(ctx, p.cid, uint16(r.Width), uint16(r.Height), &opts); err != nil { return nil, err } diff --git a/runtime/v2/shim/shim_windows.go b/runtime/v2/shim/shim_windows.go index 6e5642620..bd3f62aea 100644 --- a/runtime/v2/shim/shim_windows.go +++ b/runtime/v2/shim/shim_windows.go @@ -27,7 +27,6 @@ import ( "os" "os/exec" "sync" - "syscall" "unsafe" winio "github.com/Microsoft/go-winio" @@ -40,10 +39,6 @@ import ( "golang.org/x/sys/windows" ) -const ( - errorConnectionAborted syscall.Errno = 1236 -) - // setupSignals creates a new signal handler for all signals func setupSignals() (chan os.Signal, error) { signals := make(chan os.Signal, 32) @@ -209,7 +204,7 @@ func (dswl *deferredShimWriteLogger) beginAccept() { dswl.mu.Unlock() c, err := dswl.l.Accept() - if err == errorConnectionAborted { + if err == winio.ErrPipeListenerClosed { dswl.mu.Lock() dswl.aborted = true dswl.l.Close()