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