Files
kata-containers/virtcontainers/shim.go
Julio Montes 582f20f489 virtcontainers: Use shim to print the agent logs if there is no proxy
The proxy is in charge to print the agent logs, but when `use_vsocks` is true
the runtime doesn't start the proxy, because it's not needed, hence the agent
logs are ignored. To mitigate this limitation and to make the debugging
processes easier, the fist shim started (the one who monitors the sandbox)
will read the console.sock and print the agent logs.

Depends-on: github.com/kata-containers/shim#172

fixes #1596

Signed-off-by: Julio Montes <julio.montes@intel.com>
2019-04-26 13:15:44 -05:00

282 lines
5.6 KiB
Go

// Copyright (c) 2017 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package virtcontainers
import (
"fmt"
"os"
"os/exec"
"syscall"
"time"
ns "github.com/kata-containers/runtime/virtcontainers/pkg/nsenter"
"github.com/kata-containers/runtime/virtcontainers/types"
"github.com/mitchellh/mapstructure"
"github.com/sirupsen/logrus"
)
// ShimType describes a shim type.
type ShimType string
const (
// NoopShimType is the noopShim.
NoopShimType ShimType = "noopShim"
// KataShimType is the Kata Containers shim type.
KataShimType ShimType = "kataShim"
// KataBuiltInShimType is the Kata Containers builtin shim type.
KataBuiltInShimType ShimType = "kataBuiltInShim"
)
var waitForShimTimeout = 10.0
var consoleFileMode = os.FileMode(0660)
// ShimParams is the structure providing specific parameters needed
// for the execution of the shim binary.
type ShimParams struct {
Container string
Token string
URL string
Console string
ConsoleURL string
Terminal bool
Detach bool
PID int
CreateNS []ns.NSType
EnterNS []ns.Namespace
}
// ShimConfig is the structure providing specific configuration
// for shim implementations.
type ShimConfig struct {
Path string
Debug bool
Trace bool
}
// Set sets a shim type based on the input string.
func (pType *ShimType) Set(value string) error {
switch value {
case "noopShim":
*pType = NoopShimType
case "kataShim":
*pType = KataShimType
case "kataBuiltInShim":
*pType = KataBuiltInShimType
default:
return fmt.Errorf("Unknown shim type %s", value)
}
return nil
}
// String converts a shim type to a string.
func (pType *ShimType) String() string {
switch *pType {
case NoopShimType:
return string(NoopShimType)
case KataShimType:
return string(KataShimType)
case KataBuiltInShimType:
return string(KataBuiltInShimType)
default:
return ""
}
}
// newShim returns a shim from a shim type.
func newShim(pType ShimType) (shim, error) {
switch pType {
case NoopShimType:
return &noopShim{}, nil
case KataShimType:
return &kataShim{}, nil
case KataBuiltInShimType:
return &kataBuiltInShim{}, nil
default:
return &noopShim{}, nil
}
}
// newShimConfig returns a shim config from a generic SandboxConfig interface.
func newShimConfig(config SandboxConfig) interface{} {
switch config.ShimType {
case NoopShimType, KataBuiltInShimType:
return nil
case KataShimType:
var shimConfig ShimConfig
err := mapstructure.Decode(config.ShimConfig, &shimConfig)
if err != nil {
return err
}
return shimConfig
default:
return nil
}
}
func shimLogger() *logrus.Entry {
return virtLog.WithField("subsystem", "shim")
}
func signalShim(pid int, sig syscall.Signal) error {
if pid <= 0 {
return nil
}
shimLogger().WithFields(
logrus.Fields{
"shim-pid": pid,
"shim-signal": sig,
}).Info("Signalling shim")
return syscall.Kill(pid, sig)
}
func stopShim(pid int) error {
if pid <= 0 {
return nil
}
if err := signalShim(pid, syscall.SIGKILL); err != nil && err != syscall.ESRCH {
return err
}
return nil
}
func prepareAndStartShim(sandbox *Sandbox, shim shim, cid, token, url, consoleURL string, cmd types.Cmd,
createNSList []ns.NSType, enterNSList []ns.Namespace) (*Process, error) {
process := &Process{
Token: token,
StartTime: time.Now().UTC(),
}
shimParams := ShimParams{
Container: cid,
Token: token,
URL: url,
Console: cmd.Console,
Terminal: cmd.Interactive,
Detach: cmd.Detach,
CreateNS: createNSList,
EnterNS: enterNSList,
ConsoleURL: consoleURL,
}
pid, err := shim.start(sandbox, shimParams)
if err != nil {
return nil, err
}
process.Pid = pid
return process, nil
}
func startShim(args []string, params ShimParams) (int, error) {
cmd := exec.Command(args[0], args[1:]...)
if !params.Detach {
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
}
cloneFlags := 0
for _, nsType := range params.CreateNS {
cloneFlags |= ns.CloneFlagsTable[nsType]
}
cmd.SysProcAttr = &syscall.SysProcAttr{
Cloneflags: uintptr(cloneFlags),
}
var f *os.File
var err error
if params.Console != "" {
f, err = os.OpenFile(params.Console, os.O_RDWR, consoleFileMode)
if err != nil {
return -1, err
}
cmd.Stdin = f
cmd.Stdout = f
cmd.Stderr = f
// Create Session
cmd.SysProcAttr.Setsid = true
// Set Controlling terminal to Ctty
cmd.SysProcAttr.Setctty = true
cmd.SysProcAttr.Ctty = int(f.Fd())
}
defer func() {
if f != nil {
f.Close()
}
}()
if err := ns.NsEnter(params.EnterNS, func() error {
return cmd.Start()
}); err != nil {
return -1, err
}
return cmd.Process.Pid, nil
}
func isShimRunning(pid int) (bool, error) {
if pid <= 0 {
return false, nil
}
process, err := os.FindProcess(pid)
if err != nil {
return false, err
}
if err := process.Signal(syscall.Signal(0)); err != nil {
return false, nil
}
return true, nil
}
// waitForShim waits for the end of the shim unless it reaches the timeout
// first, returning an error in that case.
func waitForShim(pid int) error {
if pid <= 0 {
return nil
}
tInit := time.Now()
for {
running, err := isShimRunning(pid)
if err != nil {
return err
}
if !running {
break
}
if time.Since(tInit).Seconds() >= waitForShimTimeout {
return fmt.Errorf("Shim still running, timeout %f s has been reached", waitForShimTimeout)
}
// Let's avoid to run a too busy loop
time.Sleep(time.Duration(100) * time.Millisecond)
}
return nil
}
// shim is the virtcontainers shim interface.
type shim interface {
// start starts the shim relying on its configuration and on
// parameters provided.
start(sandbox *Sandbox, params ShimParams) (int, error)
}