github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/uniter/operation/factory_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  	"github.com/juju/charm/v12/hooks"
     8  	"github.com/juju/loggo"
     9  	"github.com/juju/names/v5"
    10  	"github.com/juju/testing"
    11  	jc "github.com/juju/testing/checkers"
    12  	utilexec "github.com/juju/utils/v3/exec"
    13  	gc "gopkg.in/check.v1"
    14  
    15  	"github.com/juju/juju/api/agent/uniter"
    16  	basetesting "github.com/juju/juju/api/base/testing"
    17  	"github.com/juju/juju/rpc/params"
    18  	"github.com/juju/juju/worker/common/charmrunner"
    19  	"github.com/juju/juju/worker/uniter/hook"
    20  	"github.com/juju/juju/worker/uniter/operation"
    21  )
    22  
    23  type FactorySuite struct {
    24  	testing.IsolationSuite
    25  	factory   operation.Factory
    26  	actionErr *params.Error
    27  }
    28  
    29  var _ = gc.Suite(&FactorySuite{})
    30  
    31  func (s *FactorySuite) SetUpTest(c *gc.C) {
    32  	s.IsolationSuite.SetUpTest(c)
    33  	// Yes, this factory will produce useless ops; this suite is just for
    34  	// verifying that inadequate args to the factory methods will produce
    35  	// the expected errors; and that the results of same get a string
    36  	// representation that does not depend on the factory attributes.
    37  	deployer := &MockDeployer{
    38  		MockNotifyRevert:   &MockNoArgs{},
    39  		MockNotifyResolved: &MockNoArgs{},
    40  	}
    41  	s.actionErr = nil
    42  	apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
    43  		actionResult := params.ActionResult{
    44  			Action: &params.Action{Name: "backup"},
    45  		}
    46  		if s.actionErr != nil {
    47  			actionResult = params.ActionResult{Error: s.actionErr}
    48  		}
    49  		*(result.(*params.ActionResults)) = params.ActionResults{
    50  			Results: []params.ActionResult{actionResult},
    51  		}
    52  		return nil
    53  	})
    54  	st := uniter.NewState(apiCaller, names.NewUnitTag("mysql/0"))
    55  	s.factory = operation.NewFactory(operation.FactoryParams{
    56  		State:    st,
    57  		Deployer: deployer,
    58  		Logger:   loggo.GetLogger("test"),
    59  	})
    60  }
    61  
    62  func (s *FactorySuite) testNewDeployError(c *gc.C, newDeploy newDeploy) {
    63  	op, err := newDeploy(s.factory, "")
    64  	c.Check(op, gc.IsNil)
    65  	c.Check(err, gc.ErrorMatches, "charm url required")
    66  }
    67  
    68  func (s *FactorySuite) TestNewInstallError(c *gc.C) {
    69  	s.testNewDeployError(c, (operation.Factory).NewInstall)
    70  }
    71  
    72  func (s *FactorySuite) TestNewUpgradeError(c *gc.C) {
    73  	s.testNewDeployError(c, (operation.Factory).NewUpgrade)
    74  }
    75  
    76  func (s *FactorySuite) TestNewRevertUpgradeError(c *gc.C) {
    77  	s.testNewDeployError(c, (operation.Factory).NewRevertUpgrade)
    78  }
    79  
    80  func (s *FactorySuite) TestNewResolvedUpgradeError(c *gc.C) {
    81  	s.testNewDeployError(c, (operation.Factory).NewResolvedUpgrade)
    82  }
    83  
    84  func (s *FactorySuite) testNewDeployString(c *gc.C, newDeploy newDeploy, expectPrefix string) {
    85  	op, err := newDeploy(s.factory, "ch:quantal/wordpress-1")
    86  	c.Check(err, jc.ErrorIsNil)
    87  	c.Check(op.String(), gc.Equals, expectPrefix+" ch:quantal/wordpress-1")
    88  }
    89  
    90  func (s *FactorySuite) TestNewInstallString(c *gc.C) {
    91  	s.testNewDeployString(c, (operation.Factory).NewInstall, "install")
    92  }
    93  
    94  func (s *FactorySuite) TestNewUpgradeString(c *gc.C) {
    95  	s.testNewDeployString(c, (operation.Factory).NewUpgrade, "upgrade to")
    96  }
    97  
    98  func (s *FactorySuite) TestNewRevertUpgradeString(c *gc.C) {
    99  	s.testNewDeployString(c,
   100  		(operation.Factory).NewRevertUpgrade,
   101  		"switch upgrade to",
   102  	)
   103  }
   104  
   105  func (s *FactorySuite) TestNewResolvedUpgradeString(c *gc.C) {
   106  	s.testNewDeployString(c,
   107  		(operation.Factory).NewResolvedUpgrade,
   108  		"continue upgrade to",
   109  	)
   110  }
   111  
   112  func (s *FactorySuite) TestNewActionError(c *gc.C) {
   113  	op, err := s.factory.NewAction("lol-something")
   114  	c.Check(op, gc.IsNil)
   115  	c.Check(err, gc.ErrorMatches, `invalid action id "lol-something"`)
   116  }
   117  
   118  func (s *FactorySuite) TestNewActionString(c *gc.C) {
   119  	op, err := s.factory.NewAction(someActionId)
   120  	c.Check(err, jc.ErrorIsNil)
   121  	c.Check(op.String(), gc.Equals, "run action "+someActionId)
   122  }
   123  
   124  func panicSendResponse(*utilexec.ExecResponse, error) bool {
   125  	panic("don't call this")
   126  }
   127  
   128  func commandArgs(commands string, relationId int, remoteUnit string) operation.CommandArgs {
   129  	return operation.CommandArgs{
   130  		Commands:       commands,
   131  		RelationId:     relationId,
   132  		RemoteUnitName: remoteUnit,
   133  		// TODO(jam): 2019-10-24 Include RemoteAppName
   134  	}
   135  }
   136  
   137  func (s *FactorySuite) TestNewCommandsSendResponseError(c *gc.C) {
   138  	op, err := s.factory.NewCommands(commandArgs("anything", -1, ""), nil)
   139  	c.Check(op, gc.IsNil)
   140  	c.Check(err, gc.ErrorMatches, "response sender required")
   141  }
   142  
   143  func (s *FactorySuite) testNewCommandsArgsError(
   144  	c *gc.C, args operation.CommandArgs, expect string,
   145  ) {
   146  	op, err := s.factory.NewCommands(args, panicSendResponse)
   147  	c.Check(op, gc.IsNil)
   148  	c.Check(err, gc.ErrorMatches, expect)
   149  }
   150  
   151  func (s *FactorySuite) TestNewCommandsArgsError_NoCommand(c *gc.C) {
   152  	s.testNewCommandsArgsError(c,
   153  		commandArgs("", -1, ""),
   154  		"commands required",
   155  	)
   156  }
   157  
   158  func (s *FactorySuite) TestNewCommandsArgsError_BadRemoteUnit(c *gc.C) {
   159  	s.testNewCommandsArgsError(c,
   160  		commandArgs("any old thing", -1, "unit/1"),
   161  		"remote unit not valid without relation",
   162  	)
   163  }
   164  
   165  func (s *FactorySuite) TestNewCommandsArgsError_BadRemoteUnitName(c *gc.C) {
   166  	s.testNewCommandsArgsError(c,
   167  		commandArgs("any old thing", 0, "lol"),
   168  		`invalid remote unit name "lol"`,
   169  	)
   170  }
   171  
   172  func (s *FactorySuite) testNewCommandsString(
   173  	c *gc.C, args operation.CommandArgs, expect string,
   174  ) {
   175  	op, err := s.factory.NewCommands(args, panicSendResponse)
   176  	c.Check(err, jc.ErrorIsNil)
   177  	c.Check(op.String(), gc.Equals, expect)
   178  }
   179  
   180  func (s *FactorySuite) TestNewCommandsString_CommandsOnly(c *gc.C) {
   181  	s.testNewCommandsString(c,
   182  		commandArgs("anything", -1, ""),
   183  		"run commands",
   184  	)
   185  }
   186  
   187  func (s *FactorySuite) TestNewCommandsString_WithRelation(c *gc.C) {
   188  	s.testNewCommandsString(c,
   189  		commandArgs("anything", 0, ""),
   190  		"run commands (0)",
   191  	)
   192  }
   193  
   194  func (s *FactorySuite) TestNewCommandsString_WithRelationAndUnit(c *gc.C) {
   195  	s.testNewCommandsString(c,
   196  		commandArgs("anything", 3, "unit/123"),
   197  		"run commands (3; unit/123)",
   198  	)
   199  }
   200  
   201  func (s *FactorySuite) testNewHookError(c *gc.C, newHook newHook) {
   202  	op, err := newHook(s.factory, hook.Info{Kind: hooks.Kind("gibberish")})
   203  	c.Check(op, gc.IsNil)
   204  	c.Check(err, gc.ErrorMatches, `unknown hook kind "gibberish"`)
   205  }
   206  
   207  func (s *FactorySuite) TestNewHookError_Run(c *gc.C) {
   208  	s.testNewHookError(c, (operation.Factory).NewRunHook)
   209  }
   210  
   211  func (s *FactorySuite) TestNewHookError_Skip(c *gc.C) {
   212  	s.testNewHookError(c, (operation.Factory).NewSkipHook)
   213  }
   214  
   215  func (s *FactorySuite) TestNewHookString_Run(c *gc.C) {
   216  	op, err := s.factory.NewRunHook(hook.Info{Kind: hooks.Install})
   217  	c.Check(err, jc.ErrorIsNil)
   218  	c.Check(op.String(), gc.Equals, "run install hook")
   219  }
   220  
   221  func (s *FactorySuite) TestNewHookString_Skip(c *gc.C) {
   222  	op, err := s.factory.NewSkipHook(hook.Info{
   223  		Kind:              hooks.RelationJoined,
   224  		RemoteUnit:        "foo/22",
   225  		RemoteApplication: "foo/22",
   226  		RelationId:        123,
   227  	})
   228  	c.Assert(err, jc.ErrorIsNil)
   229  	c.Check(op.String(), gc.Equals, "skip run relation-joined (123; unit: foo/22) hook")
   230  }
   231  
   232  func (s *FactorySuite) TestNewAcceptLeadershipString(c *gc.C) {
   233  	op, err := s.factory.NewAcceptLeadership()
   234  	c.Assert(err, jc.ErrorIsNil)
   235  	c.Assert(op.String(), gc.Equals, "accept leadership")
   236  }
   237  
   238  func (s *FactorySuite) TestNewResignLeadershipString(c *gc.C) {
   239  	op, err := s.factory.NewResignLeadership()
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	c.Assert(op.String(), gc.Equals, "resign leadership")
   242  }
   243  
   244  func (s *FactorySuite) TestNewActionNotAvailable(c *gc.C) {
   245  	s.actionErr = &params.Error{Code: "action no longer available"}
   246  	rnr, err := s.factory.NewAction("666")
   247  	c.Assert(rnr, gc.IsNil)
   248  	c.Assert(err, gc.Equals, charmrunner.ErrActionNotAvailable)
   249  }
   250  
   251  func (s *FactorySuite) TestNewActionUnauthorised(c *gc.C) {
   252  	s.actionErr = &params.Error{Code: "unauthorized access"}
   253  	rnr, err := s.factory.NewAction("666")
   254  	c.Assert(rnr, gc.IsNil)
   255  	c.Assert(err, gc.Equals, charmrunner.ErrActionNotAvailable)
   256  }
   257  
   258  func (s *FactorySuite) TestNewNoOpSecretsRemoved(c *gc.C) {
   259  	op, err := s.factory.NewNoOpSecretsRemoved([]string{"secreturi"})
   260  	c.Assert(err, jc.ErrorIsNil)
   261  	c.Assert(op.String(), gc.Equals, "process removed secrets: [secreturi]")
   262  }