Merge pull request #724 from cloudfoundry-incubator/hookstate-bundlepath

HookState adhears to OCI
This commit is contained in:
Mrunal Patel 2016-04-11 14:59:14 -07:00
commit 4023fe0fb9
10 changed files with 92 additions and 32 deletions

View File

@ -10,6 +10,7 @@ import (
"strings"
"github.com/codegangsta/cli"
"github.com/opencontainers/runc/libcontainer/utils"
"github.com/opencontainers/specs/specs-go"
)
@ -103,7 +104,7 @@ func execProcess(context *cli.Context) (int, error) {
if err != nil {
return -1, err
}
bundle := searchLabels(state.Config.Labels, "bundle")
bundle := utils.SearchLabels(state.Config.Labels, "bundle")
p, err := getProcess(context, bundle)
if err != nil {
return -1, err

View File

@ -249,10 +249,11 @@ func (hooks Hooks) MarshalJSON() ([]byte, error) {
// HookState is the payload provided to a hook on execution.
type HookState struct {
Version string `json:"version"`
ID string `json:"id"`
Pid int `json:"pid"`
Root string `json:"root"`
Version string `json:"ociVersion"`
ID string `json:"id"`
Pid int `json:"pid"`
Root string `json:"root"`
BundlePath string `json:"bundlePath"`
}
type Hook interface {

View File

@ -205,10 +205,11 @@ func (c *linuxContainer) Start(process *Process) error {
}
if c.config.Hooks != nil {
s := configs.HookState{
Version: c.config.Version,
ID: c.id,
Pid: parent.pid(),
Root: c.config.Rootfs,
Version: c.config.Version,
ID: c.id,
Pid: parent.pid(),
Root: c.config.Rootfs,
BundlePath: utils.SearchLabels(c.config.Labels, "bundle"),
}
for _, hook := range c.config.Hooks.Poststart {
if err := hook.Run(s); err != nil {

View File

@ -1068,9 +1068,15 @@ func TestHook(t *testing.T) {
defer remove(rootfs)
config := newTemplateConfig(rootfs)
expectedBundlePath := "/path/to/bundle/path"
config.Labels = append(config.Labels, fmt.Sprintf("bundle=%s", expectedBundlePath))
config.Hooks = &configs.Hooks{
Prestart: []configs.Hook{
configs.NewFunctionHook(func(s configs.HookState) error {
if s.BundlePath != expectedBundlePath {
t.Fatalf("Expected prestart hook bundlePath '%s'; got '%s'", expectedBundlePath, s.BundlePath)
}
f, err := os.Create(filepath.Join(s.Root, "test"))
if err != nil {
return err
@ -1078,8 +1084,21 @@ func TestHook(t *testing.T) {
return f.Close()
}),
},
Poststart: []configs.Hook{
configs.NewFunctionHook(func(s configs.HookState) error {
if s.BundlePath != expectedBundlePath {
t.Fatalf("Expected poststart hook bundlePath '%s'; got '%s'", expectedBundlePath, s.BundlePath)
}
return ioutil.WriteFile(filepath.Join(s.Root, "test"), []byte("hello world"), 0755)
}),
},
Poststop: []configs.Hook{
configs.NewFunctionHook(func(s configs.HookState) error {
if s.BundlePath != expectedBundlePath {
t.Fatalf("Expected poststop hook bundlePath '%s'; got '%s'", expectedBundlePath, s.BundlePath)
}
return os.RemoveAll(filepath.Join(s.Root, "test"))
}),
},
@ -1109,6 +1128,16 @@ func TestHook(t *testing.T) {
t.Fatalf("ls output doesn't have the expected file: %s", outputLs)
}
// Check that the file is written by the poststart hook
testFilePath := filepath.Join(rootfs, "test")
contents, err := ioutil.ReadFile(testFilePath)
if err != nil {
t.Fatalf("cannot read file '%s': %s", testFilePath, err)
}
if string(contents) != "hello world" {
t.Fatalf("Expected test file to contain 'hello world'; got '%s'", string(contents))
}
if err := container.Destroy(); err != nil {
t.Fatalf("container destory %s", err)
}

View File

@ -318,10 +318,11 @@ loop:
case procHooks:
if p.config.Config.Hooks != nil {
s := configs.HookState{
Version: p.container.config.Version,
ID: p.container.id,
Pid: p.pid(),
Root: p.config.Config.Rootfs,
Version: p.container.config.Version,
ID: p.container.id,
Pid: p.pid(),
Root: p.config.Config.Rootfs,
BundlePath: utils.SearchLabels(p.config.Config.Labels, "bundle"),
}
for _, hook := range p.config.Config.Hooks.Prestart {
if err := hook.Run(s); err != nil {

View File

@ -9,6 +9,7 @@ import (
"github.com/Sirupsen/logrus"
"github.com/opencontainers/runc/libcontainer/configs"
"github.com/opencontainers/runc/libcontainer/utils"
)
func newStateTransitionError(from, to containerState) error {
@ -56,9 +57,10 @@ func destroy(c *linuxContainer) error {
func runPoststopHooks(c *linuxContainer) error {
if c.config.Hooks != nil {
s := configs.HookState{
Version: c.config.Version,
ID: c.id,
Root: c.config.Rootfs,
Version: c.config.Version,
ID: c.id,
Root: c.config.Rootfs,
BundlePath: utils.SearchLabels(c.config.Labels, "bundle"),
}
for _, hook := range c.config.Hooks.Poststop {
if err := hook.Run(s); err != nil {

View File

@ -7,6 +7,7 @@ import (
"io"
"os"
"path/filepath"
"strings"
"syscall"
)
@ -84,3 +85,18 @@ func CleanPath(path string) string {
// Clean the path again for good measure.
return filepath.Clean(path)
}
// SearchLabels searches a list of key-value pairs for the provided key and
// returns the corresponding value. The pairs must be separated with '='.
func SearchLabels(labels []string, query string) string {
for _, l := range labels {
parts := strings.SplitN(l, "=", 2)
if len(parts) < 2 {
continue
}
if parts[0] == query {
return parts[1]
}
}
return ""
}

View File

@ -23,3 +23,24 @@ func TestGenerateName(t *testing.T) {
t.Fatalf("expected name to be %d chars but received %d", expected, len(name))
}
}
var labelTest = []struct {
labels []string
query string
expectedValue string
}{
{[]string{"bundle=/path/to/bundle"}, "bundle", "/path/to/bundle"},
{[]string{"test=a", "test=b"}, "bundle", ""},
{[]string{"bundle=a", "test=b", "bundle=c"}, "bundle", "a"},
{[]string{"", "test=a", "bundle=b"}, "bundle", "b"},
{[]string{"test", "bundle=a"}, "bundle", "a"},
{[]string{"test=a", "bundle="}, "bundle", ""},
}
func TestSearchLabels(t *testing.T) {
for _, tt := range labelTest {
if v := SearchLabels(tt.labels, tt.query); v != tt.expectedValue {
t.Errorf("expected value '%s' for query '%s'; got '%s'", tt.expectedValue, tt.query, v)
}
}
}

17
list.go
View File

@ -7,13 +7,13 @@ import (
"io/ioutil"
"os"
"path/filepath"
"strings"
"text/tabwriter"
"time"
"encoding/json"
"github.com/codegangsta/cli"
"github.com/opencontainers/runc/libcontainer/utils"
)
const formatOptions = `table or json`
@ -116,22 +116,9 @@ func getContainers(context *cli.Context) ([]containerState, error) {
ID: state.BaseState.ID,
InitProcessPid: state.BaseState.InitProcessPid,
Status: containerStatus.String(),
Bundle: searchLabels(state.Config.Labels, "bundle"),
Bundle: utils.SearchLabels(state.Config.Labels, "bundle"),
Created: state.BaseState.Created})
}
}
return s, nil
}
func searchLabels(labels []string, query string) string {
for _, l := range labels {
parts := strings.SplitN(l, "=", 2)
if len(parts) < 2 {
continue
}
if parts[0] == query {
return parts[1]
}
}
return ""
}

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/codegangsta/cli"
"github.com/opencontainers/runc/libcontainer/utils"
)
// cState represents the platform agnostic pieces relating to a running
@ -57,7 +58,7 @@ instance of a container.`,
ID: state.BaseState.ID,
InitProcessPid: state.BaseState.InitProcessPid,
Status: containerStatus.String(),
Bundle: searchLabels(state.Config.Labels, "bundle"),
Bundle: utils.SearchLabels(state.Config.Labels, "bundle"),
Rootfs: state.BaseState.Config.Rootfs,
Created: state.BaseState.Created}
data, err := json.MarshalIndent(cs, "", " ")