Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions validation/poststart.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package main

import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

tap "github.com/mndrix/tap-go"
rspec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/specerror"
"github.com/opencontainers/runtime-tools/validation/util"
uuid "github.com/satori/go.uuid"
)

func main() {
t := tap.New()
t.Header(0)

g := util.GetDefaultGenerator()

var output string
config := util.LifecycleConfig{
Actions: util.LifecycleActionCreate | util.LifecycleActionStart | util.LifecycleActionDelete,
PreCreate: func(r *util.Runtime) error {
r.SetID(uuid.NewV4().String())

output = filepath.Join(r.BundleDir, g.Spec().Root.Path, "output")
poststart := rspec.Hook{
Path: fmt.Sprintf("%s/%s/bin/sh", r.BundleDir, g.Spec().Root.Path),
Args: []string{
"sh", "-c", fmt.Sprintf("echo 'post-start called' >> %s", output),
},
}

g.AddPostStartHook(poststart)
g.SetProcessArgs([]string{"sh", "-c", fmt.Sprintf("echo 'process called' >> %s", "/output")})
r.SetConfig(g)
return nil
},
PostCreate: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
if err == nil {
if strings.Contains(string(outputData), "post-start called") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always got it in 'runc'!

return specerror.NewError(specerror.PoststartTiming, fmt.Errorf("The post-start hooks MUST be called before the `start` operation returns"), rspec.Version)
} else if strings.Contains(string(outputData), "process called") {
return specerror.NewError(specerror.ProcNotRunAtResRequest, fmt.Errorf("The user-specified program (from process) MUST NOT be run at this time"), rspec.Version)
}
return fmt.Errorf("File %v should not exist", output)
}
return nil
},
PreDelete: func(r *util.Runtime) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest we can add

util.WaitingForStatus(*r, util.LifecycleStatusStopped, time.Second * 10, time.Second)

'start' will return immediately, although 'echo' runs fast, but we are not sure how many time it costs. (for example, in runc/cc/Kata project)

util.WaitingForStatus(*r, util.LifecycleStatusStopped, time.Second*10, time.Second)
outputData, err := ioutil.ReadFile(output)
if err != nil {
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))
}
switch string(outputData) {
case "post-start called\n":
return specerror.NewError(specerror.ProcImplement, fmt.Errorf("The runtime MUST run the user-specified program, as specified by `process`"), rspec.Version)
case "process called\n":
fmt.Fprintln(os.Stderr, "WARNING: The poststart hook invoke fails")
return nil
case "post-start called\nprocess called\n":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since 'post-start' runs in 'create', the sequence here is wrong.

return specerror.NewError(specerror.PoststartTiming, fmt.Errorf("The post-start hooks MUST be called after the user-specified process is executed"), rspec.Version)
case "process called\npost-start called\n":
return nil
default:
return fmt.Errorf("Unsupported output information: %v", string(outputData))
}
},
}

err := util.RuntimeLifecycleValidate(g, config)
if err != nil {
diagnostic := map[string]string{
"error": err.Error(),
}
if e, ok := err.(*exec.ExitError); ok {
if len(e.Stderr) > 0 {
diagnostic["stderr"] = string(e.Stderr)
}
}
t.YAML(diagnostic)
}

t.AutoPlan()
}
109 changes: 109 additions & 0 deletions validation/poststop.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package main

import (
"errors"
"fmt"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"
"time"

tap "github.com/mndrix/tap-go"
rspec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/specerror"
"github.com/opencontainers/runtime-tools/validation/util"
uuid "github.com/satori/go.uuid"
)

func main() {
t := tap.New()
t.Header(0)

g := util.GetDefaultGenerator()

var output string
config := util.LifecycleConfig{
Actions: util.LifecycleActionCreate | util.LifecycleActionStart | util.LifecycleActionDelete,
PreCreate: func(r *util.Runtime) error {
r.SetID(uuid.NewV4().String())

output = filepath.Join(r.BundleDir, g.Spec().Root.Path, "output")
poststop := rspec.Hook{
Path: fmt.Sprintf("%s/%s/bin/sh", r.BundleDir, g.Spec().Root.Path),
Args: []string{
"sh", "-c", fmt.Sprintf("echo 'post-stop called' >> %s", output),
},
}

g.AddPostStopHook(poststop)
g.SetProcessArgs([]string{"sh", "-c", fmt.Sprintf("echo 'process called' >> %s", "/output")})
r.SetConfig(g)
return nil
},
PostCreate: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
if err == nil {
if strings.Contains(string(outputData), "post-stop called") {
return specerror.NewError(specerror.PoststopTiming, fmt.Errorf("The post-stop hooks MUST be called after the container is deleted"), rspec.Version)
} else if strings.Contains(string(outputData), "process called") {
return specerror.NewError(specerror.ProcNotRunAtResRequest, fmt.Errorf("The user-specified program (from process) MUST NOT be run at this time"), rspec.Version)
}
return fmt.Errorf("File %v should not exist", output)
}
return nil
},
PreDelete: func(r *util.Runtime) error {
util.WaitingForStatus(*r, util.LifecycleStatusStopped, time.Second*10, time.Second)
outputData, err := ioutil.ReadFile(output)
if err != nil {
return specerror.NewError(specerror.ProcImplement, fmt.Errorf("The runtime MUST run the user-specified program, as specified by `process`"), rspec.Version)
}
switch string(outputData) {
case "post-stop called\n":
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))
case "process called\n":
return nil
case "post-stop called\nprocess called\n", "process called\npost-stop called\n":
return specerror.NewError(specerror.PoststopTiming, fmt.Errorf("The post-stop hooks MUST be called after the container is deleted"), rspec.Version)
default:
return fmt.Errorf("Unsupported output information: %v", string(outputData))
}
},
PostDelete: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
if err != nil {
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))
}
switch string(outputData) {
case "post-stop called\n":
return specerror.NewError(specerror.ProcImplement, fmt.Errorf("The runtime MUST run the user-specified program, as specified by `process`"), rspec.Version)
case "process called\n":
fmt.Fprintln(os.Stderr, "WARNING: The poststop hook invoke fails")
return nil
case "post-stop called\nprocess called\n":
return errors.New("The Post-stop should called after the user-specified program command is executed")
case "process called\npost-stop called\n":
return nil
default:
return fmt.Errorf("Unsupported output information: %v", string(outputData))
}
},
}

err := util.RuntimeLifecycleValidate(g, config)
if err != nil {
diagnostic := map[string]string{
"error": err.Error(),
}
if e, ok := err.(*exec.ExitError); ok {
if len(e.Stderr) > 0 {
diagnostic["stderr"] = string(e.Stderr)
}
}
t.YAML(diagnostic)
}

t.AutoPlan()
}
91 changes: 91 additions & 0 deletions validation/prestart.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package main

import (
"fmt"
"io/ioutil"
"os/exec"
"path/filepath"
"strings"
"time"

tap "github.com/mndrix/tap-go"
rspec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/runtime-tools/specerror"
"github.com/opencontainers/runtime-tools/validation/util"
uuid "github.com/satori/go.uuid"
)

func main() {
t := tap.New()
t.Header(0)

g := util.GetDefaultGenerator()

var output string
config := util.LifecycleConfig{
Actions: util.LifecycleActionCreate | util.LifecycleActionStart | util.LifecycleActionDelete,
PreCreate: func(r *util.Runtime) error {
r.SetID(uuid.NewV4().String())

output = filepath.Join(r.BundleDir, g.Spec().Root.Path, "output")
prestart := rspec.Hook{
Path: fmt.Sprintf("%s/%s/bin/sh", r.BundleDir, g.Spec().Root.Path),
Args: []string{
"sh", "-c", fmt.Sprintf("echo 'pre-start called' >> %s", output),
},
}

g.AddPreStartHook(prestart)
g.SetProcessArgs([]string{"sh", "-c", fmt.Sprintf("echo 'process called' >> %s", "/output")})
r.SetConfig(g)
return nil
},
PostCreate: func(r *util.Runtime) error {
outputData, err := ioutil.ReadFile(output)
if err == nil {
if strings.Contains(string(outputData), "pre-start called") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always get this in 'runc'.

return specerror.NewError(specerror.PrestartTiming, fmt.Errorf("Pre-start hooks MUST be called after the `start` operation is called"), rspec.Version)
} else if strings.Contains(string(outputData), "process called") {
return specerror.NewError(specerror.ProcNotRunAtResRequest, fmt.Errorf("The user-specified program (from process) MUST NOT be run at this time"), rspec.Version)
}

return fmt.Errorf("File %v should not exist", output)
}
return nil
},
PreDelete: func(r *util.Runtime) error {
util.WaitingForStatus(*r, util.LifecycleStatusStopped, time.Second*10, time.Second)
outputData, err := ioutil.ReadFile(output)
if err != nil {
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))
}
switch string(outputData) {
case "pre-start called\n":
return specerror.NewError(specerror.ProcImplement, fmt.Errorf("The runtime MUST run the user-specified program, as specified by `process`"), rspec.Version)
case "process called\n":
return specerror.NewError(specerror.PrestartHooksInvoke, fmt.Errorf("The prestart hooks MUST be invoked by the runtime"), rspec.Version)
case "pre-start called\nprocess called\n":
return nil
case "process called\npre-start called\n":
return specerror.NewError(specerror.PrestartTiming, fmt.Errorf("Pre-start hooks MUST be called before the user-specified program command is executed"), rspec.Version)
default:
return fmt.Errorf("Unsupported output information: %v", string(outputData))
}
},
}

err := util.RuntimeLifecycleValidate(g, config)
if err != nil {
diagnostic := map[string]string{
"error": err.Error(),
}
if e, ok := err.(*exec.ExitError); ok {
if len(e.Stderr) > 0 {
diagnostic["stderr"] = string(e.Stderr)
}
}
t.YAML(diagnostic)
}

t.AutoPlan()
}