Files
kata-containers/src/runtime/pkg/katautils/hook.go
Samuel Ortiz 6be6d0a3b3 katautils: Pass the OCI annotations back to the called OCI hooks
That allows us to amend those annotations with information that could be
used when running those hooks.

For example nerdctl will use those annotations to resolve the networking
namespace path in where to run the CNI plugin, i.e. the created pod
networking namespace.

Fixes #3629

Signed-off-by: Samuel Ortiz <s.ortiz@apple.com>
2022-02-15 17:31:56 +01:00

142 lines
3.4 KiB
Go

// Copyright (c) 2018 Intel Corporation
// Copyright (c) 2018 HyperHQ Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
package katautils
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os/exec"
"syscall"
"time"
"github.com/kata-containers/kata-containers/src/runtime/pkg/katautils/katatrace"
"github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
)
// hookTracingTags defines tags for the trace span
var hookTracingTags = map[string]string{
"source": "runtime",
"package": "katautils",
"subsystem": "hook",
}
// Logger returns a logrus logger appropriate for logging hook messages
func hookLogger() *logrus.Entry {
return kataUtilsLogger.WithField("subsystem", "hook")
}
func runHook(ctx context.Context, spec specs.Spec, hook specs.Hook, cid, bundlePath string) error {
span, _ := katatrace.Trace(ctx, hookLogger(), "runHook", hookTracingTags)
defer span.End()
katatrace.AddTags(span, "path", hook.Path, "args", hook.Args)
state := specs.State{
Pid: syscall.Gettid(),
Bundle: bundlePath,
ID: cid,
Annotations: spec.Annotations,
}
stateJSON, err := json.Marshal(state)
if err != nil {
return err
}
var stdout, stderr bytes.Buffer
cmd := &exec.Cmd{
Path: hook.Path,
Args: hook.Args,
Env: hook.Env,
Stdin: bytes.NewReader(stateJSON),
Stdout: &stdout,
Stderr: &stderr,
}
if err := cmd.Start(); err != nil {
return err
}
if hook.Timeout == nil {
if err := cmd.Wait(); err != nil {
return fmt.Errorf("%s: stdout: %s, stderr: %s", err, stdout.String(), stderr.String())
}
} else {
done := make(chan error, 1)
go func() {
done <- cmd.Wait()
close(done)
}()
select {
case err := <-done:
if err != nil {
return fmt.Errorf("%s: stdout: %s, stderr: %s", err, stdout.String(), stderr.String())
}
case <-time.After(time.Duration(*hook.Timeout) * time.Second):
if err := syscall.Kill(cmd.Process.Pid, syscall.SIGKILL); err != nil {
return err
}
return fmt.Errorf("Hook timeout")
}
}
return nil
}
func runHooks(ctx context.Context, spec specs.Spec, hooks []specs.Hook, cid, bundlePath, hookType string) error {
span, ctx := katatrace.Trace(ctx, hookLogger(), "runHooks", hookTracingTags)
katatrace.AddTags(span, "type", hookType)
defer span.End()
for _, hook := range hooks {
if err := runHook(ctx, spec, hook, cid, bundlePath); err != nil {
hookLogger().WithFields(logrus.Fields{
"hook-type": hookType,
"error": err,
}).Error("hook error")
return err
}
}
return nil
}
// PreStartHooks run the hooks before start container
func PreStartHooks(ctx context.Context, spec specs.Spec, cid, bundlePath string) error {
// If no hook available, nothing needs to be done.
if spec.Hooks == nil {
return nil
}
return runHooks(ctx, spec, spec.Hooks.Prestart, cid, bundlePath, "pre-start")
}
// PostStartHooks run the hooks just after start container
func PostStartHooks(ctx context.Context, spec specs.Spec, cid, bundlePath string) error {
// If no hook available, nothing needs to be done.
if spec.Hooks == nil {
return nil
}
return runHooks(ctx, spec, spec.Hooks.Poststart, cid, bundlePath, "post-start")
}
// PostStopHooks run the hooks after stop container
func PostStopHooks(ctx context.Context, spec specs.Spec, cid, bundlePath string) error {
// If no hook available, nothing needs to be done.
if spec.Hooks == nil {
return nil
}
return runHooks(ctx, spec, spec.Hooks.Poststop, cid, bundlePath, "post-stop")
}