github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/uniter/hook/hook.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package hook
     5  
     6  import (
     7  	"github.com/juju/charm/v12/hooks"
     8  	"github.com/juju/errors"
     9  	"github.com/juju/names/v5"
    10  
    11  	"github.com/juju/juju/core/secrets"
    12  )
    13  
    14  // Info holds details required to execute a hook. Not all fields are
    15  // relevant to all Kind values.
    16  type Info struct {
    17  	Kind hooks.Kind `yaml:"kind"`
    18  
    19  	// RelationId identifies the relation associated with the hook.
    20  	// It is only set when Kind indicates a relation hook.
    21  	RelationId int `yaml:"relation-id,omitempty"`
    22  
    23  	// RemoteUnit is the name of the unit that triggered the hook.
    24  	// It is only set when Kind indicates a relation hook other than
    25  	// relation-created or relation-broken.
    26  	RemoteUnit string `yaml:"remote-unit,omitempty"`
    27  
    28  	// RemoteApplication is always set if either an app or a unit triggers
    29  	// the hook. If the app triggers the hook, then RemoteUnit will be empty.
    30  	RemoteApplication string `yaml:"remote-application,omitempty"`
    31  
    32  	// ChangeVersion identifies the most recent unit settings change
    33  	// associated with RemoteUnit. It is only set when RemoteUnit is set.
    34  	ChangeVersion int64 `yaml:"change-version,omitempty"`
    35  
    36  	// StorageId is the ID of the storage instance relevant to the hook.
    37  	StorageId string `yaml:"storage-id,omitempty"`
    38  
    39  	// DepartingUnit is the name of the unit that goes away. It is only set
    40  	// when Kind indicates a relation-departed hook.
    41  	DepartingUnit string `yaml:"departee,omitempty"`
    42  
    43  	// WorkloadName is the name of the sidecar container or workload relevant to the hook.
    44  	WorkloadName string `yaml:"workload-name,omitempty"`
    45  
    46  	// NoticeID is the Pebble notice ID associated with the hook.
    47  	NoticeID string `yaml:"notice-id,omitempty"`
    48  
    49  	// NoticeType is the Pebble notice type associated with the hook.
    50  	NoticeType string `yaml:"notice-type,omitempty"`
    51  
    52  	// NoticeKey is the Pebble notice key associated with the hook.
    53  	NoticeKey string `yaml:"notice-key,omitempty"`
    54  
    55  	// MachineUpgradeTarget is the base that the unit's machine is to be
    56  	// updated to when Juju is issued the `upgrade-machine` command.
    57  	// It is only set for the pre-series-upgrade hook.
    58  	MachineUpgradeTarget string `yaml:"series-upgrade-target,omitempty"`
    59  
    60  	// SecretURI is the secret URI relevant to the hook.
    61  	SecretURI string `yaml:"secret-uri,omitempty"`
    62  
    63  	// SecretRevision is the secret revision relevant to the hook.
    64  	SecretRevision int `yaml:"secret-revision,omitempty"`
    65  
    66  	// SecretLabel is the secret label to expose to the hook.
    67  	SecretLabel string `yaml:"secret-label,omitempty"`
    68  }
    69  
    70  // SecretHookRequiresRevision returns true if the hook context needs a secret revision.
    71  func SecretHookRequiresRevision(kind hooks.Kind) bool {
    72  	return kind == hooks.SecretRemove || kind == hooks.SecretExpired
    73  }
    74  
    75  // Validate returns an error if the info is not valid.
    76  func (hi Info) Validate() error {
    77  	switch hi.Kind {
    78  	case hooks.RelationChanged:
    79  		if hi.RemoteUnit == "" {
    80  			if hi.RemoteApplication == "" {
    81  				return errors.Errorf("%q hook requires a remote unit or application", hi.Kind)
    82  			}
    83  		} else if hi.RemoteApplication == "" {
    84  			return errors.Errorf("%q hook has a remote unit but no application", hi.Kind)
    85  		}
    86  		return nil
    87  	case hooks.RelationJoined, hooks.RelationDeparted:
    88  		if hi.RemoteUnit == "" {
    89  			return errors.Errorf("%q hook requires a remote unit", hi.Kind)
    90  		}
    91  		if hi.RemoteApplication == "" {
    92  			return errors.Errorf("%q hook has a remote unit but no application", hi.Kind)
    93  		}
    94  		return nil
    95  	case hooks.PebbleCustomNotice:
    96  		if hi.WorkloadName == "" {
    97  			return errors.Errorf("%q hook requires a workload name", hi.Kind)
    98  		}
    99  		if hi.NoticeID == "" || hi.NoticeType == "" || hi.NoticeKey == "" {
   100  			return errors.Errorf("%q hook requires a notice ID, type, and key", hi.Kind)
   101  		}
   102  		return nil
   103  	case hooks.PebbleReady:
   104  		if hi.WorkloadName == "" {
   105  			return errors.Errorf("%q hook requires a workload name", hi.Kind)
   106  		}
   107  		return nil
   108  	case hooks.PreSeriesUpgrade:
   109  		if hi.MachineUpgradeTarget == "" {
   110  			return errors.Errorf("%q hook requires a target base", hi.Kind)
   111  		}
   112  		return nil
   113  	case hooks.Install, hooks.Remove, hooks.Start, hooks.ConfigChanged, hooks.UpgradeCharm, hooks.Stop,
   114  		hooks.RelationCreated, hooks.RelationBroken, hooks.CollectMetrics, hooks.MeterStatusChanged, hooks.UpdateStatus,
   115  		hooks.PostSeriesUpgrade:
   116  		return nil
   117  	case hooks.Action:
   118  		return errors.Errorf("hooks.Kind Action is deprecated")
   119  	case hooks.StorageAttached, hooks.StorageDetaching:
   120  		if !names.IsValidStorage(hi.StorageId) {
   121  			return errors.Errorf("invalid storage ID %q", hi.StorageId)
   122  		}
   123  		return nil
   124  	case hooks.LeaderElected, hooks.LeaderDeposed, hooks.LeaderSettingsChanged:
   125  		return nil
   126  	case hooks.SecretRotate, hooks.SecretChanged, hooks.SecretExpired, hooks.SecretRemove:
   127  		if hi.SecretURI == "" {
   128  			return errors.Errorf("%q hook requires a secret URI", hi.Kind)
   129  		}
   130  		if _, err := secrets.ParseURI(hi.SecretURI); err != nil {
   131  			return errors.Errorf("invalid secret URI %q", hi.SecretURI)
   132  		}
   133  		if SecretHookRequiresRevision(hi.Kind) && hi.SecretRevision <= 0 {
   134  			return errors.Errorf("%q hook requires a secret revision", hi.Kind)
   135  		}
   136  		return nil
   137  	}
   138  	return errors.Errorf("unknown hook kind %q", hi.Kind)
   139  }
   140  
   141  // Committer is an interface that may be used to convey the fact that the
   142  // specified hook has been successfully executed, and committed.
   143  type Committer interface {
   144  	CommitHook(Info) error
   145  }
   146  
   147  // Validator is an interface that may be used to validate a hook execution
   148  // request prior to executing it.
   149  type Validator interface {
   150  	ValidateHook(Info) error
   151  }