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