Merge pull request #724 from cloudfoundry-incubator/hookstate-bundlepath
HookState adhears to OCI
This commit is contained in:
commit
4023fe0fb9
3
exec.go
3
exec.go
|
@ -10,6 +10,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
"github.com/opencontainers/specs/specs-go"
|
"github.com/opencontainers/specs/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -103,7 +104,7 @@ func execProcess(context *cli.Context) (int, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
}
|
}
|
||||||
bundle := searchLabels(state.Config.Labels, "bundle")
|
bundle := utils.SearchLabels(state.Config.Labels, "bundle")
|
||||||
p, err := getProcess(context, bundle)
|
p, err := getProcess(context, bundle)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return -1, err
|
return -1, err
|
||||||
|
|
|
@ -249,10 +249,11 @@ func (hooks Hooks) MarshalJSON() ([]byte, error) {
|
||||||
|
|
||||||
// HookState is the payload provided to a hook on execution.
|
// HookState is the payload provided to a hook on execution.
|
||||||
type HookState struct {
|
type HookState struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"ociVersion"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Pid int `json:"pid"`
|
Pid int `json:"pid"`
|
||||||
Root string `json:"root"`
|
Root string `json:"root"`
|
||||||
|
BundlePath string `json:"bundlePath"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Hook interface {
|
type Hook interface {
|
||||||
|
|
|
@ -205,10 +205,11 @@ func (c *linuxContainer) Start(process *Process) error {
|
||||||
}
|
}
|
||||||
if c.config.Hooks != nil {
|
if c.config.Hooks != nil {
|
||||||
s := configs.HookState{
|
s := configs.HookState{
|
||||||
Version: c.config.Version,
|
Version: c.config.Version,
|
||||||
ID: c.id,
|
ID: c.id,
|
||||||
Pid: parent.pid(),
|
Pid: parent.pid(),
|
||||||
Root: c.config.Rootfs,
|
Root: c.config.Rootfs,
|
||||||
|
BundlePath: utils.SearchLabels(c.config.Labels, "bundle"),
|
||||||
}
|
}
|
||||||
for _, hook := range c.config.Hooks.Poststart {
|
for _, hook := range c.config.Hooks.Poststart {
|
||||||
if err := hook.Run(s); err != nil {
|
if err := hook.Run(s); err != nil {
|
||||||
|
|
|
@ -1068,9 +1068,15 @@ func TestHook(t *testing.T) {
|
||||||
defer remove(rootfs)
|
defer remove(rootfs)
|
||||||
|
|
||||||
config := newTemplateConfig(rootfs)
|
config := newTemplateConfig(rootfs)
|
||||||
|
expectedBundlePath := "/path/to/bundle/path"
|
||||||
|
config.Labels = append(config.Labels, fmt.Sprintf("bundle=%s", expectedBundlePath))
|
||||||
config.Hooks = &configs.Hooks{
|
config.Hooks = &configs.Hooks{
|
||||||
Prestart: []configs.Hook{
|
Prestart: []configs.Hook{
|
||||||
configs.NewFunctionHook(func(s configs.HookState) error {
|
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"))
|
f, err := os.Create(filepath.Join(s.Root, "test"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -1078,8 +1084,21 @@ func TestHook(t *testing.T) {
|
||||||
return f.Close()
|
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{
|
Poststop: []configs.Hook{
|
||||||
configs.NewFunctionHook(func(s configs.HookState) error {
|
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"))
|
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)
|
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 {
|
if err := container.Destroy(); err != nil {
|
||||||
t.Fatalf("container destory %s", err)
|
t.Fatalf("container destory %s", err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,10 +318,11 @@ loop:
|
||||||
case procHooks:
|
case procHooks:
|
||||||
if p.config.Config.Hooks != nil {
|
if p.config.Config.Hooks != nil {
|
||||||
s := configs.HookState{
|
s := configs.HookState{
|
||||||
Version: p.container.config.Version,
|
Version: p.container.config.Version,
|
||||||
ID: p.container.id,
|
ID: p.container.id,
|
||||||
Pid: p.pid(),
|
Pid: p.pid(),
|
||||||
Root: p.config.Config.Rootfs,
|
Root: p.config.Config.Rootfs,
|
||||||
|
BundlePath: utils.SearchLabels(p.config.Config.Labels, "bundle"),
|
||||||
}
|
}
|
||||||
for _, hook := range p.config.Config.Hooks.Prestart {
|
for _, hook := range p.config.Config.Hooks.Prestart {
|
||||||
if err := hook.Run(s); err != nil {
|
if err := hook.Run(s); err != nil {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/opencontainers/runc/libcontainer/configs"
|
"github.com/opencontainers/runc/libcontainer/configs"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func newStateTransitionError(from, to containerState) error {
|
func newStateTransitionError(from, to containerState) error {
|
||||||
|
@ -56,9 +57,10 @@ func destroy(c *linuxContainer) error {
|
||||||
func runPoststopHooks(c *linuxContainer) error {
|
func runPoststopHooks(c *linuxContainer) error {
|
||||||
if c.config.Hooks != nil {
|
if c.config.Hooks != nil {
|
||||||
s := configs.HookState{
|
s := configs.HookState{
|
||||||
Version: c.config.Version,
|
Version: c.config.Version,
|
||||||
ID: c.id,
|
ID: c.id,
|
||||||
Root: c.config.Rootfs,
|
Root: c.config.Rootfs,
|
||||||
|
BundlePath: utils.SearchLabels(c.config.Labels, "bundle"),
|
||||||
}
|
}
|
||||||
for _, hook := range c.config.Hooks.Poststop {
|
for _, hook := range c.config.Hooks.Poststop {
|
||||||
if err := hook.Run(s); err != nil {
|
if err := hook.Run(s); err != nil {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -84,3 +85,18 @@ func CleanPath(path string) string {
|
||||||
// Clean the path again for good measure.
|
// Clean the path again for good measure.
|
||||||
return filepath.Clean(path)
|
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 ""
|
||||||
|
}
|
||||||
|
|
|
@ -23,3 +23,24 @@ func TestGenerateName(t *testing.T) {
|
||||||
t.Fatalf("expected name to be %d chars but received %d", expected, len(name))
|
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
17
list.go
|
@ -7,13 +7,13 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
"text/tabwriter"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const formatOptions = `table or json`
|
const formatOptions = `table or json`
|
||||||
|
@ -116,22 +116,9 @@ func getContainers(context *cli.Context) ([]containerState, error) {
|
||||||
ID: state.BaseState.ID,
|
ID: state.BaseState.ID,
|
||||||
InitProcessPid: state.BaseState.InitProcessPid,
|
InitProcessPid: state.BaseState.InitProcessPid,
|
||||||
Status: containerStatus.String(),
|
Status: containerStatus.String(),
|
||||||
Bundle: searchLabels(state.Config.Labels, "bundle"),
|
Bundle: utils.SearchLabels(state.Config.Labels, "bundle"),
|
||||||
Created: state.BaseState.Created})
|
Created: state.BaseState.Created})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return s, nil
|
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 ""
|
|
||||||
}
|
|
||||||
|
|
3
state.go
3
state.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/codegangsta/cli"
|
"github.com/codegangsta/cli"
|
||||||
|
"github.com/opencontainers/runc/libcontainer/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// cState represents the platform agnostic pieces relating to a running
|
// cState represents the platform agnostic pieces relating to a running
|
||||||
|
@ -57,7 +58,7 @@ instance of a container.`,
|
||||||
ID: state.BaseState.ID,
|
ID: state.BaseState.ID,
|
||||||
InitProcessPid: state.BaseState.InitProcessPid,
|
InitProcessPid: state.BaseState.InitProcessPid,
|
||||||
Status: containerStatus.String(),
|
Status: containerStatus.String(),
|
||||||
Bundle: searchLabels(state.Config.Labels, "bundle"),
|
Bundle: utils.SearchLabels(state.Config.Labels, "bundle"),
|
||||||
Rootfs: state.BaseState.Config.Rootfs,
|
Rootfs: state.BaseState.Config.Rootfs,
|
||||||
Created: state.BaseState.Created}
|
Created: state.BaseState.Created}
|
||||||
data, err := json.MarshalIndent(cs, "", " ")
|
data, err := json.MarshalIndent(cs, "", " ")
|
||||||
|
|
Loading…
Reference in New Issue