mirror of
				https://github.com/containers/podman.git
				synced 2025-11-04 17:07:20 +08:00 
			
		
		
		
	Vendor in containers/(storage,image, common, buildah)
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
		
							
								
								
									
										88
									
								
								vendor/github.com/containers/common/pkg/hooks/0.1.0/hook.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								vendor/github.com/containers/common/pkg/hooks/0.1.0/hook.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,88 @@
 | 
			
		||||
// Package hook is the 0.1.0 hook configuration structure.
 | 
			
		||||
package hook
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	current "github.com/containers/common/pkg/hooks/1.0.0"
 | 
			
		||||
	rspec "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Version is the hook configuration version defined in this package.
 | 
			
		||||
const Version = "0.1.0"
 | 
			
		||||
 | 
			
		||||
// Hook is the hook configuration structure.
 | 
			
		||||
type Hook struct {
 | 
			
		||||
	Hook      *string  `json:"hook"`
 | 
			
		||||
	Arguments []string `json:"arguments,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// https://github.com/cri-o/cri-o/pull/1235
 | 
			
		||||
	Stages []string `json:"stages"`
 | 
			
		||||
	Stage  []string `json:"stage"`
 | 
			
		||||
 | 
			
		||||
	Cmds []string `json:"cmds,omitempty"`
 | 
			
		||||
	Cmd  []string `json:"cmd,omitempty"`
 | 
			
		||||
 | 
			
		||||
	Annotations []string `json:"annotations,omitempty"`
 | 
			
		||||
	Annotation  []string `json:"annotation,omitempty"`
 | 
			
		||||
 | 
			
		||||
	HasBindMounts *bool `json:"hasbindmounts,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Read(content []byte) (hook *current.Hook, err error) {
 | 
			
		||||
	var raw Hook
 | 
			
		||||
 | 
			
		||||
	if err = json.Unmarshal(content, &raw); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if raw.Hook == nil {
 | 
			
		||||
		return nil, errors.New("missing required property: hook")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if raw.Stages == nil {
 | 
			
		||||
		raw.Stages = raw.Stage
 | 
			
		||||
	} else if raw.Stage != nil {
 | 
			
		||||
		return nil, errors.New("cannot set both 'stage' and 'stages'")
 | 
			
		||||
	}
 | 
			
		||||
	if raw.Stages == nil {
 | 
			
		||||
		return nil, errors.New("missing required property: stages")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if raw.Cmds == nil {
 | 
			
		||||
		raw.Cmds = raw.Cmd
 | 
			
		||||
	} else if raw.Cmd != nil {
 | 
			
		||||
		return nil, errors.New("cannot set both 'cmd' and 'cmds'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if raw.Annotations == nil {
 | 
			
		||||
		raw.Annotations = raw.Annotation
 | 
			
		||||
	} else if raw.Annotation != nil {
 | 
			
		||||
		return nil, errors.New("cannot set both 'annotation' and 'annotations'")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hook = ¤t.Hook{
 | 
			
		||||
		Version: current.Version,
 | 
			
		||||
		Hook: rspec.Hook{
 | 
			
		||||
			Path: *raw.Hook,
 | 
			
		||||
		},
 | 
			
		||||
		When: current.When{
 | 
			
		||||
			Commands:      raw.Cmds,
 | 
			
		||||
			HasBindMounts: raw.HasBindMounts,
 | 
			
		||||
			Or:            true,
 | 
			
		||||
		},
 | 
			
		||||
		Stages: raw.Stages,
 | 
			
		||||
	}
 | 
			
		||||
	if raw.Arguments != nil {
 | 
			
		||||
		hook.Hook.Args = append([]string{*raw.Hook}, raw.Arguments...)
 | 
			
		||||
	}
 | 
			
		||||
	if raw.Annotations != nil {
 | 
			
		||||
		hook.When.Annotations = map[string]string{
 | 
			
		||||
			".*": strings.Join(raw.Annotations, "|"),
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return hook, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								vendor/github.com/containers/common/pkg/hooks/1.0.0/hook.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								vendor/github.com/containers/common/pkg/hooks/1.0.0/hook.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,89 @@
 | 
			
		||||
// Package hook is the 1.0.0 hook configuration structure.
 | 
			
		||||
package hook
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"regexp"
 | 
			
		||||
 | 
			
		||||
	rspec "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Version is the hook configuration version defined in this package.
 | 
			
		||||
const Version = "1.0.0"
 | 
			
		||||
 | 
			
		||||
// Hook is the hook configuration structure.
 | 
			
		||||
type Hook struct {
 | 
			
		||||
	Version string     `json:"version"`
 | 
			
		||||
	Hook    rspec.Hook `json:"hook"`
 | 
			
		||||
	When    When       `json:"when"`
 | 
			
		||||
	Stages  []string   `json:"stages"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Read reads hook JSON bytes, verifies them, and returns the hook configuration.
 | 
			
		||||
func Read(content []byte) (hook *Hook, err error) {
 | 
			
		||||
	if err = json.Unmarshal(content, &hook); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return hook, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate performs load-time hook validation.
 | 
			
		||||
func (hook *Hook) Validate(extensionStages []string) (err error) {
 | 
			
		||||
	if hook == nil {
 | 
			
		||||
		return errors.New("nil hook")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hook.Version != Version {
 | 
			
		||||
		return fmt.Errorf("unexpected hook version %q (expecting %v)", hook.Version, Version)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hook.Hook.Path == "" {
 | 
			
		||||
		return errors.New("missing required property: hook.path")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, err := os.Stat(hook.Hook.Path); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for key, value := range hook.When.Annotations {
 | 
			
		||||
		if _, err = regexp.Compile(key); err != nil {
 | 
			
		||||
			return fmt.Errorf("invalid annotation key %q: %w", key, err)
 | 
			
		||||
		}
 | 
			
		||||
		if _, err = regexp.Compile(value); err != nil {
 | 
			
		||||
			return fmt.Errorf("invalid annotation value %q: %w", value, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, command := range hook.When.Commands {
 | 
			
		||||
		if _, err = regexp.Compile(command); err != nil {
 | 
			
		||||
			return fmt.Errorf("invalid command %q: %w", command, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hook.Stages == nil {
 | 
			
		||||
		return errors.New("missing required property: stages")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	validStages := map[string]bool{
 | 
			
		||||
		"createContainer": true,
 | 
			
		||||
		"createRuntime":   true,
 | 
			
		||||
		"prestart":        true,
 | 
			
		||||
		"poststart":       true,
 | 
			
		||||
		"poststop":        true,
 | 
			
		||||
		"startContainer":  true,
 | 
			
		||||
	}
 | 
			
		||||
	for _, stage := range extensionStages {
 | 
			
		||||
		validStages[stage] = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, stage := range hook.Stages {
 | 
			
		||||
		if !validStages[stage] {
 | 
			
		||||
			return fmt.Errorf("unknown stage %q", stage)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										96
									
								
								vendor/github.com/containers/common/pkg/hooks/1.0.0/when.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								vendor/github.com/containers/common/pkg/hooks/1.0.0/when.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
			
		||||
package hook
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"regexp"
 | 
			
		||||
 | 
			
		||||
	rspec "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// When holds hook-injection conditions.
 | 
			
		||||
type When struct {
 | 
			
		||||
	Always        *bool             `json:"always,omitempty"`
 | 
			
		||||
	Annotations   map[string]string `json:"annotations,omitempty"`
 | 
			
		||||
	Commands      []string          `json:"commands,omitempty"`
 | 
			
		||||
	HasBindMounts *bool             `json:"hasBindMounts,omitempty"`
 | 
			
		||||
 | 
			
		||||
	// Or enables any-of matching.
 | 
			
		||||
	//
 | 
			
		||||
	// Deprecated: this property is for is backwards-compatibility with
 | 
			
		||||
	// 0.1.0 hooks.  It will be removed when we drop support for them.
 | 
			
		||||
	Or bool `json:"-"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Match returns true if the given conditions match the configuration.
 | 
			
		||||
func (when *When) Match(config *rspec.Spec, annotations map[string]string, hasBindMounts bool) (match bool, err error) {
 | 
			
		||||
	matches := 0
 | 
			
		||||
 | 
			
		||||
	if when.Always != nil {
 | 
			
		||||
		if *when.Always {
 | 
			
		||||
			if when.Or {
 | 
			
		||||
				return true, nil
 | 
			
		||||
			}
 | 
			
		||||
			matches++
 | 
			
		||||
		} else if !when.Or {
 | 
			
		||||
			return false, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if when.HasBindMounts != nil {
 | 
			
		||||
		if *when.HasBindMounts && hasBindMounts {
 | 
			
		||||
			if when.Or {
 | 
			
		||||
				return true, nil
 | 
			
		||||
			}
 | 
			
		||||
			matches++
 | 
			
		||||
		} else if !when.Or {
 | 
			
		||||
			return false, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for keyPattern, valuePattern := range when.Annotations {
 | 
			
		||||
		match := false
 | 
			
		||||
		for key, value := range annotations {
 | 
			
		||||
			match, err = regexp.MatchString(keyPattern, key)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return false, fmt.Errorf("annotation key: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if match {
 | 
			
		||||
				match, err = regexp.MatchString(valuePattern, value)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return false, fmt.Errorf("annotation value: %w", err)
 | 
			
		||||
				}
 | 
			
		||||
				if match {
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if match {
 | 
			
		||||
			if when.Or {
 | 
			
		||||
				return true, nil
 | 
			
		||||
			}
 | 
			
		||||
			matches++
 | 
			
		||||
		} else if !when.Or {
 | 
			
		||||
			return false, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if config.Process != nil && len(when.Commands) > 0 {
 | 
			
		||||
		if len(config.Process.Args) == 0 {
 | 
			
		||||
			return false, errors.New("process.args must have at least one entry")
 | 
			
		||||
		}
 | 
			
		||||
		command := config.Process.Args[0]
 | 
			
		||||
		for _, cmdPattern := range when.Commands {
 | 
			
		||||
			match, err := regexp.MatchString(cmdPattern, command)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return false, fmt.Errorf("command: %w", err)
 | 
			
		||||
			}
 | 
			
		||||
			if match {
 | 
			
		||||
				return true, nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return matches > 0, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								vendor/github.com/containers/common/pkg/hooks/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								vendor/github.com/containers/common/pkg/hooks/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
# OCI Hooks Configuration
 | 
			
		||||
 | 
			
		||||
For POSIX platforms, the [OCI runtime configuration][runtime-spec] supports [hooks][spec-hooks] for configuring custom actions related to the life cycle of the container.
 | 
			
		||||
The way you enable the hooks above is by editing the OCI runtime configuration before running the OCI runtime (e.g. [`runc`][runc]).
 | 
			
		||||
CRI-O and `podman create` create the OCI configuration for you, and this documentation allows developers to configure them to set their intended hooks.
 | 
			
		||||
 | 
			
		||||
One problem with hooks is that the runtime actually stalls execution of the container before running the hooks and stalls completion of the container, until all hooks complete.
 | 
			
		||||
This can cause some performance issues.
 | 
			
		||||
Also a lot of hooks just check if certain configuration is set and then exit early, without doing anything.
 | 
			
		||||
For example the [oci-systemd-hook][] only executes if the command is `init` or `systemd`, otherwise it just exits.
 | 
			
		||||
This means if we automatically enabled all hooks, every container would have to execute `oci-systemd-hook`, even if they don't run systemd inside of the container.
 | 
			
		||||
Performance would also suffer if we executed each hook at each stage ([pre-start][], [post-start][], and [post-stop][]).
 | 
			
		||||
 | 
			
		||||
The hooks configuration is documented in [`oci-hooks.5`](docs/oci-hooks.5.md).
 | 
			
		||||
 | 
			
		||||
[oci-systemd-hook]: https://github.com/projectatomic/oci-systemd-hook
 | 
			
		||||
[post-start]: https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#poststart
 | 
			
		||||
[post-stop]: https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#poststop
 | 
			
		||||
[pre-start]: https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#prestart
 | 
			
		||||
[runc]: https://github.com/opencontainers/runc
 | 
			
		||||
[runtime-spec]: https://github.com/opencontainers/runtime-spec/blob/v1.0.1/spec.md
 | 
			
		||||
[spec-hooks]: https://github.com/opencontainers/runtime-spec/blob/v1.0.1/config.md#posix-platform-hooks
 | 
			
		||||
							
								
								
									
										69
									
								
								vendor/github.com/containers/common/pkg/hooks/exec/exec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								vendor/github.com/containers/common/pkg/hooks/exec/exec.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,69 @@
 | 
			
		||||
// Package exec provides utilities for executing Open Container Initiative runtime hooks.
 | 
			
		||||
package exec
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	osexec "os/exec"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	rspec "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// DefaultPostKillTimeout is the recommended default post-kill timeout.
 | 
			
		||||
const DefaultPostKillTimeout = time.Duration(10) * time.Second
 | 
			
		||||
 | 
			
		||||
// Run executes the hook and waits for it to complete or for the
 | 
			
		||||
// context or hook-specified timeout to expire.
 | 
			
		||||
func Run(ctx context.Context, hook *rspec.Hook, state []byte, stdout io.Writer, stderr io.Writer, postKillTimeout time.Duration) (hookErr, err error) {
 | 
			
		||||
	cmd := osexec.Cmd{
 | 
			
		||||
		Path:   hook.Path,
 | 
			
		||||
		Args:   hook.Args,
 | 
			
		||||
		Env:    hook.Env,
 | 
			
		||||
		Stdin:  bytes.NewReader(state),
 | 
			
		||||
		Stdout: stdout,
 | 
			
		||||
		Stderr: stderr,
 | 
			
		||||
	}
 | 
			
		||||
	if cmd.Env == nil {
 | 
			
		||||
		cmd.Env = []string{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hook.Timeout != nil {
 | 
			
		||||
		var cancel context.CancelFunc
 | 
			
		||||
		ctx, cancel = context.WithTimeout(ctx, time.Duration(*hook.Timeout)*time.Second)
 | 
			
		||||
		defer cancel()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = cmd.Start()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err, err
 | 
			
		||||
	}
 | 
			
		||||
	exit := make(chan error, 1)
 | 
			
		||||
	go func() {
 | 
			
		||||
		err := cmd.Wait()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			err = fmt.Errorf("executing %v: %w", cmd.Args, err)
 | 
			
		||||
		}
 | 
			
		||||
		exit <- err
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	select {
 | 
			
		||||
	case err = <-exit:
 | 
			
		||||
		return err, err
 | 
			
		||||
	case <-ctx.Done():
 | 
			
		||||
		if err := cmd.Process.Kill(); err != nil {
 | 
			
		||||
			logrus.Errorf("Failed to kill pid %v", cmd.Process)
 | 
			
		||||
		}
 | 
			
		||||
		timer := time.NewTimer(postKillTimeout)
 | 
			
		||||
		defer timer.Stop()
 | 
			
		||||
		select {
 | 
			
		||||
		case <-timer.C:
 | 
			
		||||
			err = fmt.Errorf("failed to reap process within %s of the kill signal", postKillTimeout)
 | 
			
		||||
		case err = <-exit:
 | 
			
		||||
		}
 | 
			
		||||
		return err, ctx.Err()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										72
									
								
								vendor/github.com/containers/common/pkg/hooks/exec/runtimeconfigfilter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								vendor/github.com/containers/common/pkg/hooks/exec/runtimeconfigfilter.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
			
		||||
package exec
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"context"
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/davecgh/go-spew/spew"
 | 
			
		||||
	spec "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"github.com/pmezard/go-difflib/difflib"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var spewConfig = spew.ConfigState{
 | 
			
		||||
	Indent:                  " ",
 | 
			
		||||
	DisablePointerAddresses: true,
 | 
			
		||||
	DisableCapacities:       true,
 | 
			
		||||
	SortKeys:                true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RuntimeConfigFilter calls a series of hooks.  But instead of
 | 
			
		||||
// passing container state on their standard input,
 | 
			
		||||
// RuntimeConfigFilter passes the proposed runtime configuration (and
 | 
			
		||||
// reads back a possibly-altered form from their standard output).
 | 
			
		||||
func RuntimeConfigFilter(ctx context.Context, hooks []spec.Hook, config *spec.Spec, postKillTimeout time.Duration) (hookErr, err error) {
 | 
			
		||||
	data, err := json.Marshal(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	for i, hook := range hooks {
 | 
			
		||||
		hook := hook
 | 
			
		||||
		var stdout bytes.Buffer
 | 
			
		||||
		hookErr, err = Run(ctx, &hook, data, &stdout, nil, postKillTimeout)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return hookErr, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data = stdout.Bytes()
 | 
			
		||||
		var newConfig spec.Spec
 | 
			
		||||
		err = json.Unmarshal(data, &newConfig)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Debugf("invalid JSON from config-filter hook %d:\n%s", i, string(data))
 | 
			
		||||
			return nil, fmt.Errorf("unmarshal output from config-filter hook %d: %w", i, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !reflect.DeepEqual(config, &newConfig) {
 | 
			
		||||
			oldConfig := spewConfig.Sdump(config)
 | 
			
		||||
			newConfig := spewConfig.Sdump(&newConfig)
 | 
			
		||||
			diff, err := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{
 | 
			
		||||
				A:        difflib.SplitLines(oldConfig),
 | 
			
		||||
				B:        difflib.SplitLines(newConfig),
 | 
			
		||||
				FromFile: "Old",
 | 
			
		||||
				FromDate: "",
 | 
			
		||||
				ToFile:   "New",
 | 
			
		||||
				ToDate:   "",
 | 
			
		||||
				Context:  1,
 | 
			
		||||
			})
 | 
			
		||||
			if err == nil {
 | 
			
		||||
				logrus.Debugf("precreate hook %d made configuration changes:\n%s", i, diff)
 | 
			
		||||
			} else {
 | 
			
		||||
				logrus.Warnf("Precreate hook %d made configuration changes, but we could not compute a diff: %v", i, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		*config = newConfig
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil, nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										146
									
								
								vendor/github.com/containers/common/pkg/hooks/hooks.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								vendor/github.com/containers/common/pkg/hooks/hooks.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,146 @@
 | 
			
		||||
// Package hooks implements hook configuration and handling for CRI-O and libpod.
 | 
			
		||||
package hooks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	current "github.com/containers/common/pkg/hooks/1.0.0"
 | 
			
		||||
	rspec "github.com/opencontainers/runtime-spec/specs-go"
 | 
			
		||||
	"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 delegating 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 && !errors.Is(err, os.ErrNotExist) {
 | 
			
		||||
			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, fmt.Errorf("matching hook %q: %w", namedHook.name, err)
 | 
			
		||||
		}
 | 
			
		||||
		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 "createContainer":
 | 
			
		||||
						config.Hooks.CreateContainer = append(config.Hooks.CreateContainer, namedHook.hook.Hook)
 | 
			
		||||
					case "createRuntime":
 | 
			
		||||
						config.Hooks.CreateRuntime = append(config.Hooks.CreateRuntime, namedHook.hook.Hook)
 | 
			
		||||
					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)
 | 
			
		||||
					case "startContainer":
 | 
			
		||||
						config.Hooks.StartContainer = append(config.Hooks.StartContainer, 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
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								vendor/github.com/containers/common/pkg/hooks/monitor.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								vendor/github.com/containers/common/pkg/hooks/monitor.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
			
		||||
package hooks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"context"
 | 
			
		||||
 | 
			
		||||
	current "github.com/containers/common/pkg/hooks/1.0.0"
 | 
			
		||||
	"github.com/fsnotify/fsnotify"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Monitor dynamically monitors hook directories for additions,
 | 
			
		||||
// updates, and removals.
 | 
			
		||||
//
 | 
			
		||||
// This function writes two empty structs to the sync channel: the
 | 
			
		||||
// first is written after the watchers are established and the second
 | 
			
		||||
// when this function exits.  The expected usage is:
 | 
			
		||||
//
 | 
			
		||||
//   ctx, cancel := context.WithCancel(context.Background())
 | 
			
		||||
//   sync := make(chan error, 2)
 | 
			
		||||
//   go m.Monitor(ctx, sync)
 | 
			
		||||
//   err := <-sync // block until writers are established
 | 
			
		||||
//   if err != nil {
 | 
			
		||||
//     return err // failed to establish watchers
 | 
			
		||||
//   }
 | 
			
		||||
//   // do stuff
 | 
			
		||||
//   cancel()
 | 
			
		||||
//   err = <-sync // block until monitor finishes
 | 
			
		||||
func (m *Manager) Monitor(ctx context.Context, sync chan<- error) {
 | 
			
		||||
	watcher, err := fsnotify.NewWatcher()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sync <- err
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	defer watcher.Close()
 | 
			
		||||
 | 
			
		||||
	for _, dir := range m.directories {
 | 
			
		||||
		err = watcher.Add(dir)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			logrus.Errorf("Failed to watch %q for hooks", dir)
 | 
			
		||||
			sync <- err
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		logrus.Debugf("monitoring %q for hooks", dir)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sync <- nil
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case event := <-watcher.Events:
 | 
			
		||||
			m.hooks = make(map[string]*current.Hook)
 | 
			
		||||
			for _, dir := range m.directories {
 | 
			
		||||
				err = ReadDir(dir, m.extensionStages, m.hooks)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					logrus.Errorf("Failed loading hooks for %s: %v", event.Name, err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case <-ctx.Done():
 | 
			
		||||
			err = ctx.Err()
 | 
			
		||||
			logrus.Debugf("hook monitoring canceled: %v", err)
 | 
			
		||||
			sync <- err
 | 
			
		||||
			close(sync)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										101
									
								
								vendor/github.com/containers/common/pkg/hooks/read.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										101
									
								
								vendor/github.com/containers/common/pkg/hooks/read.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,101 @@
 | 
			
		||||
// Package hooks implements CRI-O's hook handling.
 | 
			
		||||
package hooks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	old "github.com/containers/common/pkg/hooks/0.1.0"
 | 
			
		||||
	current "github.com/containers/common/pkg/hooks/1.0.0"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type reader func(content []byte) (*current.Hook, error)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ErrNoJSONSuffix represents hook-add attempts where the filename
 | 
			
		||||
	// does not end in '.json'.
 | 
			
		||||
	ErrNoJSONSuffix = errors.New("hook filename does not end in '.json'")
 | 
			
		||||
 | 
			
		||||
	// Readers registers per-version hook readers.
 | 
			
		||||
	Readers = map[string]reader{}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Read reads a hook JSON file, verifies it, and returns the hook configuration.
 | 
			
		||||
func Read(path string, extensionStages []string) (*current.Hook, error) {
 | 
			
		||||
	if !strings.HasSuffix(path, ".json") {
 | 
			
		||||
		return nil, ErrNoJSONSuffix
 | 
			
		||||
	}
 | 
			
		||||
	content, err := ioutil.ReadFile(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	hook, err := read(content)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("parsing hook %q: %w", path, err)
 | 
			
		||||
	}
 | 
			
		||||
	err = hook.Validate(extensionStages)
 | 
			
		||||
	return hook, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func read(content []byte) (hook *current.Hook, err error) {
 | 
			
		||||
	var ver version
 | 
			
		||||
	if err := json.Unmarshal(content, &ver); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("version check: %w", err)
 | 
			
		||||
	}
 | 
			
		||||
	reader, ok := Readers[ver.Version]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, fmt.Errorf("unrecognized hook version: %q", ver.Version)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hook, err = reader(content)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return hook, fmt.Errorf("%s: %v", ver.Version, err)
 | 
			
		||||
	}
 | 
			
		||||
	return hook, err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadDir reads hook JSON files from a directory into the given map,
 | 
			
		||||
// clobbering any previous entries with the same filenames.
 | 
			
		||||
func ReadDir(path string, extensionStages []string, hooks map[string]*current.Hook) error {
 | 
			
		||||
	logrus.Debugf("reading hooks from %s", path)
 | 
			
		||||
	files, err := ioutil.ReadDir(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	res := err
 | 
			
		||||
	for _, file := range files {
 | 
			
		||||
		filePath := filepath.Join(path, file.Name())
 | 
			
		||||
		hook, err := Read(filePath, extensionStages)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err == ErrNoJSONSuffix {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			if errors.Is(err, os.ErrNotExist) {
 | 
			
		||||
				if err2, ok := err.(*os.PathError); ok && err2.Path == filePath {
 | 
			
		||||
					continue
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			if res == nil {
 | 
			
		||||
				res = err
 | 
			
		||||
			} else {
 | 
			
		||||
				res = fmt.Errorf("%v: %w", err, res)
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		hooks[file.Name()] = hook
 | 
			
		||||
		logrus.Debugf("added hook %s", filePath)
 | 
			
		||||
	}
 | 
			
		||||
	return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	Readers[current.Version] = current.Read
 | 
			
		||||
	Readers[old.Version] = old.Read
 | 
			
		||||
	Readers[""] = old.Read
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6
									
								
								vendor/github.com/containers/common/pkg/hooks/version.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								vendor/github.com/containers/common/pkg/hooks/version.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
			
		||||
package hooks
 | 
			
		||||
 | 
			
		||||
// version a structure for checking the version of a hook configuration.
 | 
			
		||||
type version struct {
 | 
			
		||||
	Version string `json:"version"`
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user