mirror of
				https://github.com/containers/podman.git
				synced 2025-10-31 18:08:51 +08:00 
			
		
		
		
	 97c9115c02
			
		
	
	97c9115c02
	
	
	
		
			
			Don't sort OCI hooks using the locale collation order; it does not make sense for the same system-wide directory to be interpreted differently depending on the user's LC_COLLATE setting, and the language-specific collation order can even change over time. Besides, the current collation order determination code has never worked with the most common LC_COLLATE values like en_US.UTF-8. Ideally, we would like to just order based on Unicode code points to be reliably stable, but the existing implementation is case-insensitive, so we are forced to rely on the unicode case mapping tables at least. (This gives up on canonicalization and width-insensitivity, potentially breaking users who rely on these previously documented properties.) Signed-off-by: Miloslav Trmač <mitr@redhat.com>
		
			
				
	
	
		
			164 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			164 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Package hooks implements hook configuration and handling for CRI-O and libpod.
 | |
| package hooks
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"fmt"
 | |
| 	"path/filepath"
 | |
| 	"sort"
 | |
| 	"strings"
 | |
| 	"sync"
 | |
| 
 | |
| 	current "github.com/containers/libpod/pkg/hooks/1.0.0"
 | |
| 	rspec "github.com/opencontainers/runtime-spec/specs-go"
 | |
| 	"github.com/pkg/errors"
 | |
| 	"github.com/sirupsen/logrus"
 | |
| )
 | |
| 
 | |
| // Version is the current hook configuration version.
 | |
| const Version = current.Version
 | |
| 
 | |
| const (
 | |
| 	// 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"
 | |
| )
 | |
| 
 | |
| // Manager provides an opaque interface for managing CRI-O hooks.
 | |
| type Manager struct {
 | |
| 	hooks           map[string]*current.Hook
 | |
| 	directories     []string
 | |
| 	extensionStages []string
 | |
| 	lock            sync.Mutex
 | |
| }
 | |
| 
 | |
| type namedHook struct {
 | |
| 	name string
 | |
| 	hook *current.Hook
 | |
| }
 | |
| 
 | |
| // 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).
 | |
| //
 | |
| // extensionStages allows callers to add additional stages beyond
 | |
| // those specified in the OCI Runtime Specification and to control
 | |
| // OCI-defined stages instead of delagating to the OCI runtime.  See
 | |
| // Hooks() for more information.
 | |
| func New(ctx context.Context, directories []string, extensionStages []string) (manager *Manager, err error) {
 | |
| 	manager = &Manager{
 | |
| 		hooks:           map[string]*current.Hook{},
 | |
| 		directories:     directories,
 | |
| 		extensionStages: extensionStages,
 | |
| 	}
 | |
| 
 | |
| 	for _, dir := range directories {
 | |
| 		err = ReadDir(dir, manager.extensionStages, manager.hooks)
 | |
| 		if err != nil {
 | |
| 			return nil, err
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return manager, nil
 | |
| }
 | |
| 
 | |
| // filenames returns sorted hook entries.
 | |
| func (m *Manager) namedHooks() (hooks []*namedHook) {
 | |
| 	m.lock.Lock()
 | |
| 	defer m.lock.Unlock()
 | |
| 
 | |
| 	hooks = make([]*namedHook, len(m.hooks))
 | |
| 	i := 0
 | |
| 	for name, hook := range m.hooks {
 | |
| 		hooks[i] = &namedHook{
 | |
| 			name: name,
 | |
| 			hook: hook,
 | |
| 		}
 | |
| 		i++
 | |
| 	}
 | |
| 
 | |
| 	return hooks
 | |
| }
 | |
| 
 | |
| // Hooks injects OCI runtime hooks for a given container configuration.
 | |
| //
 | |
| // If extensionStages was set when initializing the Manager,
 | |
| // matching hooks requesting those stages will be returned in
 | |
| // extensionStageHooks.  This takes precedence over their inclusion in
 | |
| // the OCI configuration.  For example:
 | |
| //
 | |
| //   manager, err := New(ctx, []string{DefaultDir}, []string{"poststop"})
 | |
| //   extensionStageHooks, err := manager.Hooks(config, annotations, hasBindMounts)
 | |
| //
 | |
| // will have any matching post-stop hooks in extensionStageHooks and
 | |
| // will not insert them into config.Hooks.Poststop.
 | |
| func (m *Manager) Hooks(config *rspec.Spec, annotations map[string]string, hasBindMounts bool) (extensionStageHooks map[string][]rspec.Hook, err error) {
 | |
| 	hooks := m.namedHooks()
 | |
| 	sort.Slice(hooks, func(i, j int) bool { return strings.ToLower(hooks[i].name) < strings.ToLower(hooks[j].name) })
 | |
| 	localStages := map[string]bool{} // stages destined for extensionStageHooks
 | |
| 	for _, stage := range m.extensionStages {
 | |
| 		localStages[stage] = true
 | |
| 	}
 | |
| 	for _, namedHook := range hooks {
 | |
| 		match, err := namedHook.hook.When.Match(config, annotations, hasBindMounts)
 | |
| 		if err != nil {
 | |
| 			return extensionStageHooks, errors.Wrapf(err, "matching hook %q", namedHook.name)
 | |
| 		}
 | |
| 		if match {
 | |
| 			logrus.Debugf("hook %s matched; adding to stages %v", namedHook.name, namedHook.hook.Stages)
 | |
| 			if config.Hooks == nil {
 | |
| 				config.Hooks = &rspec.Hooks{}
 | |
| 			}
 | |
| 			for _, stage := range namedHook.hook.Stages {
 | |
| 				if _, ok := localStages[stage]; ok {
 | |
| 					if extensionStageHooks == nil {
 | |
| 						extensionStageHooks = map[string][]rspec.Hook{}
 | |
| 					}
 | |
| 					extensionStageHooks[stage] = append(extensionStageHooks[stage], namedHook.hook.Hook)
 | |
| 				} else {
 | |
| 					switch stage {
 | |
| 					case "prestart":
 | |
| 						config.Hooks.Prestart = append(config.Hooks.Prestart, namedHook.hook.Hook)
 | |
| 					case "poststart":
 | |
| 						config.Hooks.Poststart = append(config.Hooks.Poststart, namedHook.hook.Hook)
 | |
| 					case "poststop":
 | |
| 						config.Hooks.Poststop = append(config.Hooks.Poststop, namedHook.hook.Hook)
 | |
| 					default:
 | |
| 						return extensionStageHooks, fmt.Errorf("hook %q: unknown stage %q", namedHook.name, stage)
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 		} else {
 | |
| 			logrus.Debugf("hook %s did not match", namedHook.name)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return extensionStageHooks, nil
 | |
| }
 | |
| 
 | |
| // 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 ok
 | |
| }
 | |
| 
 | |
| // 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, m.extensionStages)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 	m.hooks[filepath.Base(path)] = hook
 | |
| 	return nil
 | |
| }
 |