tracing: Add initial opentracing support

Add initial support for opentracing by using the `jaeger` package.
Since opentracing uses the `context` package, add a `context.Context`
as the first parameter to all the functions that we might want to
trace. Trace "spans" (trace points) are then added by extracting the
trace details from the specified context parameter.

Notes:

- Although the tracer is created in `main()`, the "root span"
  (aka the first trace point) is not added until `beforeSubcommands()`.

  This is by design and is a compromise: by delaying the creation of the
  root span, the spans become much more readable since using the web-based
  JaegerUI, you will see traces like this:

  ```
  kata-runtime: kata-runtime create
  ------------  -------------------
       ^                ^
       |                |
  Trace name        First span name
                    (which clearly shows the CLI command that was run)
  ```

  Creating the span earlier means it is necessary to expand 'n' spans in
  the UI before you get to see the name of the CLI command that was run.
  In adding support, this became very tedious, hence my design decision to
  defer the creation of the root span until after signal handling has been
  setup and after CLI options have been parsed, but still very early in
  the code path.

  - At this stage, the tracing stops at the `virtcontainers` call
  boundary.

- Tracing is "always on" as there doesn't appear to be a way to toggle
  it. However, its resolves to a "nop" unless the tracer can talk to a
  jaeger agent.

Note that this commit required a bit of rework to `beforeSubcommands()`
to reduce the cyclomatic complexity.

Fixes #557.

Signed-off-by: James O. D. Hunt <james.o.hunt@intel.com>
This commit is contained in:
James O. D. Hunt
2018-08-09 15:07:32 +01:00
parent 0ede467256
commit 3a1bbd0271
138 changed files with 20465 additions and 154 deletions

View File

@@ -102,6 +102,7 @@ type proxy struct {
type runtime struct {
Debug bool `toml:"enable_debug"`
Tracing bool `toml:"enable_tracing"`
InterNetworkModel string `toml:"internetworking_model"`
}
@@ -538,6 +539,10 @@ func loadConfiguration(configPath string, ignoreLogging bool) (resolvedConfigPat
kataLog.Logger.Level = originalLoggerLevel
}
if tomlConf.Runtime.Tracing {
tracing = true
}
if tomlConf.Runtime.InterNetworkModel != "" {
err = config.InterNetworkModel.SetModel(tomlConf.Runtime.InterNetworkModel)
if err != nil {

View File

@@ -189,3 +189,8 @@ path = "@SHIMPATH@"
# Used when the Container network interface can be bridged using
# macvtap.
internetworking_model="@DEFNETWORKMODEL@"
# If enabled, the runtime will create opentracing.io traces and spans.
# (See https://www.jaegertracing.io/docs/getting-started).
# (default: disabled)
#enable_tracing = true

View File

@@ -7,6 +7,7 @@
package main
import (
"context"
"errors"
"fmt"
"io/ioutil"
@@ -17,6 +18,7 @@ import (
vc "github.com/kata-containers/runtime/virtcontainers"
vf "github.com/kata-containers/runtime/virtcontainers/factory"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
opentracing "github.com/opentracing/opentracing-go"
"github.com/urfave/cli"
)
@@ -62,6 +64,11 @@ var createCLICommand = cli.Command{
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig)
if !ok {
return errors.New("invalid runtime config")
@@ -72,7 +79,7 @@ var createCLICommand = cli.Command{
return err
}
return create(context.Args().First(),
return create(ctx, context.Args().First(),
context.String("bundle"),
console,
context.String("pid-file"),
@@ -85,12 +92,16 @@ var createCLICommand = cli.Command{
// Use a variable to allow tests to modify its value
var getKernelParamsFunc = getKernelParams
func create(containerID, bundlePath, console, pidFilePath string, detach bool,
func create(ctx context.Context, containerID, bundlePath, console, pidFilePath string, detach bool,
runtimeConfig oci.RuntimeConfig) error {
var err error
span, ctx := opentracing.StartSpanFromContext(ctx, "create")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
if bundlePath, err = validCreateParams(containerID, bundlePath); err != nil {
@@ -136,12 +147,12 @@ func create(containerID, bundlePath, console, pidFilePath string, detach bool,
var process vc.Process
switch containerType {
case vc.PodSandbox:
process, err = createSandbox(ociSpec, runtimeConfig, containerID, bundlePath, console, disableOutput)
process, err = createSandbox(ctx, ociSpec, runtimeConfig, containerID, bundlePath, console, disableOutput)
if err != nil {
return err
}
case vc.PodContainer:
process, err = createContainer(ociSpec, containerID, bundlePath, console, disableOutput)
process, err = createContainer(ctx, ociSpec, containerID, bundlePath, console, disableOutput)
if err != nil {
return err
}
@@ -152,7 +163,7 @@ func create(containerID, bundlePath, console, pidFilePath string, detach bool,
// is shim's in our case. This is mandatory to make sure there is no one
// else (like Docker) trying to create those files on our behalf. We want to
// know those files location so that we can remove them when delete is called.
cgroupsPathList, err := processCgroupsPath(ociSpec, containerType.IsSandbox())
cgroupsPathList, err := processCgroupsPath(ctx, ociSpec, containerType.IsSandbox())
if err != nil {
return err
}
@@ -163,14 +174,14 @@ func create(containerID, bundlePath, console, pidFilePath string, detach bool,
cgroupsDirPath = ociSpec.Linux.CgroupsPath
}
if err := createCgroupsFiles(containerID, cgroupsDirPath, cgroupsPathList, process.Pid); err != nil {
if err := createCgroupsFiles(ctx, containerID, cgroupsDirPath, cgroupsPathList, process.Pid); err != nil {
return err
}
// Creation of PID file has to be the last thing done in the create
// because containerd considers the create complete after this file
// is created.
return createPIDFile(pidFilePath, process.Pid)
return createPIDFile(ctx, pidFilePath, process.Pid)
}
var systemdKernelParam = []vc.Param{
@@ -241,8 +252,10 @@ func setKernelParams(containerID string, runtimeConfig *oci.RuntimeConfig) error
return nil
}
func createSandbox(ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig,
func createSandbox(ctx context.Context, ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig,
containerID, bundlePath, console string, disableOutput bool) (vc.Process, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, "createSandbox")
defer span.Finish()
err := setKernelParams(containerID, &runtimeConfig)
if err != nil {
@@ -259,15 +272,17 @@ func createSandbox(ociSpec oci.CompatOCISpec, runtimeConfig oci.RuntimeConfig,
return vc.Process{}, err
}
kataLog = kataLog.WithField("sandbox", sandbox.ID())
sid := sandbox.ID()
kataLog = kataLog.WithField("sandbox", sid)
setExternalLoggers(kataLog)
span.SetTag("sandbox", sid)
containers := sandbox.GetAllContainers()
if len(containers) != 1 {
return vc.Process{}, fmt.Errorf("BUG: Container list from sandbox is wrong, expecting only one container, found %d containers", len(containers))
}
if err := addContainerIDMapping(containerID, sandbox.ID()); err != nil {
if err := addContainerIDMapping(ctx, containerID, sandbox.ID()); err != nil {
return vc.Process{}, err
}
@@ -288,9 +303,12 @@ func setEphemeralStorageType(ociSpec oci.CompatOCISpec) oci.CompatOCISpec {
return ociSpec
}
func createContainer(ociSpec oci.CompatOCISpec, containerID, bundlePath,
func createContainer(ctx context.Context, ociSpec oci.CompatOCISpec, containerID, bundlePath,
console string, disableOutput bool) (vc.Process, error) {
span, ctx := opentracing.StartSpanFromContext(ctx, "createContainer")
defer span.Finish()
ociSpec = setEphemeralStorageType(ociSpec)
contConfig, err := oci.ContainerConfig(ociSpec, bundlePath, containerID, console, disableOutput)
@@ -305,20 +323,24 @@ func createContainer(ociSpec oci.CompatOCISpec, containerID, bundlePath,
kataLog = kataLog.WithField("sandbox", sandboxID)
setExternalLoggers(kataLog)
span.SetTag("sandbox", sandboxID)
_, c, err := vci.CreateContainer(sandboxID, contConfig)
if err != nil {
return vc.Process{}, err
}
if err := addContainerIDMapping(containerID, sandboxID); err != nil {
if err := addContainerIDMapping(ctx, containerID, sandboxID); err != nil {
return vc.Process{}, err
}
return c.Process(), nil
}
func createCgroupsFiles(containerID string, cgroupsDirPath string, cgroupsPathList []string, pid int) error {
func createCgroupsFiles(ctx context.Context, containerID string, cgroupsDirPath string, cgroupsPathList []string, pid int) error {
span, _ := opentracing.StartSpanFromContext(ctx, "createCgroupsFiles")
defer span.Finish()
if len(cgroupsPathList) == 0 {
kataLog.WithField("pid", pid).Info("Cgroups files not created because cgroupsPath was empty")
return nil
@@ -361,7 +383,10 @@ func createCgroupsFiles(containerID string, cgroupsDirPath string, cgroupsPathLi
return nil
}
func createPIDFile(pidFilePath string, pid int) error {
func createPIDFile(ctx context.Context, pidFilePath string, pid int) error {
span, _ := opentracing.StartSpanFromContext(ctx, "createPIDFile")
defer span.Finish()
if pidFilePath == "" {
// runtime should not fail since pid file is optional
return nil

View File

@@ -6,6 +6,7 @@
package main
import (
"context"
"errors"
"flag"
"fmt"
@@ -45,7 +46,7 @@ func mockCPUSetContent(contents map[string]string) error {
}
func testCreateCgroupsFilesSuccessful(t *testing.T, cgroupsDirPath string, cgroupsPathList []string, pid int) {
if err := createCgroupsFiles("foo", cgroupsDirPath, cgroupsPathList, pid); err != nil {
if err := createCgroupsFiles(context.Background(), "foo", cgroupsDirPath, cgroupsPathList, pid); err != nil {
t.Fatalf("This test should succeed (cgroupsPath %q, pid %d): %s", cgroupsPathList, pid, err)
}
}
@@ -95,7 +96,7 @@ func TestCreateCgroupsFilesFailToWriteFile(t *testing.T) {
files := []string{file}
err = createCgroupsFiles("foo", "cgroups-file", files, testPID)
err = createCgroupsFiles(context.Background(), "foo", "cgroups-file", files, testPID)
assert.Error(err)
}
@@ -135,7 +136,7 @@ func TestCreatePIDFileSuccessful(t *testing.T) {
}
pidFilePath := filepath.Join(pidDirPath, "pid-file-path")
if err := createPIDFile(pidFilePath, testPID); err != nil {
if err := createPIDFile(context.Background(), pidFilePath, testPID); err != nil {
t.Fatal(err)
}
@@ -155,7 +156,7 @@ func TestCreatePIDFileSuccessful(t *testing.T) {
func TestCreatePIDFileEmptyPathSuccessful(t *testing.T) {
file := ""
if err := createPIDFile(file, testPID); err != nil {
if err := createPIDFile(context.Background(), file, testPID); err != nil {
t.Fatalf("This test should not fail (pidFilePath %q, pid %d)", file, testPID)
}
}
@@ -179,7 +180,7 @@ func TestCreatePIDFileUnableToRemove(t *testing.T) {
err = os.MkdirAll(subdir, os.FileMode(0000))
assert.NoError(err)
err = createPIDFile(file, testPID)
err = createPIDFile(context.Background(), file, testPID)
assert.Error(err)
// let it be deleted
@@ -196,7 +197,7 @@ func TestCreatePIDFileUnableToCreate(t *testing.T) {
subdir := filepath.Join(tmpdir, "dir")
file := filepath.Join(subdir, "pidfile")
err = createPIDFile(file, testPID)
err = createPIDFile(context.Background(), file, testPID)
// subdir doesn't exist
assert.Error(err)
@@ -337,7 +338,7 @@ func TestCreateInvalidArgs(t *testing.T) {
}
for i, d := range data {
err := create(d.containerID, d.bundlePath, d.console, d.pidFilePath, d.detach, d.runtimeConfig)
err := create(context.Background(), d.containerID, d.bundlePath, d.console, d.pidFilePath, d.detach, d.runtimeConfig)
assert.Errorf(err, "test %d (%+v)", i, d)
}
}
@@ -376,7 +377,7 @@ func TestCreateInvalidConfigJSON(t *testing.T) {
f.Close()
for detach := range []bool{true, false} {
err := create(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
@@ -420,7 +421,7 @@ func TestCreateInvalidContainerType(t *testing.T) {
assert.NoError(err)
for detach := range []bool{true, false} {
err := create(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
@@ -465,7 +466,7 @@ func TestCreateContainerInvalid(t *testing.T) {
assert.NoError(err)
for detach := range []bool{true, false} {
err := create(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
@@ -556,7 +557,7 @@ func TestCreateProcessCgroupsPathSuccessful(t *testing.T) {
assert.NoError(err)
for _, detach := range []bool{true, false} {
err := create(testContainerID, bundlePath, testConsole, pidFilePath, detach, runtimeConfig)
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, detach, runtimeConfig)
assert.NoError(err, "detached: %+v", detach)
os.RemoveAll(path)
}
@@ -640,7 +641,7 @@ func TestCreateCreateCgroupsFilesFail(t *testing.T) {
assert.NoError(err)
for detach := range []bool{true, false} {
err := create(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
@@ -716,7 +717,7 @@ func TestCreateCreateCreatePidFileFail(t *testing.T) {
assert.NoError(err)
for detach := range []bool{true, false} {
err := create(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
@@ -782,7 +783,7 @@ func TestCreate(t *testing.T) {
assert.NoError(err)
for detach := range []bool{true, false} {
err := create(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
assert.NoError(err, "%+v", detach)
os.RemoveAll(path)
}
@@ -839,7 +840,7 @@ func TestCreateInvalidKernelParams(t *testing.T) {
}
for detach := range []bool{true, false} {
err := create(testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
err := create(context.Background(), testContainerID, bundlePath, testConsole, pidFilePath, true, runtimeConfig)
assert.Errorf(err, "%+v", detach)
assert.False(vcmock.IsMockError(err))
os.RemoveAll(path)
@@ -884,7 +885,7 @@ func TestCreateSandboxConfigFail(t *testing.T) {
Quota: &quota,
}
_, err = createSandbox(spec, runtimeConfig, testContainerID, bundlePath, testConsole, true)
_, err = createSandbox(context.Background(), spec, runtimeConfig, testContainerID, bundlePath, testConsole, true)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
@@ -915,7 +916,7 @@ func TestCreateCreateSandboxFail(t *testing.T) {
spec, err := readOCIConfigFile(ociConfigFile)
assert.NoError(err)
_, err = createSandbox(spec, runtimeConfig, testContainerID, bundlePath, testConsole, true)
_, err = createSandbox(context.Background(), spec, runtimeConfig, testContainerID, bundlePath, testConsole, true)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
}
@@ -953,7 +954,7 @@ func TestCreateCreateContainerContainerConfigFail(t *testing.T) {
assert.NoError(err)
for _, disableOutput := range []bool{true, false} {
_, err = createContainer(spec, testContainerID, bundlePath, testConsole, disableOutput)
_, err = createContainer(context.Background(), spec, testContainerID, bundlePath, testConsole, disableOutput)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
assert.True(strings.Contains(err.Error(), containerType))
@@ -994,7 +995,7 @@ func TestCreateCreateContainerFail(t *testing.T) {
assert.NoError(err)
for _, disableOutput := range []bool{true, false} {
_, err = createContainer(spec, testContainerID, bundlePath, testConsole, disableOutput)
_, err = createContainer(context.Background(), spec, testContainerID, bundlePath, testConsole, disableOutput)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
os.RemoveAll(path)
@@ -1060,7 +1061,7 @@ func TestCreateCreateContainer(t *testing.T) {
assert.NoError(err)
for _, disableOutput := range []bool{true, false} {
_, err = createContainer(spec, testContainerID, bundlePath, testConsole, disableOutput)
_, err = createContainer(context.Background(), spec, testContainerID, bundlePath, testConsole, disableOutput)
assert.NoError(err)
os.RemoveAll(path)
}

View File

@@ -7,11 +7,13 @@
package main
import (
"context"
"fmt"
"os"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -36,6 +38,11 @@ EXAMPLE:
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
args := context.Args()
if args.Present() == false {
return fmt.Errorf("Missing container ID, should at least provide one")
@@ -43,7 +50,7 @@ EXAMPLE:
force := context.Bool("force")
for _, cID := range []string(args) {
if err := delete(cID, force); err != nil {
if err := delete(ctx, cID, force); err != nil {
return err
}
}
@@ -52,9 +59,13 @@ EXAMPLE:
},
}
func delete(containerID string, force bool) error {
func delete(ctx context.Context, containerID string, force bool) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "delete")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
status, sandboxID, err := getExistingContainerInfo(containerID)
@@ -71,6 +82,9 @@ func delete(containerID string, force bool) error {
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
containerType, err := oci.GetContainerType(status.Annotations)
if err != nil {
return err
@@ -93,11 +107,11 @@ func delete(containerID string, force bool) error {
switch containerType {
case vc.PodSandbox:
if err := deleteSandbox(sandboxID); err != nil {
if err := deleteSandbox(ctx, sandboxID); err != nil {
return err
}
case vc.PodContainer:
if err := deleteContainer(sandboxID, containerID, forceStop); err != nil {
if err := deleteContainer(ctx, sandboxID, containerID, forceStop); err != nil {
return err
}
default:
@@ -107,19 +121,22 @@ func delete(containerID string, force bool) error {
// In order to prevent any file descriptor leak related to cgroups files
// that have been previously created, we have to remove them before this
// function returns.
cgroupsPathList, err := processCgroupsPath(ociSpec, containerType.IsSandbox())
cgroupsPathList, err := processCgroupsPath(ctx, ociSpec, containerType.IsSandbox())
if err != nil {
return err
}
if err := delContainerIDMapping(containerID); err != nil {
if err := delContainerIDMapping(ctx, containerID); err != nil {
return err
}
return removeCgroupsPath(containerID, cgroupsPathList)
return removeCgroupsPath(ctx, containerID, cgroupsPathList)
}
func deleteSandbox(sandboxID string) error {
func deleteSandbox(ctx context.Context, sandboxID string) error {
span, _ := opentracing.StartSpanFromContext(ctx, "deleteSandbox")
defer span.Finish()
status, err := vci.StatusSandbox(sandboxID)
if err != nil {
return err
@@ -138,7 +155,10 @@ func deleteSandbox(sandboxID string) error {
return nil
}
func deleteContainer(sandboxID, containerID string, forceStop bool) error {
func deleteContainer(ctx context.Context, sandboxID, containerID string, forceStop bool) error {
span, _ := opentracing.StartSpanFromContext(ctx, "deleteContainer")
defer span.Finish()
if forceStop {
if _, err := vci.StopContainer(sandboxID, containerID); err != nil {
return err
@@ -152,7 +172,10 @@ func deleteContainer(sandboxID, containerID string, forceStop bool) error {
return nil
}
func removeCgroupsPath(containerID string, cgroupsPathList []string) error {
func removeCgroupsPath(ctx context.Context, containerID string, cgroupsPathList []string) error {
span, _ := opentracing.StartSpanFromContext(ctx, "removeCgroupsPath")
defer span.Finish()
if len(cgroupsPathList) == 0 {
kataLog.WithField("container", containerID).Info("Cgroups files not removed because cgroupsPath was empty")
return nil

View File

@@ -6,6 +6,7 @@
package main
import (
"context"
"flag"
"io/ioutil"
"os"
@@ -20,7 +21,7 @@ import (
)
func testRemoveCgroupsPathSuccessful(t *testing.T, cgroupsPathList []string) {
if err := removeCgroupsPath("foo", cgroupsPathList); err != nil {
if err := removeCgroupsPath(context.Background(), "foo", cgroupsPathList); err != nil {
t.Fatalf("This test should succeed (cgroupsPathList = %v): %s", cgroupsPathList, err)
}
}
@@ -50,7 +51,7 @@ func TestDeleteInvalidContainer(t *testing.T) {
assert := assert.New(t)
// Missing container id
err := delete("", false)
err := delete(context.Background(), "", false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
@@ -60,7 +61,7 @@ func TestDeleteInvalidContainer(t *testing.T) {
ctrsMapTreePath = path
// Container missing in ListSandbox
err = delete(testContainerID, false)
err = delete(context.Background(), testContainerID, false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
@@ -87,7 +88,7 @@ func TestDeleteMissingContainerTypeAnnotation(t *testing.T) {
testingImpl.StatusContainerFunc = nil
}()
err = delete(sandbox.ID(), false)
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
@@ -116,7 +117,7 @@ func TestDeleteInvalidConfig(t *testing.T) {
testingImpl.StatusContainerFunc = nil
}()
err = delete(sandbox.ID(), false)
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
@@ -172,7 +173,7 @@ func TestDeleteSandbox(t *testing.T) {
testingImpl.StatusContainerFunc = nil
}()
err = delete(sandbox.ID(), false)
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -189,7 +190,7 @@ func TestDeleteSandbox(t *testing.T) {
testingImpl.StatusSandboxFunc = nil
}()
err = delete(sandbox.ID(), false)
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -201,7 +202,7 @@ func TestDeleteSandbox(t *testing.T) {
testingImpl.StopSandboxFunc = nil
}()
err = delete(sandbox.ID(), false)
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -213,7 +214,7 @@ func TestDeleteSandbox(t *testing.T) {
testingImpl.DeleteSandboxFunc = nil
}()
err = delete(sandbox.ID(), false)
err = delete(context.Background(), sandbox.ID(), false)
assert.Nil(err)
}
@@ -251,7 +252,7 @@ func TestDeleteInvalidContainerType(t *testing.T) {
}()
// Delete an invalid container type
err = delete(sandbox.ID(), false)
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
@@ -290,7 +291,7 @@ func TestDeleteSandboxRunning(t *testing.T) {
}()
// Delete on a running sandbox should fail
err = delete(sandbox.ID(), false)
err = delete(context.Background(), sandbox.ID(), false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
@@ -313,7 +314,7 @@ func TestDeleteSandboxRunning(t *testing.T) {
}()
// Force delete a running sandbox
err = delete(sandbox.ID(), true)
err = delete(context.Background(), sandbox.ID(), true)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -325,7 +326,7 @@ func TestDeleteSandboxRunning(t *testing.T) {
testingImpl.DeleteSandboxFunc = nil
}()
err = delete(sandbox.ID(), true)
err = delete(context.Background(), sandbox.ID(), true)
assert.Nil(err)
}
@@ -370,7 +371,7 @@ func TestDeleteRunningContainer(t *testing.T) {
}()
// Delete on a running container should fail.
err = delete(sandbox.MockContainers[0].ID(), false)
err = delete(context.Background(), sandbox.MockContainers[0].ID(), false)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
@@ -379,7 +380,7 @@ func TestDeleteRunningContainer(t *testing.T) {
defer os.RemoveAll(path)
// force delete
err = delete(sandbox.MockContainers[0].ID(), true)
err = delete(context.Background(), sandbox.MockContainers[0].ID(), true)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -392,7 +393,7 @@ func TestDeleteRunningContainer(t *testing.T) {
assert.NoError(err)
defer os.RemoveAll(path)
err = delete(sandbox.MockContainers[0].ID(), true)
err = delete(context.Background(), sandbox.MockContainers[0].ID(), true)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -408,7 +409,7 @@ func TestDeleteRunningContainer(t *testing.T) {
assert.NoError(err)
defer os.RemoveAll(path)
err = delete(sandbox.MockContainers[0].ID(), true)
err = delete(context.Background(), sandbox.MockContainers[0].ID(), true)
assert.Nil(err)
}
@@ -452,7 +453,7 @@ func TestDeleteContainer(t *testing.T) {
testingImpl.StatusContainerFunc = nil
}()
err = delete(sandbox.MockContainers[0].ID(), false)
err = delete(context.Background(), sandbox.MockContainers[0].ID(), false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -465,7 +466,7 @@ func TestDeleteContainer(t *testing.T) {
testingImpl.StopContainerFunc = nil
}()
err = delete(sandbox.MockContainers[0].ID(), false)
err = delete(context.Background(), sandbox.MockContainers[0].ID(), false)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -481,7 +482,7 @@ func TestDeleteContainer(t *testing.T) {
assert.NoError(err)
defer os.RemoveAll(path)
err = delete(sandbox.MockContainers[0].ID(), false)
err = delete(context.Background(), sandbox.MockContainers[0].ID(), false)
assert.Nil(err)
}
@@ -613,6 +614,6 @@ func TestRemoveCGroupsPath(t *testing.T) {
_ = os.Chmod(tmpdir, 0755)
}()
err = removeCgroupsPath("foo", []string{dir})
err = removeCgroupsPath(context.Background(), "foo", []string{dir})
assert.Error(err)
}

View File

@@ -14,6 +14,7 @@ import (
"time"
vc "github.com/kata-containers/runtime/virtcontainers"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
@@ -136,6 +137,14 @@ information is displayed once every 5 seconds.`,
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, _ := opentracing.StartSpanFromContext(ctx, "events")
defer span.Finish()
containerID := context.Args().First()
if containerID == "" {
return fmt.Errorf("container id cannot be empty")
@@ -143,6 +152,7 @@ information is displayed once every 5 seconds.`,
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
duration := context.Duration("interval")
if duration <= 0 {
@@ -162,6 +172,8 @@ information is displayed once every 5 seconds.`,
})
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
if status.State.State == vc.StateStopped {
return fmt.Errorf("container with id %s is not running", status.ID)

View File

@@ -7,6 +7,7 @@
package main
import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
@@ -16,6 +17,7 @@ import (
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
specs "github.com/opencontainers/runtime-spec/specs-go"
opentracing "github.com/opentracing/opentracing-go"
"github.com/urfave/cli"
)
@@ -107,7 +109,12 @@ EXAMPLE:
},
},
Action: func(context *cli.Context) error {
return execute(context)
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
return execute(ctx, context)
},
}
@@ -181,11 +188,15 @@ func generateExecParams(context *cli.Context, specProcess *oci.CompatOCIProcess)
return params, nil
}
func execute(context *cli.Context) error {
func execute(ctx context.Context, context *cli.Context) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "execute")
defer span.Finish()
containerID := context.Args().First()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
status, sandboxID, err := getExistingContainerInfo(containerID)
if err != nil {
@@ -194,6 +205,7 @@ func execute(context *cli.Context) error {
kataLog = kataLog.WithField("sandbox", sandboxID)
setExternalLoggers(kataLog)
span.SetTag("sandbox", sandboxID)
// Retrieve OCI spec configuration.
ociSpec, err := oci.GetOCIConfig(status)
@@ -211,6 +223,7 @@ func execute(context *cli.Context) error {
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
// container MUST be ready or running.
if status.State.State != vc.StateReady &&
@@ -253,7 +266,7 @@ func execute(context *cli.Context) error {
// Creation of PID file has to be the last thing done in the exec
// because containerd considers the exec to have finished starting
// after this file is created.
if err := createPIDFile(params.pidFile, process.Pid); err != nil {
if err := createPIDFile(ctx, params.pidFile, process.Pid); err != nil {
return err
}

View File

@@ -6,6 +6,7 @@
package main
import (
"context"
"flag"
"io/ioutil"
"os"
@@ -56,7 +57,7 @@ func TestExecuteErrors(t *testing.T) {
ctx := createCLIContext(flagSet)
// missing container id
err := execute(ctx)
err := execute(context.Background(), ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
@@ -66,7 +67,7 @@ func TestExecuteErrors(t *testing.T) {
// StatusSandbox error
flagSet.Parse([]string{testContainerID})
err = execute(ctx)
err = execute(context.Background(), ctx)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -83,7 +84,7 @@ func TestExecuteErrors(t *testing.T) {
testingImpl.StatusContainerFunc = nil
}()
err = execute(ctx)
err = execute(context.Background(), ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
@@ -103,7 +104,7 @@ func TestExecuteErrors(t *testing.T) {
return newSingleContainerStatus(testContainerID, containerState, annotations), nil
}
err = execute(ctx)
err = execute(context.Background(), ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
@@ -115,7 +116,7 @@ func TestExecuteErrors(t *testing.T) {
return newSingleContainerStatus(testContainerID, containerState, annotations), nil
}
err = execute(ctx)
err = execute(context.Background(), ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
@@ -127,7 +128,7 @@ func TestExecuteErrors(t *testing.T) {
return newSingleContainerStatus(testContainerID, containerState, annotations), nil
}
err = execute(ctx)
err = execute(context.Background(), ctx)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
@@ -295,6 +296,7 @@ func TestExecuteWithFlags(t *testing.T) {
// EnterContainer error
err = fn(ctx)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
testingImpl.EnterContainerFunc = func(sandboxID, containerID string, cmd vc.Cmd) (vc.VCSandbox, vc.VCContainer, *vc.Process, error) {

View File

@@ -24,6 +24,7 @@ import (
"syscall"
vc "github.com/kata-containers/runtime/virtcontainers"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -282,6 +283,13 @@ var kataCheckCLICommand = cli.Command{
Name: checkCmd,
Usage: "tests if system can run " + project,
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, _ := opentracing.StartSpanFromContext(ctx, "kata-check")
defer span.Finish()
setCPUtype()
@@ -292,7 +300,7 @@ var kataCheckCLICommand = cli.Command{
requiredKernelModules: archRequiredKernelModules,
}
err := hostIsVMContainerCapable(details)
err = hostIsVMContainerCapable(details)
if err != nil {
return err

View File

@@ -17,6 +17,7 @@ import (
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
specs "github.com/opencontainers/runtime-spec/specs-go"
opentracing "github.com/opentracing/opentracing-go"
"github.com/urfave/cli"
)
@@ -398,6 +399,14 @@ var kataEnvCLICommand = cli.Command{
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, _ := opentracing.StartSpanFromContext(ctx, "kata-env")
defer span.Finish()
return handleSettings(defaultOutputFile, context)
},
}

View File

@@ -7,12 +7,14 @@
package main
import (
"context"
"fmt"
"strconv"
"syscall"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -37,6 +39,11 @@ EXAMPLE:
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
args := context.Args()
if args.Present() == false {
return fmt.Errorf("Missing container ID")
@@ -48,7 +55,7 @@ EXAMPLE:
signal = "SIGTERM"
}
return kill(args.First(), signal, context.Bool("all"))
return kill(ctx, args.First(), signal, context.Bool("all"))
},
}
@@ -90,9 +97,13 @@ var signals = map[string]syscall.Signal{
"SIGXFSZ": syscall.SIGXFSZ,
}
func kill(containerID, signal string, all bool) error {
func kill(ctx context.Context, containerID, signal string, all bool) error {
span, _ := opentracing.StartSpanFromContext(ctx, "kill")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
status, sandboxID, err := getExistingContainerInfo(containerID)
@@ -108,6 +119,9 @@ func kill(containerID, signal string, all bool) error {
"sandbox": sandboxID,
})
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
setExternalLoggers(kataLog)
signum, err := processSignal(signal)

View File

@@ -16,6 +16,7 @@ import (
"text/tabwriter"
"time"
opentracing "github.com/opentracing/opentracing-go"
"github.com/urfave/cli"
vc "github.com/kata-containers/runtime/virtcontainers"
@@ -108,6 +109,14 @@ To list containers created using a non-default value for "--root":
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, _ := opentracing.StartSpanFromContext(ctx, "list")
defer span.Finish()
s, err := getContainers(context)
if err != nil {
return err

View File

@@ -7,6 +7,7 @@
package main
import (
"context"
"errors"
"fmt"
"io"
@@ -20,6 +21,7 @@ import (
vf "github.com/kata-containers/runtime/virtcontainers/factory"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
specs "github.com/opencontainers/runtime-spec/specs-go"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -56,6 +58,9 @@ var originalLoggerLevel logrus.Level
var debug = false
// if true, enable opentracing support.
var tracing = false
// if true, coredump when an internal error occurs or a fatal signal is received
var crashOnError = false
@@ -131,6 +136,10 @@ var runtimeCommands = []cli.Command{
// parsing occurs.
var runtimeBeforeSubcommands = beforeSubcommands
// runtimeAfterSubcommands is the function to run after the command-line
// has been parsed.
var runtimeAfterSubcommands = afterSubcommands
// runtimeCommandNotFound is the function to handle an invalid sub-command.
var runtimeCommandNotFound = commandNotFound
@@ -161,7 +170,13 @@ func init() {
kataLog.Logger.Level = logrus.DebugLevel
}
func setupSignalHandler() {
// setupSignalHandler sets up signal handling, starting a go routine to deal
// with signals as they arrive.
//
// Note that the specified context is NOT used to create a trace span (since the
// first (root) span must be created in beforeSubcommands()): it is simply
// used to pass to the crash handling functions to finalise tracing.
func setupSignalHandler(ctx context.Context) {
sigCh := make(chan os.Signal, 8)
for _, sig := range handledSignals() {
@@ -181,7 +196,7 @@ func setupSignalHandler() {
if fatalSignal(nativeSignal) {
kataLog.WithField("signal", sig).Error("received fatal signal")
die()
die(ctx)
} else if debug && nonFatalSignal(nativeSignal) {
kataLog.WithField("signal", sig).Debug("handling signal")
backtrace()
@@ -206,15 +221,7 @@ func setExternalLoggers(logger *logrus.Entry) {
// beforeSubcommands is the function to perform preliminary checks
// before command-line parsing occurs.
func beforeSubcommands(context *cli.Context) error {
if context.GlobalBool(showConfigPathsOption) {
files := getDefaultConfigFilePaths()
for _, file := range files {
fmt.Fprintf(defaultOutputFile, "%s\n", file)
}
exit(0)
}
handleShowConfig(context)
if userWantsUsage(context) || (context.NArg() == 1 && (context.Args()[0] == checkCmd)) {
// No setup required if the user just
@@ -241,11 +248,17 @@ func beforeSubcommands(context *cli.Context) error {
return fmt.Errorf("unknown log-format %q", context.GlobalString("log-format"))
}
var traceRootSpan string
// Add the name of the sub-command to each log entry for easier
// debugging.
cmdName := context.Args().First()
if context.App.Command(cmdName) != nil {
kataLog = kataLog.WithField("command", cmdName)
// Name for the root span (used for tracing) now the
// sub-command name is known.
traceRootSpan = name + " " + cmdName
}
setExternalLoggers(kataLog)
@@ -262,6 +275,19 @@ func beforeSubcommands(context *cli.Context) error {
fatal(err)
}
if traceRootSpan != "" {
// Create the tracer.
//
// Note: no spans are created until the command-line has been parsed.
// This delays collection of trace data slightly but benefits the user by
// ensuring the first span is the name of the sub-command being
// invoked from the command-line.
err = setupTracing(context, traceRootSpan)
if err != nil {
return err
}
}
args := strings.Join(context.Args(), " ")
fields := logrus.Fields{
@@ -273,10 +299,63 @@ func beforeSubcommands(context *cli.Context) error {
kataLog.WithFields(fields).Info()
// make the data accessible to the sub-commands.
context.App.Metadata = map[string]interface{}{
"runtimeConfig": runtimeConfig,
"configFile": configFile,
context.App.Metadata["runtimeConfig"] = runtimeConfig
context.App.Metadata["configFile"] = configFile
return nil
}
// handleShowConfig determines if the user wishes to see the configuration
// paths. If so, it will display them and then exit.
func handleShowConfig(context *cli.Context) {
if context.GlobalBool(showConfigPathsOption) {
files := getDefaultConfigFilePaths()
for _, file := range files {
fmt.Fprintf(defaultOutputFile, "%s\n", file)
}
exit(0)
}
}
func setupTracing(context *cli.Context, rootSpanName string) error {
tracer, err := createTracer(name)
if err != nil {
fatal(err)
}
// Create the root span now that the sub-command name is
// known.
//
// Note that this "Before" function is called (and returns)
// before the subcommand handler is called. As such, we cannot
// "Finish()" the span here - that is handled in the .After
// function.
span := tracer.StartSpan(rootSpanName)
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
// Associate the root span with the context
ctx = opentracing.ContextWithSpan(ctx, span)
// Add tracer to metadata and update the context
context.App.Metadata["tracer"] = tracer
context.App.Metadata["context"] = ctx
return nil
}
func afterSubcommands(c *cli.Context) error {
ctx, err := cliContextToContext(c)
if err != nil {
return err
}
stopTracing(ctx)
return nil
}
@@ -336,7 +415,7 @@ func setCLIGlobals() {
// createRuntimeApp creates an application to process the command-line
// arguments and invoke the requested runtime command.
func createRuntimeApp(args []string) error {
func createRuntimeApp(ctx context.Context, args []string) error {
app := cli.NewApp()
app.Name = name
@@ -347,8 +426,14 @@ func createRuntimeApp(args []string) error {
app.Flags = runtimeFlags
app.Commands = runtimeCommands
app.Before = runtimeBeforeSubcommands
app.After = runtimeAfterSubcommands
app.EnableBashCompletion = true
// allow sub-commands to access context
app.Metadata = map[string]interface{}{
"context": ctx,
}
return app.Run(args)
}
@@ -387,18 +472,38 @@ func (f *fatalWriter) Write(p []byte) (n int, err error) {
return f.cliErrWriter.Write(p)
}
func createRuntime() {
setupSignalHandler()
func createRuntime(ctx context.Context) {
setupSignalHandler(ctx)
setCLIGlobals()
err := createRuntimeApp(os.Args)
err := createRuntimeApp(ctx, os.Args)
if err != nil {
fatal(err)
}
}
func main() {
defer handlePanic()
createRuntime()
// cliContextToContext extracts the generic context from the specified
// cli context.
func cliContextToContext(c *cli.Context) (context.Context, error) {
if c == nil {
return nil, errors.New("need cli.Context")
}
// extract the main context
ctx, ok := c.App.Metadata["context"].(context.Context)
if !ok {
return nil, errors.New("invalid or missing context in metadata")
}
return ctx, nil
}
func main() {
// create a new empty context
ctx := context.Background()
defer handlePanic(ctx)
createRuntime(ctx)
}

View File

@@ -7,6 +7,7 @@ package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"flag"
@@ -26,6 +27,7 @@ import (
"github.com/kata-containers/runtime/virtcontainers/pkg/vcmock"
specs "github.com/opencontainers/runtime-spec/specs-go"
"github.com/stretchr/testify/assert"
jaeger "github.com/uber/jaeger-client-go"
"github.com/urfave/cli"
)
@@ -474,6 +476,10 @@ func createCLIContextWithApp(flagSet *flag.FlagSet, app *cli.App) *cli.Context {
ctx.App.Metadata = map[string]interface{}{}
}
// add standard entries
ctx.App.Metadata["context"] = context.Background()
ctx.App.Metadata["tracer"] = &jaeger.Tracer{}
return ctx
}
@@ -918,7 +924,7 @@ func TestMainCreateRuntimeApp(t *testing.T) {
args := []string{name}
err = createRuntimeApp(args)
err = createRuntimeApp(context.Background(), args)
assert.NoError(err, "%v", args)
}
@@ -941,7 +947,7 @@ func TestMainCreateRuntimeAppInvalidSubCommand(t *testing.T) {
}()
// calls fatal() so no return
_ = createRuntimeApp([]string{name, "i-am-an-invalid-sub-command"})
_ = createRuntimeApp(context.Background(), []string{name, "i-am-an-invalid-sub-command"})
assert.NotEqual(exitStatus, 0)
}
@@ -985,7 +991,7 @@ func TestMainCreateRuntime(t *testing.T) {
}()
assert.Equal(exitStatus, 0)
createRuntime()
createRuntime(context.Background())
assert.NotEqual(exitStatus, 0)
}
@@ -1012,7 +1018,7 @@ func TestMainVersionPrinter(t *testing.T) {
setCLIGlobals()
err = createRuntimeApp([]string{name, "--version"})
err = createRuntimeApp(context.Background(), []string{name, "--version"})
assert.NoError(err)
err = grep(fmt.Sprintf(`%s\s*:\s*%s`, name, version), output)
@@ -1060,7 +1066,7 @@ func TestMainFatalWriter(t *testing.T) {
setCLIGlobals()
err := createRuntimeApp([]string{name, cmd})
err := createRuntimeApp(context.Background(), []string{name, cmd})
assert.Error(err)
re := regexp.MustCompile(

View File

@@ -7,6 +7,7 @@ package main
import (
"bufio"
"context"
"errors"
"fmt"
"io/ioutil"
@@ -20,6 +21,7 @@ import (
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
"github.com/opencontainers/runc/libcontainer/utils"
specs "github.com/opencontainers/runtime-spec/specs-go"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
)
@@ -125,7 +127,10 @@ func validCreateParams(containerID, bundlePath string) (string, error) {
// processCgroupsPath process the cgroups path as expected from the
// OCI runtime specification. It returns a list of complete paths
// that should be created and used for every specified resource.
func processCgroupsPath(ociSpec oci.CompatOCISpec, isSandbox bool) ([]string, error) {
func processCgroupsPath(ctx context.Context, ociSpec oci.CompatOCISpec, isSandbox bool) ([]string, error) {
span, _ := opentracing.StartSpanFromContext(ctx, "processCgroupsPath")
defer span.Finish()
var cgroupsPathList []string
if ociSpec.Linux.CgroupsPath == "" {
@@ -371,7 +376,10 @@ func fetchContainerIDMapping(containerID string) (string, error) {
return files[0].Name(), nil
}
func addContainerIDMapping(containerID, sandboxID string) error {
func addContainerIDMapping(ctx context.Context, containerID, sandboxID string) error {
span, _ := opentracing.StartSpanFromContext(ctx, "addContainerIDMapping")
defer span.Finish()
if containerID == "" {
return fmt.Errorf("Missing container ID")
}
@@ -395,7 +403,10 @@ func addContainerIDMapping(containerID, sandboxID string) error {
return nil
}
func delContainerIDMapping(containerID string) error {
func delContainerIDMapping(ctx context.Context, containerID string) error {
span, _ := opentracing.StartSpanFromContext(ctx, "delContainerIDMapping")
defer span.Finish()
if containerID == "" {
return fmt.Errorf("Missing container ID")
}

View File

@@ -6,6 +6,7 @@
package main
import (
"context"
"fmt"
"io/ioutil"
"math/rand"
@@ -182,7 +183,7 @@ func TestValidCreateParamsBundleIsAFile(t *testing.T) {
func testProcessCgroupsPath(t *testing.T, ociSpec oci.CompatOCISpec, expected []string) {
assert := assert.New(t)
result, err := processCgroupsPath(ociSpec, true)
result, err := processCgroupsPath(context.Background(), ociSpec, true)
assert.NoError(err)
@@ -268,7 +269,7 @@ func TestProcessCgroupsPathAbsoluteNoCgroupMountDestinationFailure(t *testing.T)
for _, d := range cgroupTestData {
ociSpec.Linux.Resources = d.linuxSpec
for _, isSandbox := range []bool{true, false} {
_, err := processCgroupsPath(ociSpec, isSandbox)
_, err := processCgroupsPath(context.Background(), ociSpec, isSandbox)
assert.Error(err, "This test should fail because no cgroup mount destination provided")
}
}
@@ -563,14 +564,14 @@ func TestFetchContainerIDMappingSuccess(t *testing.T) {
func TestAddContainerIDMappingContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
err := addContainerIDMapping("", testSandboxID)
err := addContainerIDMapping(context.Background(), "", testSandboxID)
assert.Error(err)
}
func TestAddContainerIDMappingSandboxIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
err := addContainerIDMapping(testContainerID, "")
err := addContainerIDMapping(context.Background(), testContainerID, "")
assert.Error(err)
}
@@ -585,7 +586,7 @@ func TestAddContainerIDMappingSuccess(t *testing.T) {
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
assert.True(os.IsNotExist(err))
err = addContainerIDMapping(testContainerID, testSandboxID)
err = addContainerIDMapping(context.Background(), testContainerID, testSandboxID)
assert.NoError(err)
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
@@ -595,7 +596,7 @@ func TestAddContainerIDMappingSuccess(t *testing.T) {
func TestDelContainerIDMappingContainerIDEmptyFailure(t *testing.T) {
assert := assert.New(t)
err := delContainerIDMapping("")
err := delContainerIDMapping(context.Background(), "")
assert.Error(err)
}
@@ -609,7 +610,7 @@ func TestDelContainerIDMappingSuccess(t *testing.T) {
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))
assert.NoError(err)
err = delContainerIDMapping(testContainerID)
err = delContainerIDMapping(context.Background(), testContainerID)
assert.NoError(err)
_, err = os.Stat(filepath.Join(ctrsMapTreePath, testContainerID, testSandboxID))

View File

@@ -7,6 +7,9 @@
package main
import (
"context"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -22,9 +25,7 @@ Where "<container-id>" is the container name to be paused.`,
Description: `The pause command suspends all processes in a container.
` + noteText,
Action: func(context *cli.Context) error {
return toggleContainerPause(context.Args().First(), true)
},
Action: pause,
}
var resumeCLICommand = cli.Command{
@@ -36,14 +37,34 @@ Where "<container-id>" is the container name to be resumed.`,
Description: `The resume command unpauses all processes in a container.
` + noteText,
Action: func(context *cli.Context) error {
return toggleContainerPause(context.Args().First(), false)
},
Action: resume,
}
func toggleContainerPause(containerID string, pause bool) (err error) {
func pause(c *cli.Context) error {
return toggle(c, true)
}
func resume(c *cli.Context) error {
return toggle(c, false)
}
func toggle(c *cli.Context, pause bool) error {
ctx, err := cliContextToContext(c)
if err != nil {
return err
}
return toggleContainerPause(ctx, c.Args().First(), pause)
}
func toggleContainerPause(ctx context.Context, containerID string, pause bool) (err error) {
span, _ := opentracing.StartSpanFromContext(ctx, "pause")
defer span.Finish()
span.SetTag("pause", pause)
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
status, sandboxID, err := getExistingContainerInfo(containerID)
@@ -59,6 +80,8 @@ func toggleContainerPause(containerID string, pause bool) (err error) {
})
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
if pause {
err = vci.PauseContainer(sandboxID, containerID)

View File

@@ -7,9 +7,11 @@
package main
import (
"context"
"fmt"
vc "github.com/kata-containers/runtime/virtcontainers"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -26,6 +28,11 @@ var psCLICommand = cli.Command{
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
if context.Args().Present() == false {
return fmt.Errorf("Missing container ID, should at least provide one")
}
@@ -38,18 +45,22 @@ var psCLICommand = cli.Command{
args = context.Args()[1:]
}
return ps(context.Args().First(), context.String("format"), args)
return ps(ctx, context.Args().First(), context.String("format"), args)
},
SkipArgReorder: true,
}
func ps(containerID, format string, args []string) error {
func ps(ctx context.Context, containerID, format string, args []string) error {
span, _ := opentracing.StartSpanFromContext(ctx, "ps")
defer span.Finish()
if containerID == "" {
return fmt.Errorf("Missing container ID")
}
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
status, sandboxID, err := getExistingContainerInfo(containerID)
@@ -65,6 +76,8 @@ func ps(containerID, format string, args []string) error {
})
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
// container MUST be running
if status.State.State != vc.StateRunning {

View File

@@ -6,6 +6,7 @@
package main
import (
"context"
"flag"
"os"
"testing"
@@ -66,11 +67,11 @@ func TestPSFailure(t *testing.T) {
}()
// inexistent container
err = ps("xyz123abc", "json", []string{"-ef"})
err = ps(context.Background(), "xyz123abc", "json", []string{"-ef"})
assert.Error(err)
// container is not running
err = ps(sandbox.ID(), "json", []string{"-ef"})
err = ps(context.Background(), sandbox.ID(), "json", []string{"-ef"})
assert.Error(err)
}
@@ -113,6 +114,6 @@ func TestPSSuccessful(t *testing.T) {
testingImpl.ProcessListContainerFunc = nil
}()
err = ps(sandbox.ID(), "json", []string{})
err = ps(context.Background(), sandbox.ID(), "json", []string{})
assert.NoError(err)
}

View File

@@ -7,12 +7,14 @@
package main
import (
"context"
"errors"
"fmt"
"os"
"syscall"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
opentracing "github.com/opentracing/opentracing-go"
"github.com/urfave/cli"
)
@@ -58,12 +60,17 @@ var runCLICommand = cli.Command{
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
runtimeConfig, ok := context.App.Metadata["runtimeConfig"].(oci.RuntimeConfig)
if !ok {
return errors.New("invalid runtime config")
}
return run(context.Args().First(),
return run(ctx, context.Args().First(),
context.String("bundle"),
context.String("console"),
context.String("console-socket"),
@@ -73,19 +80,21 @@ var runCLICommand = cli.Command{
},
}
func run(containerID, bundle, console, consoleSocket, pidFile string, detach bool,
func run(ctx context.Context, containerID, bundle, console, consoleSocket, pidFile string, detach bool,
runtimeConfig oci.RuntimeConfig) error {
span, ctx := opentracing.StartSpanFromContext(ctx, "run")
defer span.Finish()
consolePath, err := setupConsole(console, consoleSocket)
if err != nil {
return err
}
if err := create(containerID, bundle, consolePath, pidFile, detach, runtimeConfig); err != nil {
if err := create(ctx, containerID, bundle, consolePath, pidFile, detach, runtimeConfig); err != nil {
return err
}
sandbox, err := start(containerID)
sandbox, err := start(ctx, containerID)
if err != nil {
return err
}
@@ -110,7 +119,7 @@ func run(containerID, bundle, console, consoleSocket, pidFile string, detach boo
}
// delete container's resources
if err := delete(sandbox.ID(), true); err != nil {
if err := delete(ctx, sandbox.ID(), true); err != nil {
return err
}

View File

@@ -6,6 +6,7 @@
package main
import (
"context"
"flag"
"fmt"
"io/ioutil"
@@ -142,7 +143,7 @@ func TestRunInvalidArgs(t *testing.T) {
}
for i, a := range args {
err := run(a.containerID, a.bundle, a.console, a.consoleSocket, a.pidFile, a.detach, a.runtimeConfig)
err := run(context.Background(), a.containerID, a.bundle, a.console, a.consoleSocket, a.pidFile, a.detach, a.runtimeConfig)
assert.Errorf(err, "test %d (%+v)", i, a)
}
}
@@ -284,7 +285,7 @@ func TestRunContainerSuccessful(t *testing.T) {
testingImpl.DeleteContainerFunc = nil
}()
err = run(d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, d.runtimeConfig)
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, d.runtimeConfig)
// should return ExitError with the message and exit code
e, ok := err.(*cli.ExitError)
@@ -358,7 +359,7 @@ func TestRunContainerDetachSuccessful(t *testing.T) {
testingImpl.DeleteContainerFunc = nil
}()
err = run(d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, true, d.runtimeConfig)
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, true, d.runtimeConfig)
// should not return ExitError
assert.NoError(err)
@@ -431,7 +432,7 @@ func TestRunContainerDeleteFail(t *testing.T) {
testingImpl.DeleteContainerFunc = nil
}()
err = run(d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, d.runtimeConfig)
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, d.runtimeConfig)
// should not return ExitError
err, ok := err.(*cli.ExitError)
@@ -508,7 +509,7 @@ func TestRunContainerWaitFail(t *testing.T) {
testingImpl.DeleteContainerFunc = nil
}()
err = run(d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, d.runtimeConfig)
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, d.runtimeConfig)
// should not return ExitError
err, ok := err.(*cli.ExitError)
@@ -566,7 +567,7 @@ func TestRunContainerStartFail(t *testing.T) {
testingImpl.StatusContainerFunc = nil
}()
err = run(d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, d.runtimeConfig)
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, d.runtimeConfig)
// should not return ExitError
err, ok := err.(*cli.ExitError)
@@ -621,7 +622,7 @@ func TestRunContainerStartFailExistingContainer(t *testing.T) {
testingImpl.StartSandboxFunc = nil
}()
err = run(d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, d.runtimeConfig)
err = run(context.Background(), d.sandbox.ID(), d.bundlePath, d.consolePath, "", d.pidFilePath, false, d.runtimeConfig)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}

View File

@@ -7,6 +7,7 @@ package main
import (
"bytes"
"context"
"fmt"
"os/signal"
"runtime/pprof"
@@ -29,14 +30,14 @@ var handledSignalsMap = map[syscall.Signal]bool{
syscall.SIGUSR1: false,
}
func handlePanic() {
func handlePanic(ctx context.Context) {
r := recover()
if r != nil {
msg := fmt.Sprintf("%s", r)
kataLog.WithField("panic", msg).Error("fatal error")
die()
die(ctx)
}
}
@@ -84,7 +85,9 @@ func handledSignals() []syscall.Signal {
return signals
}
func die() {
func die(ctx context.Context) {
stopTracing(ctx)
backtrace()
if crashOnError {

View File

@@ -13,6 +13,7 @@ import (
"os"
"github.com/opencontainers/runc/libcontainer/specconv"
opentracing "github.com/opentracing/opentracing-go"
"github.com/urfave/cli"
)
@@ -72,6 +73,14 @@ generate a proper rootless spec file.`,
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, _ := opentracing.StartSpanFromContext(ctx, "spec")
defer span.Finish()
spec := specconv.Example()
checkNoFile := func(name string) error {

View File

@@ -7,10 +7,12 @@
package main
import (
"context"
"fmt"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -25,13 +27,18 @@ var startCLICommand = cli.Command{
unique on your host.`,
Description: `The start command executes the user defined process in a created container .`,
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
args := context.Args()
if args.Present() == false {
return fmt.Errorf("Missing container ID, should at least provide one")
}
for _, cID := range []string(args) {
if _, err := start(cID); err != nil {
if _, err := start(ctx, cID); err != nil {
return err
}
}
@@ -40,9 +47,13 @@ var startCLICommand = cli.Command{
},
}
func start(containerID string) (vc.VCSandbox, error) {
func start(ctx context.Context, containerID string) (vc.VCSandbox, error) {
span, _ := opentracing.StartSpanFromContext(ctx, "start")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
// Checks the MUST and MUST NOT from OCI runtime specification
status, sandboxID, err := getExistingContainerInfo(containerID)
@@ -58,6 +69,8 @@ func start(containerID string) (vc.VCSandbox, error) {
})
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
containerType, err := oci.GetContainerType(status.Annotations)
if err != nil {

View File

@@ -6,6 +6,7 @@
package main
import (
"context"
"flag"
"io/ioutil"
"os"
@@ -22,7 +23,7 @@ func TestStartInvalidArgs(t *testing.T) {
assert := assert.New(t)
// Missing container id
_, err := start("")
_, err := start(context.Background(), "")
assert.Error(err)
assert.False(vcmock.IsMockError(err))
@@ -31,7 +32,7 @@ func TestStartInvalidArgs(t *testing.T) {
defer os.RemoveAll(path)
// Mock StatusContainer error
_, err = start(testContainerID)
_, err = start(context.Background(), testContainerID)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -41,7 +42,7 @@ func TestStartInvalidArgs(t *testing.T) {
ctrsMapTreePath = path
// Container missing in container mapping
_, err = start(testContainerID)
_, err = start(context.Background(), testContainerID)
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
@@ -70,7 +71,7 @@ func TestStartSandbox(t *testing.T) {
testingImpl.StatusContainerFunc = nil
}()
_, err = start(sandbox.ID())
_, err = start(context.Background(), sandbox.ID())
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -82,7 +83,7 @@ func TestStartSandbox(t *testing.T) {
testingImpl.StartSandboxFunc = nil
}()
_, err = start(sandbox.ID())
_, err = start(context.Background(), sandbox.ID())
assert.Nil(err)
}
@@ -108,7 +109,7 @@ func TestStartMissingAnnotation(t *testing.T) {
testingImpl.StatusContainerFunc = nil
}()
_, err = start(sandbox.ID())
_, err = start(context.Background(), sandbox.ID())
assert.Error(err)
assert.False(vcmock.IsMockError(err))
}
@@ -144,7 +145,7 @@ func TestStartContainerSucessFailure(t *testing.T) {
testingImpl.StatusContainerFunc = nil
}()
_, err = start(testContainerID)
_, err = start(context.Background(), testContainerID)
assert.Error(err)
assert.True(vcmock.IsMockError(err))
@@ -156,7 +157,7 @@ func TestStartContainerSucessFailure(t *testing.T) {
testingImpl.StartContainerFunc = nil
}()
_, err = start(testContainerID)
_, err = start(context.Background(), testContainerID)
assert.Nil(err)
}

View File

@@ -7,11 +7,13 @@
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"github.com/kata-containers/runtime/virtcontainers/pkg/oci"
opentracing "github.com/opentracing/opentracing-go"
"github.com/urfave/cli"
)
@@ -24,17 +26,26 @@ var stateCLICommand = cli.Command{
Description: `The state command outputs current state information for the
instance of a container.`,
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
args := context.Args()
if len(args) != 1 {
return fmt.Errorf("Expecting only one container ID, got %d: %v", len(args), []string(args))
}
return state(args.First())
return state(ctx, args.First())
},
}
func state(containerID string) error {
func state(ctx context.Context, containerID string) error {
span, _ := opentracing.StartSpanFromContext(ctx, "state")
defer span.Finish()
kataLog = kataLog.WithField("container", containerID)
span.SetTag("container", containerID)
setExternalLoggers(kataLog)

View File

@@ -6,6 +6,7 @@
package main
import (
"context"
"flag"
"io/ioutil"
"os"
@@ -59,7 +60,7 @@ func TestStateSuccessful(t *testing.T) {
ctrsMapTreePath = path
// trying with an inexistent id
err = state("123456789")
err = state(context.Background(), "123456789")
assert.Error(err)
path, err = createTempContainerIDMapping(testContainerID, sandbox.ID())
@@ -79,6 +80,6 @@ func TestStateSuccessful(t *testing.T) {
testingImpl.StatusContainerFunc = nil
}()
err = state(testContainerID)
err = state(context.Background(), testContainerID)
assert.NoError(err)
}

78
cli/tracing.go Normal file
View File

@@ -0,0 +1,78 @@
// Copyright (c) 2018 Intel Corporation
//
// SPDX-License-Identifier: Apache-2.0
//
package main
import (
"context"
"io"
opentracing "github.com/opentracing/opentracing-go"
"github.com/uber/jaeger-client-go/config"
)
// Implements jaeger-client-go.Logger interface
type traceLogger struct {
}
// tracerCloser contains a copy of the closer returned by createTracer() which
// is used by stopTracing().
var tracerCloser io.Closer
func (t traceLogger) Error(msg string) {
kataLog.Error(msg)
}
func (t traceLogger) Infof(msg string, args ...interface{}) {
kataLog.Infof(msg, args...)
}
func createTracer(name string) (opentracing.Tracer, error) {
cfg := &config.Configuration{
ServiceName: name,
// If tracing is disabled, use a NOP trace implementation
Disabled: !tracing,
// Note that span logging reporter option cannot be enabled as
// it pollutes the output stream which causes (atleast) the
// "state" command to fail under Docker.
Sampler: &config.SamplerConfig{
Type: "const",
Param: 1,
},
}
logger := traceLogger{}
tracer, closer, err := cfg.NewTracer(config.Logger(logger))
if err != nil {
return nil, err
}
// save for stopTracing()'s exclusive use
tracerCloser = closer
// Seems to be essential to ensure non-root spans are logged
opentracing.SetGlobalTracer(tracer)
return tracer, nil
}
// stopTracing() ends all tracing, reporting the spans to the collector.
func stopTracing(ctx context.Context) {
if !tracing {
return
}
span := opentracing.SpanFromContext(ctx)
if span != nil {
span.Finish()
}
// report all possible spans to the collector
tracerCloser.Close()
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/docker/go-units"
vc "github.com/kata-containers/runtime/virtcontainers"
"github.com/opencontainers/runtime-spec/specs-go"
opentracing "github.com/opentracing/opentracing-go"
"github.com/sirupsen/logrus"
"github.com/urfave/cli"
)
@@ -127,6 +128,14 @@ other options are ignored.
},
},
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, _ := opentracing.StartSpanFromContext(ctx, "update")
defer span.Finish()
if context.Args().Present() == false {
return fmt.Errorf("Missing container ID, should at least provide one")
}
@@ -135,6 +144,7 @@ other options are ignored.
kataLog = kataLog.WithField("container", containerID)
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
status, sandboxID, err := getExistingContainerInfo(containerID)
if err != nil {
@@ -150,6 +160,9 @@ other options are ignored.
setExternalLoggers(kataLog)
span.SetTag("container", containerID)
span.SetTag("sandbox", sandboxID)
// container MUST be running
if status.State.State != vc.StateRunning {
return fmt.Errorf("Container %s is not running", containerID)

View File

@@ -6,6 +6,7 @@
package main
import (
opentracing "github.com/opentracing/opentracing-go"
"github.com/urfave/cli"
)
@@ -13,6 +14,14 @@ var versionCLICommand = cli.Command{
Name: "version",
Usage: "display version details",
Action: func(context *cli.Context) error {
ctx, err := cliContextToContext(context)
if err != nil {
return err
}
span, _ := opentracing.StartSpanFromContext(ctx, "version")
defer span.Finish()
cli.VersionPrinter(context)
return nil
},