|
| 1 | +package main |
| 2 | + |
| 3 | +import ( |
| 4 | + "encoding/json" |
| 5 | + "fmt" |
| 6 | + "io/ioutil" |
| 7 | + "os" |
| 8 | + "path/filepath" |
| 9 | + "reflect" |
| 10 | + "time" |
| 11 | + |
| 12 | + tap "github.com/mndrix/tap-go" |
| 13 | + rspecs "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 stdinStateCheck(outputDir, hookName string, expectedState rspecs.State) error { |
| 20 | + var state rspecs.State |
| 21 | + data, err := ioutil.ReadFile(filepath.Join(outputDir, hookName)) |
| 22 | + if err != nil { |
| 23 | + return err |
| 24 | + } |
| 25 | + err = json.Unmarshal(data, &state) |
| 26 | + if err != nil { |
| 27 | + return err |
| 28 | + } |
| 29 | + |
| 30 | + if state.ID != expectedState.ID { |
| 31 | + return fmt.Errorf("wrong container ID %q in the stdin of %s hook, expected %q", state.ID, hookName, expectedState.ID) |
| 32 | + } |
| 33 | + |
| 34 | + if state.Bundle != expectedState.Bundle { |
| 35 | + return fmt.Errorf("wrong bundle directory %q in the stdin of %s hook, expected %q", state.Bundle, hookName, expectedState.Bundle) |
| 36 | + } |
| 37 | + |
| 38 | + if hookName != "poststop" && state.Pid != expectedState.Pid { |
| 39 | + return fmt.Errorf("wrong container process ID %q in the stdin of %s hook, expected %q", state.Version, hookName, expectedState.Version) |
| 40 | + } |
| 41 | + |
| 42 | + if !reflect.DeepEqual(state.Annotations, expectedState.Annotations) { |
| 43 | + return fmt.Errorf("wrong annotations \"%v\" in the stdin of %s hook, expected \"%v\"", state.Annotations, hookName, expectedState.Annotations) |
| 44 | + } |
| 45 | + return nil |
| 46 | +} |
| 47 | + |
| 48 | +func main() { |
| 49 | + t := tap.New() |
| 50 | + t.Header(0) |
| 51 | + |
| 52 | + bundleDir, err := util.PrepareBundle() |
| 53 | + if err != nil { |
| 54 | + util.Fatal(err) |
| 55 | + } |
| 56 | + containerID := uuid.NewV4().String() |
| 57 | + defer os.RemoveAll(bundleDir) |
| 58 | + |
| 59 | + var containerPid int |
| 60 | + |
| 61 | + annotationKey := "org.opencontainers.runtime-tools" |
| 62 | + annotationValue := "hook stdin test" |
| 63 | + g := util.GetDefaultGenerator() |
| 64 | + outputDir := filepath.Join(bundleDir, g.Spec().Root.Path) |
| 65 | + timeout := 1 |
| 66 | + g.AddAnnotation(annotationKey, annotationValue) |
| 67 | + g.AddPreStartHook(rspecs.Hook{ |
| 68 | + Path: filepath.Join(bundleDir, g.Spec().Root.Path, "/bin/sh"), |
| 69 | + Args: []string{ |
| 70 | + "sh", "-c", fmt.Sprintf("cat > %s", filepath.Join(outputDir, "prestart")), |
| 71 | + }, |
| 72 | + Timeout: &timeout, |
| 73 | + }) |
| 74 | + g.AddPostStartHook(rspecs.Hook{ |
| 75 | + Path: filepath.Join(bundleDir, g.Spec().Root.Path, "/bin/sh"), |
| 76 | + Args: []string{ |
| 77 | + "sh", "-c", fmt.Sprintf("cat > %s", filepath.Join(outputDir, "poststart")), |
| 78 | + }, |
| 79 | + Timeout: &timeout, |
| 80 | + }) |
| 81 | + g.AddPostStopHook(rspecs.Hook{ |
| 82 | + Path: filepath.Join(bundleDir, g.Spec().Root.Path, "/bin/sh"), |
| 83 | + Args: []string{ |
| 84 | + "sh", "-c", fmt.Sprintf("cat > %s", filepath.Join(outputDir, "poststop")), |
| 85 | + }, |
| 86 | + Timeout: &timeout, |
| 87 | + }) |
| 88 | + g.SetProcessArgs([]string{"true"}) |
| 89 | + config := util.LifecycleConfig{ |
| 90 | + BundleDir: bundleDir, |
| 91 | + Config: g, |
| 92 | + Actions: util.LifecycleActionCreate | util.LifecycleActionStart | util.LifecycleActionDelete, |
| 93 | + PreCreate: func(r *util.Runtime) error { |
| 94 | + r.SetID(containerID) |
| 95 | + return nil |
| 96 | + }, |
| 97 | + PreDelete: func(r *util.Runtime) error { |
| 98 | + state, err := r.State() |
| 99 | + if err != nil { |
| 100 | + return err |
| 101 | + } |
| 102 | + containerPid = state.Pid |
| 103 | + util.WaitingForStatus(*r, util.LifecycleStatusStopped, time.Second*10, time.Second) |
| 104 | + return nil |
| 105 | + }, |
| 106 | + } |
| 107 | + |
| 108 | + err = util.RuntimeLifecycleValidate(config) |
| 109 | + if err != nil { |
| 110 | + util.Fatal(err) |
| 111 | + } |
| 112 | + |
| 113 | + expectedState := rspecs.State{ |
| 114 | + Pid: containerPid, |
| 115 | + ID: containerID, |
| 116 | + Bundle: bundleDir, |
| 117 | + Annotations: map[string]string{annotationKey: annotationValue}, |
| 118 | + } |
| 119 | + for _, file := range []string{"prestart", "poststart", "poststop"} { |
| 120 | + err := stdinStateCheck(outputDir, file, expectedState) |
| 121 | + util.SpecErrorOK(t, err == nil, specerror.NewError(specerror.PosixHooksStateToStdin, fmt.Errorf("the state of the container MUST be passed to %q hook over stdin", file), rspecs.Version), err) |
| 122 | + } |
| 123 | + |
| 124 | + t.AutoPlan() |
| 125 | +} |
0 commit comments