Merge pull request #2686 from jterry75/runhcs_improvements

Various containerd-shim-runhcs bug fixes/improvements
This commit is contained in:
Michael Crosby 2018-09-28 11:19:54 -04:00 committed by GitHub
commit 655ba65875
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 352 additions and 56 deletions

View File

@ -31,6 +31,7 @@ import (
"github.com/containerd/containerd/runtime/v2/task"
"github.com/containerd/ttrpc"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
func shimBinary(ctx context.Context, bundle *Bundle, runtime, containerdAddress string, events *exchange.Exchange, rt *runtime.TaskList) *binary {
@ -52,13 +53,18 @@ type binary struct {
}
func (b *binary) Start(ctx context.Context) (_ *shim, err error) {
args := []string{"-id", b.bundle.ID}
if logrus.GetLevel() == logrus.DebugLevel {
args = append(args, "-debug")
}
args = append(args, "start")
cmd, err := client.Command(
ctx,
b.runtime,
b.containerdAddress,
b.bundle.Path,
"-id", b.bundle.ID,
"start",
args...,
)
if err != nil {
return nil, err

View File

@ -22,15 +22,19 @@ import (
"context"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"net"
"os"
"os/exec"
"path"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"time"
winio "github.com/Microsoft/go-winio"
"github.com/Microsoft/hcsshim/cmd/go-runhcs"
containerd_types "github.com/containerd/containerd/api/types"
"github.com/containerd/containerd/api/types/task"
@ -50,24 +54,87 @@ import (
const (
runhcsShimVersion = "0.0.1"
safePipePrefix = `\\.\pipe\ProtectedPrefix\Administrators\`
errorConnectionAborted syscall.Errno = 1236
)
var (
empty = &ptypes.Empty{}
)
func newRunhcs(bundle string) *runhcs.Runhcs {
func newRunhcs(debugLog string) *runhcs.Runhcs {
rhs := &runhcs.Runhcs{
Debug: logrus.GetLevel() == logrus.DebugLevel,
Debug: debugLog != "",
LogFormat: runhcs.JSON,
Owner: "containerd-runhcs-shim-v1",
}
if rhs.Debug {
rhs.Log = filepath.Join(bundle, "runhcs-debug.log")
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) {
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
func New(ctx context.Context, id string, publisher events.Publisher) (shim.Shim, error) {
return &service{
@ -87,6 +154,10 @@ type service struct {
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
processes map[string]*process
@ -160,8 +231,13 @@ func (s *service) StartShim(ctx context.Context, id, containerdBinary, container
"-address", containerdAddress,
"-publish-binary", containerdBinary,
"-socket", socketAddress,
"-debug",
}
opts, ok := ctx.Value(shim.OptsKey{}).(shim.Opts)
if ok && opts.Debug {
args = append(args, "-debug")
}
cmd := exec.Command(self, args...)
cmd.Dir = cwd
cmd.Env = append(os.Environ(), "GOMAXPROCS=2")
@ -190,6 +266,8 @@ func (s *service) StartShim(ctx context.Context, id, containerdBinary, container
// State returns runtime state information for a process
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()
defer s.mu.Unlock()
@ -199,14 +277,26 @@ func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI.
return nil, err
}
rhcs := newRunhcs(p.bundle)
var tstatus string
// This is a container
if p.cid == p.id {
rhcs := newRunhcs(s.debugLog)
cs, err := rhcs.State(ctx, p.id)
if err != nil {
return nil, err
}
tstatus = cs.Status
} else {
if p.started {
tstatus = "running"
} else {
tstatus = "created"
}
}
status := task.StatusUnknown
switch cs.Status {
switch tstatus {
case "created":
status = task.StatusCreated
case "running":
@ -219,8 +309,8 @@ func (s *service) State(ctx context.Context, r *taskAPI.StateRequest) (*taskAPI.
pe := p.stat()
return &taskAPI.StateResponse{
ID: p.id,
Bundle: cs.Bundle,
Pid: uint32(cs.InitProcessPid),
Bundle: p.bundle,
Pid: p.pid,
Status: status,
Stdin: p.stdin,
Stdout: p.stdout,
@ -301,7 +391,7 @@ func writeMountsToConfig(bundle string, mounts []*containerd_types.Mount) error
// Create a new initial process and container with runhcs
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.
s.mu.Lock()
@ -346,16 +436,84 @@ 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)
}
}()
}
}
pidfilePath := path.Join(r.Bundle, "runhcs-pidfile.pid")
copts := &runhcs.CreateOpts{
IO: io,
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)
if err != nil {
return nil, err
@ -391,7 +549,7 @@ func (s *service) Create(ctx context.Context, r *taskAPI.CreateTaskRequest) (*ta
// Start a process
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 err error
if p, err = s.getProcess(r.ID, r.ExecID); err != nil {
@ -404,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")
}
rhcs := newRunhcs(p.bundle)
rhcs := newRunhcs(s.debugLog)
// This is a start/exec
if r.ExecID != "" {
@ -414,10 +572,43 @@ func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.
eopts := &runhcs.ExecOpts{
IO: p.relay.io,
PidFile: pidfilePath,
ShimLog: path.Join(p.bundle, fmt.Sprintf("runhcs-%s-shim.log", execFmt)),
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.
err = rhcs.Exec(ctx, r.ID, procConfig, eopts)
if err != nil {
@ -473,7 +664,7 @@ func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.
// Delete the initial process and container
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 err error
@ -481,14 +672,16 @@ func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAP
return nil, err
}
rhs := newRunhcs(p.bundle)
// This is a container
if p.cid == p.id {
rhs := newRunhcs(s.debugLog)
dopts := &runhcs.DeleteOpts{
Force: true,
}
if err := rhs.Delete(ctx, p.id, dopts); err != nil {
return nil, errors.Wrapf(err, "failed to delete container: %s", p.id)
}
}
select {
case <-time.After(5 * time.Second):
@ -511,11 +704,15 @@ func (s *service) Delete(ctx context.Context, r *taskAPI.DeleteRequest) (*taskAP
// Pids returns all pids inside the container
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
}
// Pause the container
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
var p *process
var err error
@ -523,7 +720,7 @@ func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*ptypes.E
return nil, err
}
rhcs := newRunhcs(p.bundle)
rhcs := newRunhcs(s.debugLog)
if err = rhcs.Pause(ctx, p.id); err != nil {
return nil, err
}
@ -533,6 +730,8 @@ func (s *service) Pause(ctx context.Context, r *taskAPI.PauseRequest) (*ptypes.E
// Resume the container
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
var p *process
var err error
@ -540,7 +739,7 @@ func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (*ptypes
return nil, err
}
rhcs := newRunhcs(p.bundle)
rhcs := newRunhcs(s.debugLog)
if err = rhcs.Resume(ctx, p.id); err != nil {
return nil, err
}
@ -550,12 +749,14 @@ func (s *service) Resume(ctx context.Context, r *taskAPI.ResumeRequest) (*ptypes
// Checkpoint the container
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
}
// Kill a process with the provided signal
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 err error
@ -565,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 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 !strings.Contains(err.Error(), "container is stopped") {
return nil, err
@ -577,7 +778,7 @@ func (s *service) Kill(ctx context.Context, r *taskAPI.KillRequest) (*ptypes.Emp
// Exec an additional process inside the container
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()
defer s.mu.Unlock()
@ -660,6 +861,8 @@ func (s *service) Exec(ctx context.Context, r *taskAPI.ExecProcessRequest) (*pty
// ResizePty of a process
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 err error
if p, err = s.getProcess(r.ID, r.ExecID); err != nil {
@ -670,7 +873,7 @@ func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (*
opts := runhcs.ResizeTTYOpts{
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 {
return nil, err
}
@ -680,6 +883,8 @@ func (s *service) ResizePty(ctx context.Context, r *taskAPI.ResizePtyRequest) (*
// CloseIO of a process
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 err error
if p, err = s.getProcess(r.ID, r.ExecID); err != nil {
@ -691,11 +896,15 @@ func (s *service) CloseIO(ctx context.Context, r *taskAPI.CloseIORequest) (*ptyp
// Update a running container
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
}
// Wait for a process to exit
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 err error
if p, err = s.getProcess(r.ID, r.ExecID); err != nil {
@ -710,11 +919,15 @@ func (s *service) Wait(ctx context.Context, r *taskAPI.WaitRequest) (*taskAPI.Wa
// Stats returns statistics about the running container
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
}
// Connect returns the runhcs shim information
func (s *service) Connect(ctx context.Context, r *taskAPI.ConnectRequest) (*taskAPI.ConnectResponse, error) {
log.G(ctx).Debugf("Connect: %s", r.ID)
return &taskAPI.ConnectResponse{
ShimPid: uint32(os.Getpid()),
TaskPid: s.processes[s.id].pid,
@ -724,7 +937,11 @@ func (s *service) Connect(ctx context.Context, r *taskAPI.ConnectRequest) (*task
// Shutdown stops this instance of the runhcs shim
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)
return empty, nil

View File

@ -53,6 +53,14 @@ type Shim interface {
StartShim(ctx context.Context, id, containerdBinary, containerdAddress string) (string, error)
}
// OptsKey is the context key for the Opts value.
type OptsKey struct{}
// Opts are context options associated with the shim invocation.
type Opts struct {
Debug bool
}
var (
debugFlag bool
idFlag string
@ -133,6 +141,7 @@ func run(id string, initFunc Init) error {
return fmt.Errorf("shim namespace cannot be empty")
}
ctx := namespaces.WithNamespace(context.Background(), namespaceFlag)
ctx = context.WithValue(ctx, OptsKey{}, Opts{Debug: debugFlag})
ctx = log.WithLogger(ctx, log.G(ctx).WithField("runtime", id))
service, err := initFunc(ctx, idFlag, publisher)

View File

@ -0,0 +1,39 @@
/*
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 shim
import (
"context"
"testing"
)
func TestShimOptWithValue(t *testing.T) {
ctx := context.TODO()
ctx = context.WithValue(ctx, OptsKey{}, Opts{Debug: true})
o := ctx.Value(OptsKey{})
if o == nil {
t.Fatal("opts nil")
}
op, ok := o.(Opts)
if !ok {
t.Fatal("opts not of type Opts")
}
if !op.Debug {
t.Fatal("opts.Debug should be true")
}
}

View File

@ -33,7 +33,7 @@ golang.org/x/sync 450f422ab23cf9881c94e2db30cac0eb1b7cf80c
github.com/BurntSushi/toml a368813c5e648fee92e5f6c30e3944ff9d5e8895
github.com/grpc-ecosystem/go-grpc-prometheus 6b7015e65d366bf3f19b2b2a000a831940f0f7e0
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
golang.org/x/text 19e51611da83d6be54ddafce4a4af510cb3e9ea4
github.com/containerd/ttrpc 2a805f71863501300ae1976d29f0454ae003e85a

View File

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

View File

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

View File

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

View File

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