github.com/opencontainers/runtime-tools@v0.9.0/validation/poststart/poststart.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "strings" 10 "time" 11 12 tap "github.com/mndrix/tap-go" 13 rspec "github.com/opencontainers/runtime-spec/specs-go" 14 "github.com/opencontainers/runtime-tools/specerror" 15 "github.com/opencontainers/runtime-tools/validation/util" 16 uuid "github.com/satori/go.uuid" 17 ) 18 19 func main() { 20 t := tap.New() 21 t.Header(0) 22 23 var output string 24 config := util.LifecycleConfig{ 25 Actions: util.LifecycleActionCreate | util.LifecycleActionStart | util.LifecycleActionDelete, 26 PreCreate: func(r *util.Runtime) error { 27 r.SetID(uuid.NewV4().String()) 28 g, err := util.GetDefaultGenerator() 29 if err != nil { 30 util.Fatal(err) 31 } 32 output = filepath.Join(r.BundleDir, g.Spec().Root.Path, "output") 33 err = g.AddPostStartHook(rspec.Hook{ 34 Path: filepath.Join(r.BundleDir, g.Spec().Root.Path, "/bin/sh"), 35 Args: []string{ 36 "sh", "-c", fmt.Sprintf("echo 'post-start called' >> %s", output), 37 }, 38 }) 39 if err != nil { 40 return err 41 } 42 g.SetProcessArgs([]string{"sh", "-c", fmt.Sprintf("echo 'process called' >> %s", "/output")}) 43 return r.SetConfig(g) 44 }, 45 PostCreate: func(r *util.Runtime) error { 46 outputData, err := ioutil.ReadFile(output) 47 if err == nil { 48 if strings.Contains(string(outputData), "post-start called") { 49 return specerror.NewError(specerror.PoststartTiming, fmt.Errorf("The post-start hooks MUST be called before the `start` operation returns"), rspec.Version) 50 } else if strings.Contains(string(outputData), "process called") { 51 return specerror.NewError(specerror.ProcNotRunAtResRequest, fmt.Errorf("The user-specified program (from process) MUST NOT be run at this time"), rspec.Version) 52 } 53 return fmt.Errorf("File %v should not exist", output) 54 } 55 return nil 56 }, 57 PreDelete: func(r *util.Runtime) error { 58 util.WaitingForStatus(*r, util.LifecycleStatusStopped, time.Second*10, time.Second) 59 outputData, err := ioutil.ReadFile(output) 60 if err != nil { 61 return fmt.Errorf("%v\n%v", specerror.NewError(specerror.PoststartHooksInvoke, fmt.Errorf("The poststart hooks MUST be invoked by the runtime"), rspec.Version), specerror.NewError(specerror.ProcImplement, fmt.Errorf("The runtime MUST run the user-specified program, as specified by `process`"), rspec.Version)) 62 } 63 switch string(outputData) { 64 case "post-start called\n": 65 return specerror.NewError(specerror.ProcImplement, fmt.Errorf("The runtime MUST run the user-specified program, as specified by `process`"), rspec.Version) 66 case "process called\n": 67 fmt.Fprintln(os.Stderr, "WARNING: The poststart hook invoke fails") 68 return nil 69 case "post-start called\nprocess called\n": 70 return specerror.NewError(specerror.PoststartTiming, fmt.Errorf("The post-start hooks MUST be called after the user-specified process is executed"), rspec.Version) 71 case "process called\npost-start called\n": 72 return nil 73 default: 74 return fmt.Errorf("Unsupported output information: %v", string(outputData)) 75 } 76 }, 77 } 78 79 err := util.RuntimeLifecycleValidate(config) 80 if err != nil { 81 diagnostic := map[string]string{ 82 "error": err.Error(), 83 } 84 if e, ok := err.(*exec.ExitError); ok { 85 if len(e.Stderr) > 0 { 86 diagnostic["stderr"] = string(e.Stderr) 87 } 88 } 89 t.YAML(diagnostic) 90 } 91 92 t.AutoPlan() 93 }