gitee.com/leisunstar/runtime@v0.0.0-20200521203717-5cef3e7b53f9/pkg/katautils/hook.go (about) 1 // Copyright (c) 2018 Intel Corporation 2 // Copyright (c) 2018 HyperHQ Inc. 3 // 4 // SPDX-License-Identifier: Apache-2.0 5 // 6 7 package katautils 8 9 import ( 10 "bytes" 11 "context" 12 "encoding/json" 13 "fmt" 14 "os/exec" 15 "strings" 16 "syscall" 17 "time" 18 19 "github.com/opencontainers/runtime-spec/specs-go" 20 "github.com/opentracing/opentracing-go/log" 21 "github.com/sirupsen/logrus" 22 ) 23 24 // Logger returns a logrus logger appropriate for logging hook messages 25 func hookLogger() *logrus.Entry { 26 return kataUtilsLogger.WithField("subsystem", "hook") 27 } 28 29 func runHook(ctx context.Context, hook specs.Hook, cid, bundlePath string) error { 30 span, _ := Trace(ctx, "hook") 31 defer span.Finish() 32 33 span.SetTag("subsystem", "runHook") 34 35 span.LogFields( 36 log.String("hook-name", hook.Path), 37 log.String("hook-args", strings.Join(hook.Args, " "))) 38 39 state := specs.State{ 40 Pid: syscall.Gettid(), 41 Bundle: bundlePath, 42 ID: cid, 43 } 44 45 stateJSON, err := json.Marshal(state) 46 if err != nil { 47 return err 48 } 49 50 var stdout, stderr bytes.Buffer 51 cmd := &exec.Cmd{ 52 Path: hook.Path, 53 Args: hook.Args, 54 Env: hook.Env, 55 Stdin: bytes.NewReader(stateJSON), 56 Stdout: &stdout, 57 Stderr: &stderr, 58 } 59 60 if err := cmd.Start(); err != nil { 61 return err 62 } 63 64 if hook.Timeout == nil { 65 if err := cmd.Wait(); err != nil { 66 return fmt.Errorf("%s: stdout: %s, stderr: %s", err, stdout.String(), stderr.String()) 67 } 68 } else { 69 done := make(chan error, 1) 70 go func() { 71 done <- cmd.Wait() 72 close(done) 73 }() 74 75 select { 76 case err := <-done: 77 if err != nil { 78 return fmt.Errorf("%s: stdout: %s, stderr: %s", err, stdout.String(), stderr.String()) 79 } 80 case <-time.After(time.Duration(*hook.Timeout) * time.Second): 81 if err := syscall.Kill(cmd.Process.Pid, syscall.SIGKILL); err != nil { 82 return err 83 } 84 85 return fmt.Errorf("Hook timeout") 86 } 87 } 88 89 return nil 90 } 91 92 func runHooks(ctx context.Context, hooks []specs.Hook, cid, bundlePath, hookType string) error { 93 span, _ := Trace(ctx, "hooks") 94 defer span.Finish() 95 96 span.SetTag("subsystem", hookType) 97 98 for _, hook := range hooks { 99 if err := runHook(ctx, hook, cid, bundlePath); err != nil { 100 hookLogger().WithFields(logrus.Fields{ 101 "hook-type": hookType, 102 "error": err, 103 }).Error("hook error") 104 105 return err 106 } 107 } 108 109 return nil 110 } 111 112 // PreStartHooks run the hooks before start container 113 func PreStartHooks(ctx context.Context, spec specs.Spec, cid, bundlePath string) error { 114 // If no hook available, nothing needs to be done. 115 if spec.Hooks == nil { 116 return nil 117 } 118 119 return runHooks(ctx, spec.Hooks.Prestart, cid, bundlePath, "pre-start") 120 } 121 122 // PostStartHooks run the hooks just after start container 123 func PostStartHooks(ctx context.Context, spec specs.Spec, cid, bundlePath string) error { 124 // If no hook available, nothing needs to be done. 125 if spec.Hooks == nil { 126 return nil 127 } 128 129 return runHooks(ctx, spec.Hooks.Poststart, cid, bundlePath, "post-start") 130 } 131 132 // PostStopHooks run the hooks after stop container 133 func PostStopHooks(ctx context.Context, spec specs.Spec, cid, bundlePath string) error { 134 // If no hook available, nothing needs to be done. 135 if spec.Hooks == nil { 136 return nil 137 } 138 139 return runHooks(ctx, spec.Hooks.Poststop, cid, bundlePath, "post-stop") 140 }