Adds containerd-shim-runhcs verbose logging support

Revendors to Microsoft/hcsshim v0.7.5 that added support for logging all
runhcs.exe commands via Windows named pipes. This now launches all runhcs.exe
commands and forwards debug logging to the containerd-shim-runhcs log when
with --debug.

Signed-off-by: Justin Terry (VM) <juterry@microsoft.com>
This commit is contained in:
Justin Terry (VM) 2018-09-24 16:09:16 -07:00
parent 772644e978
commit 81eb40fabf
6 changed files with 264 additions and 41 deletions

View File

@ -22,15 +22,19 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"io/ioutil"
"net"
"os" "os"
"os/exec" "os/exec"
"path" "path"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
winio "github.com/Microsoft/go-winio"
"github.com/Microsoft/hcsshim/cmd/go-runhcs" "github.com/Microsoft/hcsshim/cmd/go-runhcs"
containerd_types "github.com/containerd/containerd/api/types" containerd_types "github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/api/types/task" "github.com/containerd/containerd/api/types/task"
@ -50,24 +54,87 @@ import (
const ( const (
runhcsShimVersion = "0.0.1" runhcsShimVersion = "0.0.1"
safePipePrefix = `\\.\pipe\ProtectedPrefix\Administrators\`
errorConnectionAborted syscall.Errno = 1236
) )
var ( var (
empty = &ptypes.Empty{} empty = &ptypes.Empty{}
) )
func newRunhcs(bundle string) *runhcs.Runhcs { func newRunhcs(debugLog string) *runhcs.Runhcs {
rhs := &runhcs.Runhcs{ rhs := &runhcs.Runhcs{
Debug: logrus.GetLevel() == logrus.DebugLevel, Debug: debugLog != "",
LogFormat: runhcs.JSON, LogFormat: runhcs.JSON,
Owner: "containerd-runhcs-shim-v1", Owner: "containerd-runhcs-shim-v1",
} }
if rhs.Debug { if rhs.Debug {
rhs.Log = filepath.Join(bundle, "runhcs-debug.log") rhs.Log = debugLog
} }
return rhs 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) {
defer c.Close()
j := json.NewDecoder(c)
for {
e := logrus.Entry{}
err := j.Decode(&e.Data)
if err == io.EOF || err == errorConnectionAborted {
break
}
if err != nil {
// Likely the last message wasn't complete at closure. Just read all
// data and forward as error.
data, _ := ioutil.ReadAll(io.MultiReader(j.Buffered(), c))
if len(data) != 0 {
log.G(ctx).WithFields(fields).Error(string(data))
}
break
}
msg := e.Data[logrus.FieldKeyMsg]
delete(e.Data, logrus.FieldKeyMsg)
level, err := logrus.ParseLevel(e.Data[logrus.FieldKeyLevel].(string))
if err != nil {
log.G(ctx).WithFields(fields).WithError(err).Debug("invalid log level")
level = logrus.DebugLevel
}
delete(e.Data, logrus.FieldKeyLevel)
// TODO: JTERRY75 maybe we need to make this configurable so we know
// that runhcs is using the same one we are deserializing.
ti, err := time.Parse(logrus.DefaultTimestampFormat, e.Data[logrus.FieldKeyTime].(string))
if err != nil {
log.G(ctx).WithFields(fields).WithError(err).Debug("invalid time stamp format")
ti = time.Time{}
}
delete(e.Data, logrus.FieldKeyTime)
etr := log.G(ctx).WithFields(fields).WithFields(e.Data)
etr.Time = ti
switch level {
case logrus.PanicLevel:
etr.Panic(msg)
case logrus.FatalLevel:
etr.Fatal(msg)
case logrus.ErrorLevel:
etr.Error(msg)
case logrus.WarnLevel:
etr.Warn(msg)
case logrus.InfoLevel:
etr.Info(msg)
case logrus.DebugLevel:
etr.Debug(msg)
}
}
}
// New returns a new runhcs shim service that can be used via GRPC // New returns a new runhcs shim service that can be used via GRPC
func New(ctx context.Context, id string, publisher events.Publisher) (shim.Shim, error) { func New(ctx context.Context, id string, publisher events.Publisher) (shim.Shim, error) {
return &service{ return &service{
@ -87,6 +154,10 @@ type service struct {
context context.Context context context.Context
// debugLog if not "" indicates the log pipe path for runhcs.exe to write its logs to.
debugLog string
debugListener net.Listener
id string id string
processes map[string]*process processes map[string]*process
@ -195,6 +266,8 @@ func (s *service) StartShim(ctx context.Context, id, containerdBinary, container
// State returns runtime state information for a process // State returns runtime state information for a process
func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI.StateResponse, error) { func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI.StateResponse, error) {
log.G(ctx).Debugf("State: %s: %s", r.ID, r.ExecID)
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@ -208,7 +281,7 @@ func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI.
// This is a container // This is a container
if p.cid == p.id { if p.cid == p.id {
rhcs := newRunhcs(p.bundle) rhcs := newRunhcs(s.debugLog)
cs, err := rhcs.State(ctx, p.id) cs, err := rhcs.State(ctx, p.id)
if err != nil { if err != nil {
return nil, err return nil, err
@ -318,7 +391,7 @@ func writeMountsToConfig(bundle string, mounts []*containerd_types.Mount) error
// Create a new initial process and container with runhcs // Create a new initial process and container with runhcs
func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*taskAPI.CreateTaskResponse, error) { func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*taskAPI.CreateTaskResponse, error) {
log.G(ctx).Infof("Create: %s", r.ID) log.G(ctx).Debugf("Create: %s", r.ID)
// Hold the lock for the entire duration to avoid duplicate process creation. // Hold the lock for the entire duration to avoid duplicate process creation.
s.mu.Lock() s.mu.Lock()
@ -363,16 +436,84 @@ func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*ta
pr.close() 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)
}
}()
}
}
pidfilePath := path.Join(r.Bundle, "runhcs-pidfile.pid") pidfilePath := path.Join(r.Bundle, "runhcs-pidfile.pid")
copts := &runhcs.CreateOpts{ copts := &runhcs.CreateOpts{
IO: io, IO: io,
PidFile: pidfilePath, PidFile: pidfilePath,
ShimLog: path.Join(r.Bundle, "runhcs-shim.log"), }
VMLog: path.Join(r.Bundle, "runhcs-vm-shim.log"), rhcs := newRunhcs(s.debugLog)
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 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
} }
rhcs := newRunhcs(r.Bundle) 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 = rhcs.Create(ctx, r.ID, r.Bundle, copts) err = rhcs.Create(ctx, r.ID, r.Bundle, copts)
if err != nil { if err != nil {
return nil, err return nil, err
@ -408,7 +549,7 @@ func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*ta
// Start a process // Start a process
func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.StartResponse, error) { func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.StartResponse, error) {
log.G(ctx).Infof("Start: %s: %s", r.ID, r.ExecID) log.G(ctx).Debugf("Start: %s: %s", r.ID, r.ExecID)
var p *process var p *process
var err error var err error
if p, err = s.getProcess(r.ID, r.ExecID); err != nil { if p, err = s.getProcess(r.ID, r.ExecID); err != nil {
@ -421,7 +562,7 @@ func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.
return nil, errors.New("cannot start already started container or process") return nil, errors.New("cannot start already started container or process")
} }
rhcs := newRunhcs(p.bundle) rhcs := newRunhcs(s.debugLog)
// This is a start/exec // This is a start/exec
if r.ExecID != "" { if r.ExecID != "" {
@ -431,10 +572,43 @@ func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.
eopts := &runhcs.ExecOpts{ eopts := &runhcs.ExecOpts{
IO: p.relay.io, IO: p.relay.io,
PidFile: pidfilePath, PidFile: pidfilePath,
ShimLog: path.Join(p.bundle, fmt.Sprintf("runhcs-%s-shim.log", execFmt)),
Detach: true, Detach: true,
} }
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 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
}
}
// ID here is the containerID to exec the process in. // ID here is the containerID to exec the process in.
err = rhcs.Exec(ctx, r.ID, procConfig, eopts) err = rhcs.Exec(ctx, r.ID, procConfig, eopts)
if err != nil { if err != nil {
@ -490,7 +664,7 @@ func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.
// Delete the initial process and container // Delete the initial process and container
func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAPI.DeleteResponse, error) { func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAPI.DeleteResponse, error) {
log.G(ctx).Infof("Delete: %s: %s", r.ID, r.ExecID) log.G(ctx).Debugf("Delete: %s: %s", r.ID, r.ExecID)
var p *process var p *process
var err error var err error
@ -500,7 +674,7 @@ func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAP
// This is a container // This is a container
if p.cid == p.id { if p.cid == p.id {
rhs := newRunhcs(p.bundle) rhs := newRunhcs(s.debugLog)
dopts := &runhcs.DeleteOpts{ dopts := &runhcs.DeleteOpts{
Force: true, Force: true,
} }
@ -530,11 +704,15 @@ func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAP
// Pids returns all pids inside the container // Pids returns all pids inside the container
func (s *service) Pids(ctx context.Context, r *taskAPI.PidsRequest) (*taskAPI.PidsResponse, error) { func (s *service) Pids(ctx context.Context, r *taskAPI.PidsRequest) (*taskAPI.PidsResponse, error) {
log.G(ctx).Debugf("Pids: %s", r.ID)
return nil, errdefs.ErrNotImplemented return nil, errdefs.ErrNotImplemented
} }
// Pause the container // Pause the container
func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*ptypes.Empty, error) { func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*ptypes.Empty, error) {
log.G(ctx).Debugf("Pause: %s", r.ID)
// TODO: Validate that 'id' is actually a valid parent container ID // TODO: Validate that 'id' is actually a valid parent container ID
var p *process var p *process
var err error var err error
@ -542,7 +720,7 @@ func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*ptypes.E
return nil, err return nil, err
} }
rhcs := newRunhcs(p.bundle) rhcs := newRunhcs(s.debugLog)
if err = rhcs.Pause(ctx, p.id); err != nil { if err = rhcs.Pause(ctx, p.id); err != nil {
return nil, err return nil, err
} }
@ -552,6 +730,8 @@ func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*ptypes.E
// Resume the container // Resume the container
func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (*ptypes.Empty, error) { func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (*ptypes.Empty, error) {
log.G(ctx).Debugf("Resume: %s", r.ID)
// TODO: Validate that 'id' is actually a valid parent container ID // TODO: Validate that 'id' is actually a valid parent container ID
var p *process var p *process
var err error var err error
@ -559,7 +739,7 @@ func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (*ptypes
return nil, err return nil, err
} }
rhcs := newRunhcs(p.bundle) rhcs := newRunhcs(s.debugLog)
if err = rhcs.Resume(ctx, p.id); err != nil { if err = rhcs.Resume(ctx, p.id); err != nil {
return nil, err return nil, err
} }
@ -569,12 +749,14 @@ func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (*ptypes
// Checkpoint the container // Checkpoint the container
func (s *service) Checkpoint(ctx context.Context, r *taskAPI.CheckpointTaskRequest) (*ptypes.Empty, error) { func (s *service) Checkpoint(ctx context.Context, r *taskAPI.CheckpointTaskRequest) (*ptypes.Empty, error) {
log.G(ctx).Debugf("Checkpoint: %s", r.ID)
return nil, errdefs.ErrNotImplemented return nil, errdefs.ErrNotImplemented
} }
// Kill a process with the provided signal // Kill a process with the provided signal
func (s *service) Kill(ctx context.Context, r *taskAPI.KillRequest) (*ptypes.Empty, error) { func (s *service) Kill(ctx context.Context, r *taskAPI.KillRequest) (*ptypes.Empty, error) {
log.G(ctx).Infof("Kill: %s: %s", r.ID, r.ExecID) log.G(ctx).Debugf("Kill: %s: %s", r.ID, r.ExecID)
var p *process var p *process
var err error var err error
@ -584,7 +766,7 @@ func (s *service) Kill(ctx context.Context, r *taskAPI.KillRequest) (*ptypes.Emp
// TODO: JTERRY75 runhcs needs r.Signal in string form // TODO: JTERRY75 runhcs needs r.Signal in string form
// TODO: JTERRY75 runhcs support for r.All? // TODO: JTERRY75 runhcs support for r.All?
rhcs := newRunhcs(p.bundle) rhcs := newRunhcs(s.debugLog)
if err = rhcs.Kill(ctx, p.id, strconv.FormatUint(uint64(r.Signal), 10)); err != nil { if err = rhcs.Kill(ctx, p.id, strconv.FormatUint(uint64(r.Signal), 10)); err != nil {
if !strings.Contains(err.Error(), "container is stopped") { if !strings.Contains(err.Error(), "container is stopped") {
return nil, err return nil, err
@ -596,7 +778,7 @@ func (s *service) Kill(ctx context.Context, r *taskAPI.KillRequest) (*ptypes.Emp
// Exec an additional process inside the container // Exec an additional process inside the container
func (s *service) Exec(ctx context.Context, r *taskAPI.ExecProcessRequest) (*ptypes.Empty, error) { func (s *service) Exec(ctx context.Context, r *taskAPI.ExecProcessRequest) (*ptypes.Empty, error) {
log.G(ctx).Infof("Exec: %s: %s", r.ID, r.ExecID) log.G(ctx).Debugf("Exec: %s: %s", r.ID, r.ExecID)
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
@ -679,6 +861,8 @@ func (s *service) Exec(ctx context.Context, r *taskAPI.ExecProcessRequest) (*pty
// ResizePty of a process // ResizePty of a process
func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (*ptypes.Empty, error) { func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (*ptypes.Empty, error) {
log.G(ctx).Debugf("ResizePty: %s: %s", r.ID, r.ExecID)
var p *process var p *process
var err error var err error
if p, err = s.getProcess(r.ID, r.ExecID); err != nil { if p, err = s.getProcess(r.ID, r.ExecID); err != nil {
@ -689,7 +873,7 @@ func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (*
opts := runhcs.ResizeTTYOpts{ opts := runhcs.ResizeTTYOpts{
Pid: &pid, Pid: &pid,
} }
rhcs := newRunhcs(p.bundle) rhcs := newRunhcs(s.debugLog)
if err = rhcs.ResizeTTY(ctx, p.cid, uint16(r.Width), uint16(r.Height), &opts); err != nil { if err = rhcs.ResizeTTY(ctx, p.cid, uint16(r.Width), uint16(r.Height), &opts); err != nil {
return nil, err return nil, err
} }
@ -699,6 +883,8 @@ func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (*
// CloseIO of a process // CloseIO of a process
func (s *service) CloseIO(ctx context.Context, r *taskAPI.CloseIORequest) (*ptypes.Empty, error) { func (s *service) CloseIO(ctx context.Context, r *taskAPI.CloseIORequest) (*ptypes.Empty, error) {
log.G(ctx).Debugf("CloseIO: %s: %s", r.ID, r.ExecID)
var p *process var p *process
var err error var err error
if p, err = s.getProcess(r.ID, r.ExecID); err != nil { if p, err = s.getProcess(r.ID, r.ExecID); err != nil {
@ -710,11 +896,15 @@ func (s *service) CloseIO(ctx context.Context, r *taskAPI.CloseIORequest) (*ptyp
// Update a running container // Update a running container
func (s *service) Update(ctx context.Context, r *taskAPI.UpdateTaskRequest) (*ptypes.Empty, error) { func (s *service) Update(ctx context.Context, r *taskAPI.UpdateTaskRequest) (*ptypes.Empty, error) {
log.G(ctx).Debugf("Update: %s", r.ID)
return nil, errdefs.ErrNotImplemented return nil, errdefs.ErrNotImplemented
} }
// Wait for a process to exit // Wait for a process to exit
func (s *service) Wait(ctx context.Context, r *taskAPI.WaitRequest) (*taskAPI.WaitResponse, error) { func (s *service) Wait(ctx context.Context, r *taskAPI.WaitRequest) (*taskAPI.WaitResponse, error) {
log.G(ctx).Debugf("Wait: %s: %s", r.ID, r.ExecID)
var p *process var p *process
var err error var err error
if p, err = s.getProcess(r.ID, r.ExecID); err != nil { if p, err = s.getProcess(r.ID, r.ExecID); err != nil {
@ -729,11 +919,15 @@ func (s *service) Wait(ctx context.Context, r *taskAPI.WaitRequest) (*taskAPI.Wa
// Stats returns statistics about the running container // Stats returns statistics about the running container
func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*taskAPI.StatsResponse, error) { func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*taskAPI.StatsResponse, error) {
log.G(ctx).Debugf("Stats: %s", r.ID)
return nil, errdefs.ErrNotImplemented return nil, errdefs.ErrNotImplemented
} }
// Connect returns the runhcs shim information // Connect returns the runhcs shim information
func (s *service) Connect(ctx context.Context, r *taskAPI.ConnectRequest) (*taskAPI.ConnectResponse, error) { func (s *service) Connect(ctx context.Context, r *taskAPI.ConnectRequest) (*taskAPI.ConnectResponse, error) {
log.G(ctx).Debugf("Connect: %s", r.ID)
return &taskAPI.ConnectResponse{ return &taskAPI.ConnectResponse{
ShimPid: uint32(os.Getpid()), ShimPid: uint32(os.Getpid()),
TaskPid: s.processes[s.id].pid, TaskPid: s.processes[s.id].pid,
@ -743,7 +937,11 @@ func (s *service) Connect(ctx context.Context, r *taskAPI.ConnectRequest) (*task
// Shutdown stops this instance of the runhcs shim // Shutdown stops this instance of the runhcs shim
func (s *service) Shutdown(ctx context.Context, r *taskAPI.ShutdownRequest) (*ptypes.Empty, error) { func (s *service) Shutdown(ctx context.Context, r *taskAPI.ShutdownRequest) (*ptypes.Empty, error) {
log.G(ctx).Infof("Shutdown: %s", r.ID) log.G(ctx).Debugf("Shutdown: %s", r.ID)
if s.debugListener != nil {
s.debugListener.Close()
}
os.Exit(0) os.Exit(0)
return empty, nil return empty, nil

View File

@ -33,7 +33,7 @@ golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895 github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0 github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
github.com/Microsoft/go-winio v0.4.10 github.com/Microsoft/go-winio v0.4.10
github.com/Microsoft/hcsshim v0.7.4 github.com/Microsoft/hcsshim v0.7.6
google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944 google.golang.org/genproto d80a6e20e776b0b17a324d0ba1ab50a39c8e8944
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4 golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a

View File

@ -6,6 +6,8 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strings"
"sync" "sync"
"github.com/containerd/go-runc" "github.com/containerd/go-runc"
@ -22,6 +24,7 @@ const (
JSON Format = "json" JSON Format = "json"
command = "runhcs" command = "runhcs"
safePipePrefix = `\\.\pipe\ProtectedPrefix\Administrators\`
) )
var bytesBufferPool = sync.Pool{ var bytesBufferPool = sync.Pool{
@ -43,7 +46,7 @@ func putBuf(b *bytes.Buffer) {
type Runhcs struct { type Runhcs struct {
// Debug enables debug output for logging. // Debug enables debug output for logging.
Debug bool Debug bool
// Log sets the log file path where internal debug information is written. // Log sets the log file path or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs-log) where internal debug information is written.
Log string Log string
// LogFormat sets the format used by logs. // LogFormat sets the format used by logs.
LogFormat Format LogFormat Format
@ -59,8 +62,14 @@ func (r *Runhcs) args() []string {
out = append(out, "--debug") out = append(out, "--debug")
} }
if r.Log != "" { if r.Log != "" {
// TODO: JTERRY75 - Should we do abs here? if strings.HasPrefix(r.Log, safePipePrefix) {
out = append(out, "--log", r.Log) out = append(out, "--log", r.Log)
} else {
abs, err := filepath.Abs(r.Log)
if err == nil {
out = append(out, "--log", abs)
}
}
} }
if r.LogFormat != none { if r.LogFormat != none {
out = append(out, "--log-format", string(r.LogFormat)) out = append(out, "--log-format", string(r.LogFormat))

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"path/filepath" "path/filepath"
"strings"
runc "github.com/containerd/go-runc" runc "github.com/containerd/go-runc"
) )
@ -13,9 +14,9 @@ type CreateOpts struct {
runc.IO runc.IO
// PidFile is the path to the file to write the process id to. // PidFile is the path to the file to write the process id to.
PidFile string PidFile string
// ShimLog is the path to the log file for the launched shim process. // ShimLog is the path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs-<container-id>-shim-log) for the launched shim process.
ShimLog string ShimLog string
// VMLog is the path to the log file for the launched VM shim process. // VMLog is the path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs-<container-id>-vm-log) for the launched VM shim process.
VMLog string VMLog string
// VMConsole is the path to the pipe for the VM's console (e.g. \\.\pipe\debugpipe) // VMConsole is the path to the pipe for the VM's console (e.g. \\.\pipe\debugpipe)
VMConsole string VMConsole string
@ -31,19 +32,27 @@ func (opt *CreateOpts) args() ([]string, error) {
out = append(out, "--pid-file", abs) out = append(out, "--pid-file", abs)
} }
if opt.ShimLog != "" { if opt.ShimLog != "" {
if strings.HasPrefix(opt.ShimLog, safePipePrefix) {
out = append(out, "--shim-log", opt.ShimLog)
} else {
abs, err := filepath.Abs(opt.ShimLog) abs, err := filepath.Abs(opt.ShimLog)
if err != nil { if err != nil {
return nil, err return nil, err
} }
out = append(out, "--shim-log", abs) out = append(out, "--shim-log", abs)
} }
}
if opt.VMLog != "" { if opt.VMLog != "" {
if strings.HasPrefix(opt.VMLog, safePipePrefix) {
out = append(out, "--vm-log", opt.VMLog)
} else {
abs, err := filepath.Abs(opt.VMLog) abs, err := filepath.Abs(opt.VMLog)
if err != nil { if err != nil {
return nil, err return nil, err
} }
out = append(out, "--vm-log", abs) out = append(out, "--vm-log", abs)
} }
}
if opt.VMConsole != "" { if opt.VMConsole != "" {
out = append(out, "--vm-console", opt.VMConsole) out = append(out, "--vm-console", opt.VMConsole)
} }

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"fmt" "fmt"
"path/filepath" "path/filepath"
"strings"
"github.com/containerd/go-runc" "github.com/containerd/go-runc"
) )
@ -15,7 +16,7 @@ type ExecOpts struct {
Detach bool Detach bool
// PidFile is the path to the file to write the process id to. // PidFile is the path to the file to write the process id to.
PidFile string PidFile string
// ShimLog is the path to the log file for the launched shim process. // ShimLog is the path to the log file or named pipe (e.g. \\.\pipe\ProtectedPrefix\Administrators\runhcs-<container-id>-<exec-id>-log) for the launched shim process.
ShimLog string ShimLog string
} }
@ -32,12 +33,16 @@ func (opt *ExecOpts) args() ([]string, error) {
out = append(out, "--pid-file", abs) out = append(out, "--pid-file", abs)
} }
if opt.ShimLog != "" { if opt.ShimLog != "" {
if strings.HasPrefix(opt.ShimLog, safePipePrefix) {
out = append(out, "--shim-log", opt.ShimLog)
} else {
abs, err := filepath.Abs(opt.ShimLog) abs, err := filepath.Abs(opt.ShimLog)
if err != nil { if err != nil {
return nil, err return nil, err
} }
out = append(out, "--shim-log", abs) out = append(out, "--shim-log", abs)
} }
}
return out, nil return out, nil
} }

View File

@ -6,6 +6,8 @@ import (
// HNSEndpoint represents a network endpoint in HNS // HNSEndpoint represents a network endpoint in HNS
type HNSEndpoint = hns.HNSEndpoint type HNSEndpoint = hns.HNSEndpoint
// Namespace represents a Compartment.
type Namespace = hns.Namespace
//SystemType represents the type of the system on which actions are done //SystemType represents the type of the system on which actions are done
type SystemType string type SystemType string