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

     1  // Copyright 2012, 2013, 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package jujuc
     5  
     6  import (
     7  	"fmt"
     8  	"strconv"
     9  	"strings"
    10  	"time"
    11  
    12  	"github.com/juju/charm/v12"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/loggo"
    15  	"github.com/juju/names/v5"
    16  
    17  	"github.com/juju/juju/core/application"
    18  	"github.com/juju/juju/core/life"
    19  	"github.com/juju/juju/core/network"
    20  	"github.com/juju/juju/core/payloads"
    21  	"github.com/juju/juju/core/relation"
    22  	"github.com/juju/juju/core/secrets"
    23  	"github.com/juju/juju/rpc/params"
    24  	"github.com/juju/juju/storage"
    25  )
    26  
    27  // Context is the interface that all hook helper commands
    28  // depend on to interact with the rest of the system.
    29  //
    30  //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/context_mock.go github.com/juju/juju/worker/uniter/runner/jujuc Context
    31  type Context interface {
    32  	HookContext
    33  	relationHookContext
    34  	actionHookContext
    35  	unitCharmStateContext
    36  	workloadHookContext
    37  }
    38  
    39  // HookContext represents the information and functionality that is
    40  // common to all charm hooks.
    41  type HookContext interface {
    42  	ContextUnit
    43  	ContextStatus
    44  	ContextInstance
    45  	ContextNetworking
    46  	ContextLeadership
    47  	ContextMetrics
    48  	ContextStorage
    49  	ContextResources
    50  	ContextPayloads
    51  	ContextRelations
    52  	ContextVersion
    53  	ContextSecrets
    54  
    55  	// GetLogger returns a juju loggo Logger for the supplied module that is
    56  	// correctly wired up for the given context
    57  	GetLogger(module string) loggo.Logger
    58  }
    59  
    60  // UnitHookContext is the context for a unit hook.
    61  type UnitHookContext interface {
    62  	HookContext
    63  }
    64  
    65  // RelationHookContext is the context for a relation hook.
    66  type RelationHookContext interface {
    67  	HookContext
    68  	relationHookContext
    69  }
    70  
    71  type relationHookContext interface {
    72  	// HookRelation returns the ContextRelation associated with the executing
    73  	// hook if it was found, or an error if it was not found (or is not available).
    74  	HookRelation() (ContextRelation, error)
    75  
    76  	// RemoteUnitName returns the name of the remote unit the hook execution
    77  	// is associated with if it was found, and an error if it was not found or is not
    78  	// available.
    79  	RemoteUnitName() (string, error)
    80  
    81  	// RemoteApplicationName returns the name of the remote application the hook execution
    82  	// is associated with if it was found, and an error if it was not found or is not
    83  	// available.
    84  	RemoteApplicationName() (string, error)
    85  }
    86  
    87  // ActionHookContext is the context for an action hook.
    88  type ActionHookContext interface {
    89  	HookContext
    90  	actionHookContext
    91  }
    92  
    93  type actionHookContext interface {
    94  	// ActionParams returns the map of params passed with an Action.
    95  	ActionParams() (map[string]interface{}, error)
    96  
    97  	// UpdateActionResults inserts new values for use with action-set.
    98  	// The results struct will be delivered to the controller upon
    99  	// completion of the Action.
   100  	UpdateActionResults(keys []string, value interface{}) error
   101  
   102  	// SetActionMessage sets a message for the Action.
   103  	SetActionMessage(string) error
   104  
   105  	// SetActionFailed sets a failure state for the Action.
   106  	SetActionFailed() error
   107  
   108  	// LogActionMessage records a progress message for the Action.
   109  	LogActionMessage(string) error
   110  }
   111  
   112  // WorkloadHookContext is the context for a workload hook.
   113  type WorkloadHookContext interface {
   114  	HookContext
   115  	workloadHookContext
   116  }
   117  
   118  type workloadHookContext interface {
   119  	// WorkloadName returns the name of the container/workload for workload hooks.
   120  	WorkloadName() (string, error)
   121  }
   122  
   123  // unitCharmStateContext provides helper for interacting with the charm state
   124  // that is stored within the context.
   125  type unitCharmStateContext interface {
   126  	// GetCharmState returns a copy of the charm state.
   127  	GetCharmState() (map[string]string, error)
   128  
   129  	// GetCharmStateValue returns the value of the given key.
   130  	GetCharmStateValue(string) (string, error)
   131  
   132  	// DeleteCharmStateValue deletes the key/value pair for the given key.
   133  	DeleteCharmStateValue(string) error
   134  
   135  	// SetCharmStateValue sets the key to the specified value.
   136  	SetCharmStateValue(string, string) error
   137  }
   138  
   139  // ContextUnit is the part of a hook context related to the unit.
   140  type ContextUnit interface {
   141  	// UnitName returns the executing unit's name.
   142  	UnitName() string
   143  
   144  	// ConfigSettings returns the current application
   145  	// configuration of the executing unit.
   146  	ConfigSettings() (charm.Settings, error)
   147  
   148  	// GoalState returns the goal state for the current unit.
   149  	GoalState() (*application.GoalState, error)
   150  
   151  	// SetPodSpec updates the yaml spec used to create a pod.
   152  	// TODO(wallyworld) - rename to SetK8sSpec (here and elsewhere)
   153  	SetPodSpec(specYaml string) error
   154  
   155  	// GetPodSpec returns the yaml spec used to create a pod.
   156  	// TODO(wallyworld) - rename to GetK8sSpec (here and elsewhere)
   157  	GetPodSpec() (string, error)
   158  
   159  	// SetRawK8sSpec updates the raw yaml spec used to create a pod.
   160  	SetRawK8sSpec(specYaml string) error
   161  
   162  	// GetRawK8sSpec returns the raw yaml spec used to create a pod.
   163  	GetRawK8sSpec() (string, error)
   164  
   165  	// CloudSpec returns the unit's cloud specification
   166  	CloudSpec() (*params.CloudSpec, error)
   167  }
   168  
   169  // SecretCreateArgs specifies args used to create a secret.
   170  // Nil values are not included in the create.
   171  type SecretCreateArgs struct {
   172  	SecretUpdateArgs
   173  
   174  	OwnerTag names.Tag
   175  }
   176  
   177  // SecretUpdateArgs specifies args used to update a secret.
   178  // Nil values are not included in the update.
   179  type SecretUpdateArgs struct {
   180  	// Value is the new secret value or nil to not update.
   181  	Value secrets.SecretValue
   182  
   183  	RotatePolicy *secrets.RotatePolicy
   184  	ExpireTime   *time.Time
   185  
   186  	Description *string
   187  	Label       *string
   188  }
   189  
   190  // SecretGrantRevokeArgs specify the args used to grant or revoke access to a secret.
   191  type SecretGrantRevokeArgs struct {
   192  	ApplicationName *string
   193  	UnitName        *string
   194  	RelationKey     *string
   195  	Role            *secrets.SecretRole
   196  }
   197  
   198  // SecretMetadata holds a secret's metadata.
   199  type SecretMetadata struct {
   200  	Owner            names.Tag
   201  	Description      string
   202  	Label            string
   203  	RotatePolicy     secrets.RotatePolicy
   204  	LatestRevision   int
   205  	LatestExpireTime *time.Time
   206  	NextRotateTime   *time.Time
   207  	Revisions        []int
   208  	Access           []secrets.AccessInfo
   209  }
   210  
   211  // ContextSecrets is the part of a hook context related to secrets.
   212  type ContextSecrets interface {
   213  	// GetSecret returns the value of the specified secret.
   214  	GetSecret(*secrets.URI, string, bool, bool) (secrets.SecretValue, error)
   215  
   216  	// CreateSecret creates a secret with the specified data.
   217  	CreateSecret(*SecretCreateArgs) (*secrets.URI, error)
   218  
   219  	// UpdateSecret creates a secret with the specified data.
   220  	UpdateSecret(*secrets.URI, *SecretUpdateArgs) error
   221  
   222  	// RemoveSecret removes a secret with the specified uri.
   223  	RemoveSecret(*secrets.URI, *int) error
   224  
   225  	// GrantSecret grants access to the specified secret.
   226  	GrantSecret(*secrets.URI, *SecretGrantRevokeArgs) error
   227  
   228  	// RevokeSecret revokes access to the specified secret.
   229  	RevokeSecret(*secrets.URI, *SecretGrantRevokeArgs) error
   230  
   231  	// SecretMetadata gets the secret metadata for secrets created by the charm.
   232  	SecretMetadata() (map[string]SecretMetadata, error)
   233  }
   234  
   235  // ContextStatus is the part of a hook context related to the unit's status.
   236  type ContextStatus interface {
   237  	// UnitStatus returns the executing unit's current status.
   238  	UnitStatus() (*StatusInfo, error)
   239  
   240  	// SetUnitStatus updates the unit's status.
   241  	SetUnitStatus(StatusInfo) error
   242  
   243  	// ApplicationStatus returns the executing unit's application status
   244  	// (including all units).
   245  	ApplicationStatus() (ApplicationStatusInfo, error)
   246  
   247  	// SetApplicationStatus updates the status for the unit's application.
   248  	SetApplicationStatus(StatusInfo) error
   249  }
   250  
   251  // RebootPriority is the type used for reboot requests.
   252  type RebootPriority int
   253  
   254  // ContextInstance is the part of a hook context related to the unit's instance.
   255  type ContextInstance interface {
   256  	// AvailabilityZone returns the executing unit's availability zone or an error
   257  	// if it was not found (or is not available).
   258  	AvailabilityZone() (string, error)
   259  
   260  	// RequestReboot will set the reboot flag to true on the machine agent
   261  	RequestReboot(prio RebootPriority) error
   262  }
   263  
   264  // ContextNetworking is the part of a hook context related to network
   265  // interface of the unit's instance.
   266  type ContextNetworking interface {
   267  	// PublicAddress returns the executing unit's public address or an
   268  	// error if it is not available.
   269  	PublicAddress() (string, error)
   270  
   271  	// PrivateAddress returns the executing unit's private address or an
   272  	// error if it is not available.
   273  	PrivateAddress() (string, error)
   274  
   275  	// OpenPortRange marks the supplied port range for opening.
   276  	OpenPortRange(endpointName string, portRange network.PortRange) error
   277  
   278  	// ClosePortRange ensures the supplied port range is closed even when
   279  	// the executing unit's application is exposed (unless it is opened
   280  	// separately by a co-located unit).
   281  	ClosePortRange(endpointName string, portRange network.PortRange) error
   282  
   283  	// OpenedPortRanges returns all port ranges currently opened by this
   284  	// unit on its assigned machine grouped by endpoint name.
   285  	OpenedPortRanges() network.GroupedPortRanges
   286  
   287  	// NetworkInfo returns the network info for the given bindings on the given relation.
   288  	NetworkInfo(bindingNames []string, relationId int) (map[string]params.NetworkInfoResult, error)
   289  }
   290  
   291  // ContextLeadership is the part of a hook context related to the
   292  // unit leadership.
   293  type ContextLeadership interface {
   294  	// IsLeader returns true if the local unit is known to be leader for at
   295  	// least the next 30s.
   296  	IsLeader() (bool, error)
   297  
   298  	// LeaderSettings returns the current leader settings. Once leader settings
   299  	// have been read in a given context, they will not be updated other than
   300  	// via successful calls to WriteLeaderSettings.
   301  	LeaderSettings() (map[string]string, error)
   302  
   303  	// WriteLeaderSettings writes the supplied settings directly to state, or
   304  	// fails if the local unit is not the application's leader.
   305  	WriteLeaderSettings(map[string]string) error
   306  }
   307  
   308  // ContextMetrics is the part of a hook context related to metrics.
   309  type ContextMetrics interface {
   310  	// AddMetric records a metric to return after hook execution.
   311  	AddMetric(string, string, time.Time) error
   312  	// AddMetricLabels records a metric with tags to return after hook execution.
   313  	AddMetricLabels(string, string, time.Time, map[string]string) error
   314  }
   315  
   316  // ContextStorage is the part of a hook context related to storage
   317  // resources associated with the unit.
   318  type ContextStorage interface {
   319  	// StorageTags returns a list of tags for storage instances
   320  	// attached to the unit or an error if they are not available.
   321  	StorageTags() ([]names.StorageTag, error)
   322  
   323  	// Storage returns the ContextStorageAttachment with the supplied
   324  	// tag if it was found, and an error if it was not found or is not
   325  	// available to the context.
   326  	Storage(names.StorageTag) (ContextStorageAttachment, error)
   327  
   328  	// HookStorage returns the storage attachment associated
   329  	// the executing hook if it was found, and an error if it
   330  	// was not found or is not available.
   331  	HookStorage() (ContextStorageAttachment, error)
   332  
   333  	// AddUnitStorage saves storage constraints in the context.
   334  	AddUnitStorage(map[string]params.StorageConstraints) error
   335  }
   336  
   337  // ContextResources exposes the functionality needed by the
   338  // "resource-*" hook commands.
   339  type ContextResources interface {
   340  	// DownloadResource downloads the named resource and returns
   341  	// the path to which it was downloaded.
   342  	DownloadResource(name string) (filePath string, _ error)
   343  }
   344  
   345  // ContextPayloads exposes the functionality needed by the
   346  // "payload-*" hook commands.
   347  type ContextPayloads interface {
   348  	// GetPayload returns the payload info corresponding to the given ID.
   349  	GetPayload(class, id string) (*payloads.Payload, error)
   350  	// TrackPayload records the payload info in the hook context.
   351  	TrackPayload(payload payloads.Payload) error
   352  	// UntrackPayload removes the payload from our list of payloads to track.
   353  	UntrackPayload(class, id string) error
   354  	// SetPayloadStatus sets the status of the payload.
   355  	SetPayloadStatus(class, id, status string) error
   356  	// ListPayloads returns the list of registered payload IDs.
   357  	ListPayloads() ([]string, error)
   358  	// FlushPayloads pushes the hook context data out to state.
   359  	FlushPayloads() error
   360  }
   361  
   362  // ContextRelations exposes the relations associated with the unit.
   363  type ContextRelations interface {
   364  	// Relation returns the relation with the supplied id if it was found, and
   365  	// an error if it was not found or is not available.
   366  	Relation(id int) (ContextRelation, error)
   367  
   368  	// RelationIds returns the ids of all relations the executing unit is
   369  	// currently participating in or an error if they are not available.
   370  	RelationIds() ([]int, error)
   371  }
   372  
   373  // ContextRelation expresses the capabilities of a hook with respect to a relation.
   374  //
   375  //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/context_relation_mock.go github.com/juju/juju/worker/uniter/runner/jujuc ContextRelation
   376  type ContextRelation interface {
   377  
   378  	// Id returns an integer which uniquely identifies the relation.
   379  	Id() int
   380  
   381  	// Name returns the name the locally executing charm assigned to this relation.
   382  	Name() string
   383  
   384  	// RelationTag returns the relation tag.
   385  	RelationTag() names.RelationTag
   386  
   387  	// FakeId returns a string of the form "relation-name:123", which uniquely
   388  	// identifies the relation to the hook. In reality, the identification
   389  	// of the relation is the integer following the colon, but the composed
   390  	// name is useful to humans observing it.
   391  	FakeId() string
   392  
   393  	// Settings allows read/write access to the local unit's settings in
   394  	// this relation.
   395  	Settings() (Settings, error)
   396  
   397  	// ApplicationSettings allows read/write access to the application settings in
   398  	// this relation, but only if the current unit is leader.
   399  	ApplicationSettings() (Settings, error)
   400  
   401  	// UnitNames returns a list of the remote units in the relation.
   402  	UnitNames() []string
   403  
   404  	// ReadSettings returns the settings of any remote unit in the relation.
   405  	ReadSettings(unit string) (params.Settings, error)
   406  
   407  	// ReadApplicationSettings returns the application settings of any remote unit in the relation.
   408  	ReadApplicationSettings(app string) (params.Settings, error)
   409  
   410  	// Suspended returns true if the relation is suspended.
   411  	Suspended() bool
   412  
   413  	// SetStatus sets the relation's status.
   414  	SetStatus(relation.Status) error
   415  
   416  	// RemoteApplicationName returns the application on the other end of
   417  	// the relation from the perspective of this unit.
   418  	RemoteApplicationName() string
   419  
   420  	// Life returns the relation's current life state.
   421  	Life() life.Value
   422  }
   423  
   424  // ContextStorageAttachment expresses the capabilities of a hook with
   425  // respect to a storage attachment.
   426  type ContextStorageAttachment interface {
   427  
   428  	// Tag returns a tag which uniquely identifies the storage attachment
   429  	// in the context of the unit.
   430  	Tag() names.StorageTag
   431  
   432  	// Kind returns the kind of the storage.
   433  	Kind() storage.StorageKind
   434  
   435  	// Location returns the location of the storage: the mount point for
   436  	// filesystem-kind stores, and the device path for block-kind stores.
   437  	Location() string
   438  }
   439  
   440  // ContextVersion expresses the parts of a hook context related to
   441  // reporting workload versions.
   442  type ContextVersion interface {
   443  
   444  	// UnitWorkloadVersion returns the currently set workload version for
   445  	// the unit.
   446  	UnitWorkloadVersion() (string, error)
   447  
   448  	// SetUnitWorkloadVersion updates the workload version for the unit.
   449  	SetUnitWorkloadVersion(string) error
   450  }
   451  
   452  // Settings is implemented by types that manipulate unit settings.
   453  type Settings interface {
   454  	Map() params.Settings
   455  	Set(string, string)
   456  	Delete(string)
   457  }
   458  
   459  // NewRelationIdValue returns a gnuflag.Value for convenient parsing of relation
   460  // ids in ctx.
   461  func NewRelationIdValue(ctx Context, result *int) (*relationIdValue, error) {
   462  	v := &relationIdValue{result: result, ctx: ctx}
   463  	id := -1
   464  	if r, err := ctx.HookRelation(); err == nil {
   465  		id = r.Id()
   466  		v.value = r.FakeId()
   467  	} else if !errors.IsNotFound(err) {
   468  		return nil, errors.Trace(err)
   469  	}
   470  	*result = id
   471  	return v, nil
   472  }
   473  
   474  // relationIdValue implements gnuflag.Value for use in relation commands.
   475  type relationIdValue struct {
   476  	result *int
   477  	ctx    Context
   478  	value  string
   479  }
   480  
   481  // String returns the current value.
   482  func (v *relationIdValue) String() string {
   483  	return v.value
   484  }
   485  
   486  // Set interprets value as a relation id, if possible, and returns an error
   487  // if it is not known to the system. The parsed relation id will be written
   488  // to v.result.
   489  func (v *relationIdValue) Set(value string) error {
   490  	trim := value
   491  	if idx := strings.LastIndex(trim, ":"); idx != -1 {
   492  		trim = trim[idx+1:]
   493  	}
   494  	id, err := strconv.Atoi(trim)
   495  	if err != nil {
   496  		return fmt.Errorf("invalid relation id")
   497  	}
   498  	if _, err := v.ctx.Relation(id); err != nil {
   499  		return errors.Trace(err)
   500  	}
   501  	*v.result = id
   502  	v.value = value
   503  	return nil
   504  }