github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/pkg/hooks/1.0.0/hook.go (about)

     1  // Package hook is the 1.0.0 hook configuration structure.
     2  package hook
     3  
     4  import (
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"regexp"
     9  
    10  	rspec "github.com/opencontainers/runtime-spec/specs-go"
    11  	"github.com/pkg/errors"
    12  )
    13  
    14  // Version is the hook configuration version defined in this package.
    15  const Version = "1.0.0"
    16  
    17  // Hook is the hook configuration structure.
    18  type Hook struct {
    19  	Version string     `json:"version"`
    20  	Hook    rspec.Hook `json:"hook"`
    21  	When    When       `json:"when"`
    22  	Stages  []string   `json:"stages"`
    23  }
    24  
    25  // Read reads hook JSON bytes, verifies them, and returns the hook configuration.
    26  func Read(content []byte) (hook *Hook, err error) {
    27  	if err = json.Unmarshal(content, &hook); err != nil {
    28  		return nil, err
    29  	}
    30  	return hook, nil
    31  }
    32  
    33  // Validate performs load-time hook validation.
    34  func (hook *Hook) Validate(extensionStages []string) (err error) {
    35  	if hook == nil {
    36  		return errors.New("nil hook")
    37  	}
    38  
    39  	if hook.Version != Version {
    40  		return fmt.Errorf("unexpected hook version %q (expecting %v)", hook.Version, Version)
    41  	}
    42  
    43  	if hook.Hook.Path == "" {
    44  		return errors.New("missing required property: hook.path")
    45  	}
    46  
    47  	if _, err := os.Stat(hook.Hook.Path); err != nil {
    48  		return err
    49  	}
    50  
    51  	for key, value := range hook.When.Annotations {
    52  		if _, err = regexp.Compile(key); err != nil {
    53  			return errors.Wrapf(err, "invalid annotation key %q", key)
    54  		}
    55  		if _, err = regexp.Compile(value); err != nil {
    56  			return errors.Wrapf(err, "invalid annotation value %q", value)
    57  		}
    58  	}
    59  
    60  	for _, command := range hook.When.Commands {
    61  		if _, err = regexp.Compile(command); err != nil {
    62  			return errors.Wrapf(err, "invalid command %q", command)
    63  		}
    64  	}
    65  
    66  	if hook.Stages == nil {
    67  		return errors.New("missing required property: stages")
    68  	}
    69  
    70  	validStages := map[string]bool{"prestart": true, "poststart": true, "poststop": true}
    71  	for _, stage := range extensionStages {
    72  		validStages[stage] = true
    73  	}
    74  
    75  	for _, stage := range hook.Stages {
    76  		if !validStages[stage] {
    77  			return fmt.Errorf("unknown stage %q", stage)
    78  		}
    79  	}
    80  
    81  	return nil
    82  }