github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/uniter/operation/util_test.go (about)

     1  // Copyright 2014-2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package operation_test
     5  
     6  import (
     7  	"sync"
     8  
     9  	"github.com/juju/charm/v12/hooks"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names/v5"
    12  	"github.com/juju/testing"
    13  	utilexec "github.com/juju/utils/v3/exec"
    14  
    15  	"github.com/juju/juju/api/agent/uniter"
    16  	"github.com/juju/juju/core/model"
    17  	"github.com/juju/juju/core/relation"
    18  	"github.com/juju/juju/core/secrets"
    19  	"github.com/juju/juju/worker/uniter/charm"
    20  	"github.com/juju/juju/worker/uniter/hook"
    21  	"github.com/juju/juju/worker/uniter/operation"
    22  	"github.com/juju/juju/worker/uniter/remotestate"
    23  	"github.com/juju/juju/worker/uniter/runner"
    24  	runnercontext "github.com/juju/juju/worker/uniter/runner/context"
    25  	"github.com/juju/juju/worker/uniter/runner/jujuc"
    26  )
    27  
    28  type MockGetArchiveInfo struct {
    29  	gotCharmURL string
    30  	info        charm.BundleInfo
    31  	err         error
    32  }
    33  
    34  func (mock *MockGetArchiveInfo) Call(charmURL string) (charm.BundleInfo, error) {
    35  	mock.gotCharmURL = charmURL
    36  	return mock.info, mock.err
    37  }
    38  
    39  type MockSetCurrentCharm struct {
    40  	gotCharmURL string
    41  	err         error
    42  }
    43  
    44  func (mock *MockSetCurrentCharm) Call(charmURL string) error {
    45  	mock.gotCharmURL = charmURL
    46  	return mock.err
    47  }
    48  
    49  type DeployCallbacks struct {
    50  	operation.Callbacks
    51  	*MockGetArchiveInfo
    52  	*MockSetCurrentCharm
    53  	MockInitializeMetricsTimers *MockNoArgs
    54  }
    55  
    56  func (cb *DeployCallbacks) GetArchiveInfo(charmURL string) (charm.BundleInfo, error) {
    57  	return cb.MockGetArchiveInfo.Call(charmURL)
    58  }
    59  
    60  func (cb *DeployCallbacks) SetCurrentCharm(charmURL string) error {
    61  	return cb.MockSetCurrentCharm.Call(charmURL)
    62  }
    63  
    64  func (cb *DeployCallbacks) InitializeMetricsTimers() error {
    65  	return cb.MockInitializeMetricsTimers.Call()
    66  }
    67  
    68  type MockBundleInfo struct {
    69  	charm.BundleInfo
    70  }
    71  
    72  type MockStage struct {
    73  	gotInfo  *charm.BundleInfo
    74  	gotAbort *<-chan struct{}
    75  	err      error
    76  }
    77  
    78  func (mock *MockStage) Call(info charm.BundleInfo, abort <-chan struct{}) error {
    79  	mock.gotInfo = &info
    80  	mock.gotAbort = &abort
    81  	return mock.err
    82  }
    83  
    84  type MockNoArgs struct {
    85  	called bool
    86  	err    error
    87  }
    88  
    89  func (mock *MockNoArgs) Call() error {
    90  	mock.called = true
    91  	return mock.err
    92  }
    93  
    94  type MockDeployer struct {
    95  	charm.Deployer
    96  	*MockStage
    97  	MockDeploy         *MockNoArgs
    98  	MockNotifyRevert   *MockNoArgs
    99  	MockNotifyResolved *MockNoArgs
   100  }
   101  
   102  func (d *MockDeployer) Stage(info charm.BundleInfo, abort <-chan struct{}) error {
   103  	return d.MockStage.Call(info, abort)
   104  }
   105  
   106  func (d *MockDeployer) Deploy() error {
   107  	return d.MockDeploy.Call()
   108  }
   109  
   110  type MockFailAction struct {
   111  	gotActionId *string
   112  	gotMessage  *string
   113  	err         error
   114  }
   115  
   116  func (mock *MockFailAction) Call(actionId, message string) error {
   117  	mock.gotActionId = &actionId
   118  	mock.gotMessage = &message
   119  	return mock.err
   120  }
   121  
   122  type RunActionCallbacks struct {
   123  	operation.Callbacks
   124  	*MockFailAction
   125  	executingMessage string
   126  	actionStatus     string
   127  	actionStatusErr  error
   128  	mut              sync.Mutex
   129  }
   130  
   131  func (cb *RunActionCallbacks) FailAction(actionId, message string) error {
   132  	return cb.MockFailAction.Call(actionId, message)
   133  }
   134  
   135  func (cb *RunActionCallbacks) SetExecutingStatus(message string) error {
   136  	cb.mut.Lock()
   137  	defer cb.mut.Unlock()
   138  	cb.executingMessage = message
   139  	return nil
   140  }
   141  
   142  func (cb *RunActionCallbacks) ActionStatus(actionId string) (string, error) {
   143  	cb.mut.Lock()
   144  	defer cb.mut.Unlock()
   145  	return cb.actionStatus, cb.actionStatusErr
   146  }
   147  
   148  func (cb *RunActionCallbacks) setActionStatus(status string, err error) {
   149  	cb.mut.Lock()
   150  	defer cb.mut.Unlock()
   151  	cb.actionStatus = status
   152  	cb.actionStatusErr = err
   153  }
   154  
   155  type RunCommandsCallbacks struct {
   156  	operation.Callbacks
   157  	executingMessage string
   158  }
   159  
   160  func (cb *RunCommandsCallbacks) SetExecutingStatus(message string) error {
   161  	cb.executingMessage = message
   162  	return nil
   163  }
   164  
   165  type MockPrepareHook struct {
   166  	gotHook *hook.Info
   167  	name    string
   168  	err     error
   169  }
   170  
   171  func (mock *MockPrepareHook) Call(hookInfo hook.Info) (string, error) {
   172  	mock.gotHook = &hookInfo
   173  	return mock.name, mock.err
   174  }
   175  
   176  type PrepareHookCallbacks struct {
   177  	operation.Callbacks
   178  	*MockPrepareHook
   179  	executingMessage string
   180  }
   181  
   182  func (cb *PrepareHookCallbacks) PrepareHook(hookInfo hook.Info) (string, error) {
   183  	return cb.MockPrepareHook.Call(hookInfo)
   184  }
   185  
   186  func (cb *PrepareHookCallbacks) SetExecutingStatus(message string) error {
   187  	cb.executingMessage = message
   188  	return nil
   189  }
   190  
   191  func (cb *PrepareHookCallbacks) SetUpgradeSeriesStatus(model.UpgradeSeriesStatus, string) error {
   192  	return nil
   193  }
   194  
   195  type MockNotify struct {
   196  	gotName    *string
   197  	gotContext *runnercontext.Context
   198  }
   199  
   200  func (mock *MockNotify) Call(hookName string, ctx runnercontext.Context) {
   201  	mock.gotName = &hookName
   202  	mock.gotContext = &ctx
   203  }
   204  
   205  type ExecuteHookCallbacks struct {
   206  	*PrepareHookCallbacks
   207  	MockNotifyHookCompleted *MockNotify
   208  	MockNotifyHookFailed    *MockNotify
   209  }
   210  
   211  func (cb *ExecuteHookCallbacks) NotifyHookCompleted(hookName string, ctx runnercontext.Context) {
   212  	cb.MockNotifyHookCompleted.Call(hookName, ctx)
   213  }
   214  
   215  func (cb *ExecuteHookCallbacks) NotifyHookFailed(hookName string, ctx runnercontext.Context) {
   216  	cb.MockNotifyHookFailed.Call(hookName, ctx)
   217  }
   218  
   219  type MockCommitHook struct {
   220  	gotHook *hook.Info
   221  	err     error
   222  }
   223  
   224  func (mock *MockCommitHook) Call(hookInfo hook.Info) error {
   225  	mock.gotHook = &hookInfo
   226  	return mock.err
   227  }
   228  
   229  type CommitHookCallbacks struct {
   230  	operation.Callbacks
   231  	*MockCommitHook
   232  
   233  	rotatedSecretURI   string
   234  	rotatedOldRevision int
   235  }
   236  
   237  func (cb *CommitHookCallbacks) PrepareHook(hookInfo hook.Info) (string, error) {
   238  	return "", nil
   239  }
   240  
   241  func (cb *CommitHookCallbacks) CommitHook(hookInfo hook.Info) error {
   242  	return cb.MockCommitHook.Call(hookInfo)
   243  }
   244  
   245  func (cb *CommitHookCallbacks) SetSecretRotated(url string, oldRevision int) error {
   246  	cb.rotatedSecretURI = url
   247  	cb.rotatedOldRevision = oldRevision
   248  	return nil
   249  }
   250  
   251  type MockNewActionRunner struct {
   252  	gotActionId *string
   253  	gotCancel   <-chan struct{}
   254  	runner      *MockRunner
   255  	err         error
   256  }
   257  
   258  func (mock *MockNewActionRunner) Call(actionId string, cancel <-chan struct{}) (runner.Runner, error) {
   259  	mock.gotActionId = &actionId
   260  	mock.gotCancel = cancel
   261  	return mock.runner, mock.err
   262  }
   263  
   264  type MockNewActionWaitRunner struct {
   265  	gotActionId *string
   266  	gotCancel   <-chan struct{}
   267  	runner      *MockActionWaitRunner
   268  	err         error
   269  }
   270  
   271  func (mock *MockNewActionWaitRunner) Call(actionId string, cancel <-chan struct{}) (runner.Runner, error) {
   272  	mock.gotActionId = &actionId
   273  	mock.gotCancel = cancel
   274  	mock.runner.context.(*MockContext).actionData.Cancel = cancel
   275  	return mock.runner, mock.err
   276  }
   277  
   278  type MockNewHookRunner struct {
   279  	gotHook *hook.Info
   280  	runner  *MockRunner
   281  	err     error
   282  }
   283  
   284  func (mock *MockNewHookRunner) Call(hookInfo hook.Info) (runner.Runner, error) {
   285  	mock.gotHook = &hookInfo
   286  	return mock.runner, mock.err
   287  }
   288  
   289  type MockNewCommandRunner struct {
   290  	gotInfo *runnercontext.CommandInfo
   291  	runner  *MockRunner
   292  	err     error
   293  }
   294  
   295  func (mock *MockNewCommandRunner) Call(info runnercontext.CommandInfo) (runner.Runner, error) {
   296  	mock.gotInfo = &info
   297  	return mock.runner, mock.err
   298  }
   299  
   300  type MockRunnerFactory struct {
   301  	*MockNewActionRunner
   302  	*MockNewHookRunner
   303  	*MockNewCommandRunner
   304  }
   305  
   306  func (f *MockRunnerFactory) NewActionRunner(action *uniter.Action, cancel <-chan struct{}) (runner.Runner, error) {
   307  	return f.MockNewActionRunner.Call(action.ID(), cancel)
   308  }
   309  
   310  func (f *MockRunnerFactory) NewHookRunner(hookInfo hook.Info) (runner.Runner, error) {
   311  	return f.MockNewHookRunner.Call(hookInfo)
   312  }
   313  
   314  func (f *MockRunnerFactory) NewCommandRunner(commandInfo runnercontext.CommandInfo) (runner.Runner, error) {
   315  	return f.MockNewCommandRunner.Call(commandInfo)
   316  }
   317  
   318  type MockRunnerActionWaitFactory struct {
   319  	runner.Factory
   320  	*MockNewActionWaitRunner
   321  }
   322  
   323  func (f *MockRunnerActionWaitFactory) NewActionRunner(action *uniter.Action, cancel <-chan struct{}) (runner.Runner, error) {
   324  	return f.MockNewActionWaitRunner.Call(action.ID(), cancel)
   325  }
   326  
   327  type MockContext struct {
   328  	runnercontext.Context
   329  	testing.Stub
   330  	actionData      *runnercontext.ActionData
   331  	setStatusCalled bool
   332  	status          jujuc.StatusInfo
   333  	isLeader        bool
   334  	relation        *MockRelation
   335  }
   336  
   337  func (mock *MockContext) SecretMetadata() (map[string]jujuc.SecretMetadata, error) {
   338  	return map[string]jujuc.SecretMetadata{
   339  		"9m4e2mr0ui3e8a215n4g": {
   340  			Description:    "description",
   341  			Label:          "label",
   342  			Owner:          names.NewApplicationTag("mariadb"),
   343  			RotatePolicy:   secrets.RotateHourly,
   344  			LatestRevision: 666,
   345  		},
   346  	}, nil
   347  }
   348  
   349  func (mock *MockContext) ActionData() (*runnercontext.ActionData, error) {
   350  	if mock.actionData == nil {
   351  		return nil, errors.New("not an action context")
   352  	}
   353  	return mock.actionData, nil
   354  }
   355  
   356  func (mock *MockContext) HasExecutionSetUnitStatus() bool {
   357  	return mock.setStatusCalled
   358  }
   359  
   360  func (mock *MockContext) ResetExecutionSetUnitStatus() {
   361  	mock.setStatusCalled = false
   362  }
   363  
   364  func (mock *MockContext) SetUnitStatus(status jujuc.StatusInfo) error {
   365  	mock.setStatusCalled = true
   366  	mock.status = status
   367  	return nil
   368  }
   369  
   370  func (mock *MockContext) UnitName() string {
   371  	return "unit/0"
   372  }
   373  
   374  func (mock *MockContext) UnitStatus() (*jujuc.StatusInfo, error) {
   375  	return &mock.status, nil
   376  }
   377  
   378  func (mock *MockContext) Prepare() error {
   379  	mock.MethodCall(mock, "Prepare")
   380  	return mock.NextErr()
   381  }
   382  
   383  func (mock *MockContext) IsLeader() (bool, error) {
   384  	return mock.isLeader, nil
   385  }
   386  
   387  func (mock *MockContext) Relation(id int) (jujuc.ContextRelation, error) {
   388  	return mock.relation, nil
   389  }
   390  
   391  type MockRelation struct {
   392  	jujuc.ContextRelation
   393  	suspended bool
   394  	status    relation.Status
   395  }
   396  
   397  func (mock *MockRelation) Suspended() bool {
   398  	return mock.suspended
   399  }
   400  
   401  func (mock *MockRelation) SetStatus(status relation.Status) error {
   402  	mock.status = status
   403  	return nil
   404  }
   405  
   406  type MockRunAction struct {
   407  	gotName *string
   408  	err     error
   409  }
   410  
   411  func (mock *MockRunAction) Call(actionName string) error {
   412  	mock.gotName = &actionName
   413  	return mock.err
   414  }
   415  
   416  type MockRunCommands struct {
   417  	gotCommands    *string
   418  	gotRunLocation *runner.RunLocation
   419  	response       *utilexec.ExecResponse
   420  	err            error
   421  }
   422  
   423  func (mock *MockRunCommands) Call(commands string, runLocation runner.RunLocation) (*utilexec.ExecResponse, error) {
   424  	mock.gotCommands = &commands
   425  	mock.gotRunLocation = &runLocation
   426  	return mock.response, mock.err
   427  }
   428  
   429  type MockRunHook struct {
   430  	gotName         *string
   431  	err             error
   432  	setStatusCalled bool
   433  }
   434  
   435  func (mock *MockRunHook) Call(hookName string) error {
   436  	mock.gotName = &hookName
   437  	return mock.err
   438  }
   439  
   440  type MockRunner struct {
   441  	*MockRunAction
   442  	*MockRunCommands
   443  	*MockRunHook
   444  	context runnercontext.Context
   445  }
   446  
   447  func (r *MockRunner) Context() runnercontext.Context {
   448  	return r.context
   449  }
   450  
   451  func (r *MockRunner) RunAction(actionName string) (runner.HookHandlerType, error) {
   452  	return runner.ExplicitHookHandler, r.MockRunAction.Call(actionName)
   453  }
   454  
   455  func (r *MockRunner) RunCommands(commands string, runLocation runner.RunLocation) (*utilexec.ExecResponse, error) {
   456  	return r.MockRunCommands.Call(commands, runLocation)
   457  }
   458  
   459  func (r *MockRunner) RunHook(hookName string) (runner.HookHandlerType, error) {
   460  	r.Context().(*MockContext).setStatusCalled = r.MockRunHook.setStatusCalled
   461  	return runner.ExplicitHookHandler, r.MockRunHook.Call(hookName)
   462  }
   463  
   464  type MockActionWaitRunner struct {
   465  	runner.Runner
   466  
   467  	context    runnercontext.Context
   468  	actionChan <-chan error
   469  
   470  	actionName string
   471  }
   472  
   473  func (r *MockActionWaitRunner) Context() runnercontext.Context {
   474  	return r.context
   475  }
   476  
   477  func (r *MockActionWaitRunner) RunAction(actionName string) (runner.HookHandlerType, error) {
   478  	r.actionName = actionName
   479  	return runner.ExplicitHookHandler, <-r.actionChan
   480  }
   481  
   482  func NewDeployCallbacks() *DeployCallbacks {
   483  	return &DeployCallbacks{
   484  		MockGetArchiveInfo:  &MockGetArchiveInfo{info: &MockBundleInfo{}},
   485  		MockSetCurrentCharm: &MockSetCurrentCharm{},
   486  	}
   487  }
   488  
   489  func NewDeployCommitCallbacks(err error) *DeployCallbacks {
   490  	return &DeployCallbacks{
   491  		MockInitializeMetricsTimers: &MockNoArgs{err: err},
   492  	}
   493  }
   494  func NewMockDeployer() *MockDeployer {
   495  	return &MockDeployer{
   496  		MockStage:          &MockStage{},
   497  		MockDeploy:         &MockNoArgs{},
   498  		MockNotifyRevert:   &MockNoArgs{},
   499  		MockNotifyResolved: &MockNoArgs{},
   500  	}
   501  }
   502  
   503  func NewPrepareHookCallbacks(kind hooks.Kind) *PrepareHookCallbacks {
   504  	return &PrepareHookCallbacks{
   505  		MockPrepareHook: &MockPrepareHook{nil, string(kind), nil},
   506  	}
   507  }
   508  
   509  func NewRunActionRunnerFactory(runErr error) *MockRunnerFactory {
   510  	return &MockRunnerFactory{
   511  		MockNewActionRunner: &MockNewActionRunner{
   512  			runner: &MockRunner{
   513  				MockRunAction: &MockRunAction{err: runErr},
   514  				context: &MockContext{
   515  					actionData: &runnercontext.ActionData{Name: "some-action-name"},
   516  				},
   517  			},
   518  		},
   519  	}
   520  }
   521  
   522  func NewRunActionWaitRunnerFactory(actionChan <-chan error) *MockRunnerActionWaitFactory {
   523  	return &MockRunnerActionWaitFactory{
   524  		MockNewActionWaitRunner: &MockNewActionWaitRunner{
   525  			runner: &MockActionWaitRunner{
   526  				actionChan: actionChan,
   527  				context: &MockContext{
   528  					actionData: &runnercontext.ActionData{
   529  						Name: "some-action-name",
   530  					},
   531  				},
   532  			},
   533  		},
   534  	}
   535  }
   536  
   537  func NewRunCommandsRunnerFactory(runResponse *utilexec.ExecResponse, runErr error) *MockRunnerFactory {
   538  	return &MockRunnerFactory{
   539  		MockNewCommandRunner: &MockNewCommandRunner{
   540  			runner: &MockRunner{
   541  				MockRunCommands: &MockRunCommands{response: runResponse, err: runErr},
   542  				context:         &MockContext{},
   543  			},
   544  		},
   545  	}
   546  }
   547  
   548  func NewRunHookRunnerFactory(runErr error, contextOps ...func(*MockContext)) *MockRunnerFactory {
   549  	ctx := &MockContext{isLeader: true}
   550  	for _, op := range contextOps {
   551  		op(ctx)
   552  	}
   553  
   554  	return &MockRunnerFactory{
   555  		MockNewHookRunner: &MockNewHookRunner{
   556  			runner: &MockRunner{
   557  				MockRunHook: &MockRunHook{err: runErr},
   558  				context:     ctx,
   559  			},
   560  		},
   561  	}
   562  }
   563  
   564  type MockSendResponse struct {
   565  	gotResponse **utilexec.ExecResponse
   566  	gotErr      *error
   567  	eatError    bool
   568  }
   569  
   570  func (mock *MockSendResponse) Call(response *utilexec.ExecResponse, err error) bool {
   571  	mock.gotResponse = &response
   572  	mock.gotErr = &err
   573  	return mock.eatError
   574  }
   575  
   576  var someActionId = "f47ac10b-58cc-4372-a567-0e02b2c3d479"
   577  var randomActionId = "9f484882-2f18-4fd2-967d-db9663db7bea"
   578  var overwriteState = operation.State{
   579  	Kind:     operation.Continue,
   580  	Step:     operation.Pending,
   581  	Started:  true,
   582  	CharmURL: "ch:quantal/wordpress-2",
   583  	ActionId: &randomActionId,
   584  	Hook:     &hook.Info{Kind: hooks.Install},
   585  }
   586  var someCommandArgs = operation.CommandArgs{
   587  	Commands:        "do something",
   588  	RelationId:      123,
   589  	RemoteUnitName:  "foo/456",
   590  	ForceRemoteUnit: true,
   591  	RunLocation:     runner.Workload,
   592  }
   593  
   594  type RemoteInitCallbacks struct {
   595  	operation.Callbacks
   596  	MockRemoteInit *MockRemoteInit
   597  }
   598  
   599  func (cb *RemoteInitCallbacks) RemoteInit(runningStatus remotestate.ContainerRunningStatus, abort <-chan struct{}) error {
   600  	return cb.MockRemoteInit.Call(runningStatus, abort)
   601  }
   602  
   603  type MockRemoteInit struct {
   604  	gotRunningStatus *remotestate.ContainerRunningStatus
   605  	gotAbort         <-chan struct{}
   606  	err              error
   607  }
   608  
   609  func (mock *MockRemoteInit) Call(runningStatus remotestate.ContainerRunningStatus, abort <-chan struct{}) error {
   610  	mock.gotRunningStatus = &runningStatus
   611  	mock.gotAbort = abort
   612  	return mock.err
   613  }