summaryrefslogtreecommitdiff
path: root/pkg/hooks/hooks.go
diff options
context:
space:
mode:
Diffstat (limited to 'pkg/hooks/hooks.go')
-rw-r--r--pkg/hooks/hooks.go177
1 files changed, 70 insertions, 107 deletions
diff --git a/pkg/hooks/hooks.go b/pkg/hooks/hooks.go
index dbcd7b773..f079dd0f7 100644
--- a/pkg/hooks/hooks.go
+++ b/pkg/hooks/hooks.go
@@ -1,141 +1,104 @@
+// Package hooks implements CRI-O's hook handling.
package hooks
import (
- "encoding/json"
+ "context"
"fmt"
- "io/ioutil"
- "os"
"path/filepath"
- "regexp"
- "strings"
- "syscall"
+ "sync"
- spec "github.com/opencontainers/runtime-spec/specs-go"
- "github.com/opencontainers/runtime-tools/generate"
+ rspec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/pkg/errors"
- "github.com/sirupsen/logrus"
+ current "github.com/projectatomic/libpod/pkg/hooks/1.0.0"
)
+// Version is the current hook configuration version.
+const Version = current.Version
+
const (
- // DefaultHooksDir Default directory containing hooks config files
- DefaultHooksDir = "/usr/share/containers/oci/hooks.d"
- // OverrideHooksDir Directory where admin can override the default configuration
- OverrideHooksDir = "/etc/containers/oci/hooks.d"
+ // DefaultDir is the default directory containing system hook configuration files.
+ DefaultDir = "/usr/share/containers/oci/hooks.d"
+
+ // OverrideDir is the directory for hook configuration files overriding the default entries.
+ OverrideDir = "/etc/containers/oci/hooks.d"
)
-// HookParams is the structure returned from read the hooks configuration
-type HookParams struct {
- Hook string `json:"hook"`
- Stage []string `json:"stage"`
- Cmds []string `json:"cmd"`
- Annotations []string `json:"annotation"`
- HasBindMounts bool `json:"hasbindmounts"`
- Arguments []string `json:"arguments"`
+// Manager provides an opaque interface for managing CRI-O hooks.
+type Manager struct {
+ hooks map[string]*current.Hook
+ directories []string
+ lock sync.Mutex
}
-// readHook reads hooks json files, verifies it and returns the json config
-func readHook(hookPath string) (HookParams, error) {
- var hook HookParams
- raw, err := ioutil.ReadFile(hookPath)
- if err != nil {
- return hook, errors.Wrapf(err, "error Reading hook %q", hookPath)
- }
- if err := json.Unmarshal(raw, &hook); err != nil {
- return hook, errors.Wrapf(err, "error Unmarshalling JSON for %q", hookPath)
+// New creates a new hook manager. Directories are ordered by
+// increasing preference (hook configurations in later directories
+// override configurations with the same filename from earlier
+// directories).
+func New(ctx context.Context, directories []string) (manager *Manager, err error) {
+ manager = &Manager{
+ hooks: map[string]*current.Hook{},
+ directories: directories,
}
- if _, err := os.Stat(hook.Hook); err != nil {
- return hook, errors.Wrapf(err, "unable to stat hook %q in hook config %q", hook.Hook, hookPath)
- }
- validStage := map[string]bool{"prestart": true, "poststart": true, "poststop": true}
- for _, cmd := range hook.Cmds {
- if _, err = regexp.Compile(cmd); err != nil {
- return hook, errors.Wrapf(err, "invalid cmd regular expression %q defined in hook config %q", cmd, hookPath)
- }
- }
- for _, cmd := range hook.Annotations {
- if _, err = regexp.Compile(cmd); err != nil {
- return hook, errors.Wrapf(err, "invalid cmd regular expression %q defined in hook config %q", cmd, hookPath)
- }
- }
- for _, stage := range hook.Stage {
- if !validStage[stage] {
- return hook, errors.Wrapf(err, "unknown stage %q defined in hook config %q", stage, hookPath)
- }
- }
- return hook, nil
-}
-// readHooks reads hooks json files in directory to setup OCI Hooks
-// adding hooks to the passedin hooks map.
-func readHooks(hooksPath string, hooks map[string]HookParams) error {
- if _, err := os.Stat(hooksPath); err != nil {
- if os.IsNotExist(err) {
- logrus.Warnf("hooks path: %q does not exist", hooksPath)
- return nil
+ for _, dir := range directories {
+ err = ReadDir(dir, manager.hooks)
+ if err != nil {
+ return nil, err
}
- return errors.Wrapf(err, "unable to stat hooks path %q", hooksPath)
}
- files, err := ioutil.ReadDir(hooksPath)
- if err != nil {
- return err
- }
+ return manager, nil
+}
- for _, file := range files {
- if !strings.HasSuffix(file.Name(), ".json") {
- continue
- }
- hook, err := readHook(filepath.Join(hooksPath, file.Name()))
+// Hooks injects OCI runtime hooks for a given container configuration.
+func (m *Manager) Hooks(config *rspec.Spec, annotations map[string]string, hasBindMounts bool) (err error) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ for name, hook := range m.hooks {
+ match, err := hook.When.Match(config, annotations, hasBindMounts)
if err != nil {
- return err
+ return errors.Wrapf(err, "matching hook %q", name)
}
- for key, h := range hooks {
- // hook.Hook can only be defined in one hook file, unless it has the
- // same name in the override path.
- if hook.Hook == h.Hook && key != file.Name() {
- return errors.Wrapf(syscall.EINVAL, "duplicate path, hook %q from %q already defined in %q", hook.Hook, hooksPath, key)
+ if match {
+ if config.Hooks == nil {
+ config.Hooks = &rspec.Hooks{}
+ }
+ for _, stage := range hook.Stages {
+ switch stage {
+ case "prestart":
+ config.Hooks.Prestart = append(config.Hooks.Prestart, hook.Hook)
+ case "poststart":
+ config.Hooks.Poststart = append(config.Hooks.Poststart, hook.Hook)
+ case "poststop":
+ config.Hooks.Poststop = append(config.Hooks.Poststop, hook.Hook)
+ default:
+ return fmt.Errorf("hook %q: unknown stage %q", name, stage)
+ }
}
}
- hooks[file.Name()] = hook
}
return nil
}
-// SetupHooks takes a hookspath and reads all of the hooks in that directory.
-// returning a map of the configured hooks
-func SetupHooks(hooksPath string) (map[string]HookParams, error) {
- hooksMap := make(map[string]HookParams)
- if err := readHooks(hooksPath, hooksMap); err != nil {
- return nil, err
- }
- if hooksPath == DefaultHooksDir {
- if err := readHooks(OverrideHooksDir, hooksMap); err != nil {
- return nil, err
- }
+// remove remove a hook by name.
+func (m *Manager) remove(hook string) (ok bool) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ _, ok = m.hooks[hook]
+ if ok {
+ delete(m.hooks, hook)
}
-
- return hooksMap, nil
+ return ok
}
-// AddOCIHook generates OCI specification using the included hook
-func AddOCIHook(g *generate.Generator, hook HookParams) error {
- for _, stage := range hook.Stage {
- h := spec.Hook{
- Path: hook.Hook,
- Args: append([]string{hook.Hook}, hook.Arguments...),
- Env: []string{fmt.Sprintf("stage=%s", stage)},
- }
- logrus.Debugf("AddOCIHook", h)
- switch stage {
- case "prestart":
- g.AddPreStartHook(h)
-
- case "poststart":
- g.AddPostStartHook(h)
-
- case "poststop":
- g.AddPostStopHook(h)
- }
+// add adds a hook by path
+func (m *Manager) add(path string) (err error) {
+ m.lock.Lock()
+ defer m.lock.Unlock()
+ hook, err := Read(path)
+ if err != nil {
+ return err
}
+ m.hooks[filepath.Base(path)] = hook
return nil
}