func (s *relationsSuite) TestNextOpNothing(c *gc.C) { unitTag := names.NewUnitTag("wordpress/0") abort := make(chan struct{}) var numCalls int32 unitEntity := params.Entities{Entities: []params.Entity{params.Entity{Tag: "unit-wordpress-0"}}} apiCaller := mockAPICaller(c, &numCalls, uniterApiCall("Life", unitEntity, params.LifeResults{Results: []params.LifeResult{{Life: params.Alive}}}, nil), uniterApiCall("JoinedRelations", unitEntity, params.StringsResults{Results: []params.StringsResult{{Result: []string{}}}}, nil), uniterApiCall("GetPrincipal", unitEntity, params.StringBoolResults{Results: []params.StringBoolResult{{Result: "", Ok: false}}}, nil), ) st := uniter.NewState(apiCaller, unitTag) r, err := relation.NewRelations(st, unitTag, s.stateDir, s.relationsDir, abort) c.Assert(err, jc.ErrorIsNil) assertNumCalls(c, &numCalls, 2) localState := resolver.LocalState{ State: operation.State{ Kind: operation.Continue, }, } remoteState := remotestate.Snapshot{} relationsResolver := relation.NewRelationsResolver(r) _, err = relationsResolver.NextOp(localState, remoteState, &mockOperations{}) c.Assert(errors.Cause(err), gc.Equals, resolver.ErrNoOperation) }
func (s *relationsSuite) TestImplicitRelationNoHooks(c *gc.C) { unitTag := names.NewUnitTag("wordpress/0") abort := make(chan struct{}) unitEntity := params.Entities{Entities: []params.Entity{params.Entity{Tag: "unit-wordpress-0"}}} relationResults := params.RelationResults{ Results: []params.RelationResult{ { Id: 1, Key: "wordpress:juju-info juju-info:juju-info", Life: params.Alive, Endpoint: multiwatcher.Endpoint{ ApplicationName: "wordpress", Relation: multiwatcher.CharmRelation{Name: "juju-info", Role: string(charm.RoleProvider), Interface: "juju-info", Scope: "global"}, }}, }, } relationUnits := params.RelationUnits{RelationUnits: []params.RelationUnit{ {Relation: "relation-wordpress.juju-info#juju-info.juju-info", Unit: "unit-wordpress-0"}, }} apiCalls := []apiCall{ uniterApiCall("Life", unitEntity, params.LifeResults{Results: []params.LifeResult{{Life: params.Alive}}}, nil), uniterApiCall("JoinedRelations", unitEntity, params.StringsResults{Results: []params.StringsResult{{Result: []string{}}}}, nil), uniterApiCall("RelationById", params.RelationIds{RelationIds: []int{1}}, relationResults, nil), uniterApiCall("Relation", relationUnits, relationResults, nil), uniterApiCall("Relation", relationUnits, relationResults, nil), uniterApiCall("Watch", unitEntity, params.NotifyWatchResults{Results: []params.NotifyWatchResult{{NotifyWatcherId: "1"}}}, nil), uniterApiCall("EnterScope", relationUnits, params.ErrorResults{Results: []params.ErrorResult{{}}}, nil), uniterApiCall("GetPrincipal", unitEntity, params.StringBoolResults{Results: []params.StringBoolResult{{Result: "", Ok: false}}}, nil), } var numCalls int32 apiCaller := mockAPICaller(c, &numCalls, apiCalls...) st := uniter.NewState(apiCaller, unitTag) r, err := relation.NewRelations(st, unitTag, s.stateDir, s.relationsDir, abort) c.Assert(err, jc.ErrorIsNil) localState := resolver.LocalState{ State: operation.State{ Kind: operation.Continue, }, } remoteState := remotestate.Snapshot{ Relations: map[int]remotestate.RelationSnapshot{ 1: remotestate.RelationSnapshot{ Life: params.Alive, Members: map[string]int64{ "wordpress": 1, }, }, }, } relationsResolver := relation.NewRelationsResolver(r) _, err = relationsResolver.NextOp(localState, remoteState, &mockOperations{}) c.Assert(errors.Cause(err), gc.Equals, resolver.ErrNoOperation) }
func (s *relationsSuite) setupRelations(c *gc.C) relation.Relations { unitTag := names.NewUnitTag("wordpress/0") abort := make(chan struct{}) var numCalls int32 unitEntity := params.Entities{Entities: []params.Entity{params.Entity{Tag: "unit-wordpress-0"}}} apiCaller := mockAPICaller(c, &numCalls, uniterApiCall("Life", unitEntity, params.LifeResults{Results: []params.LifeResult{{Life: params.Alive}}}, nil), uniterApiCall("JoinedRelations", unitEntity, params.StringsResults{Results: []params.StringsResult{{Result: []string{}}}}, nil), ) st := uniter.NewState(apiCaller, unitTag) r, err := relation.NewRelations(st, unitTag, s.stateDir, s.relationsDir, abort) c.Assert(err, jc.ErrorIsNil) assertNumCalls(c, &numCalls, 2) return r }
func (s *relationsSuite) TestNewRelationsWithExistingRelations(c *gc.C) { unitTag := names.NewUnitTag("wordpress/0") abort := make(chan struct{}) var numCalls int32 unitEntity := params.Entities{Entities: []params.Entity{params.Entity{Tag: "unit-wordpress-0"}}} relationUnits := params.RelationUnits{RelationUnits: []params.RelationUnit{ {Relation: "relation-wordpress.db#mysql.db", Unit: "unit-wordpress-0"}, }} relationResults := params.RelationResults{ Results: []params.RelationResult{ { Id: 1, Key: "wordpress:db mysql:db", Life: params.Alive, Endpoint: multiwatcher.Endpoint{ ApplicationName: "wordpress", Relation: multiwatcher.CharmRelation{Name: "mysql", Role: string(charm.RoleProvider), Interface: "db"}, }}, }, } apiCaller := mockAPICaller(c, &numCalls, uniterApiCall("Life", unitEntity, params.LifeResults{Results: []params.LifeResult{{Life: params.Alive}}}, nil), uniterApiCall("JoinedRelations", unitEntity, params.StringsResults{Results: []params.StringsResult{{Result: []string{"relation-wordpress:db mysql:db"}}}}, nil), uniterApiCall("Relation", relationUnits, relationResults, nil), uniterApiCall("Relation", relationUnits, relationResults, nil), uniterApiCall("Watch", unitEntity, params.NotifyWatchResults{Results: []params.NotifyWatchResult{{NotifyWatcherId: "1"}}}, nil), uniterApiCall("EnterScope", relationUnits, params.ErrorResults{Results: []params.ErrorResult{{}}}, nil), ) st := uniter.NewState(apiCaller, unitTag) r, err := relation.NewRelations(st, unitTag, s.stateDir, s.relationsDir, abort) c.Assert(err, jc.ErrorIsNil) assertNumCalls(c, &numCalls, 6) info := r.GetInfo() c.Assert(info, gc.HasLen, 1) oneInfo := info[1] c.Assert(oneInfo.RelationUnit.Relation().Tag(), gc.Equals, names.NewRelationTag("wordpress:db mysql:db")) c.Assert(oneInfo.RelationUnit.Endpoint(), jc.DeepEquals, uniter.Endpoint{ Relation: charm.Relation{Name: "mysql", Role: "provider", Interface: "db", Optional: false, Limit: 0, Scope: ""}, }) c.Assert(oneInfo.MemberNames, gc.HasLen, 0) }
func (s *relationsSuite) assertHookRelationJoined(c *gc.C, numCalls *int32, apiCalls ...apiCall) relation.Relations { unitTag := names.NewUnitTag("wordpress/0") abort := make(chan struct{}) apiCaller := mockAPICaller(c, numCalls, apiCalls...) st := uniter.NewState(apiCaller, unitTag) r, err := relation.NewRelations(st, unitTag, s.stateDir, s.relationsDir, abort) c.Assert(err, jc.ErrorIsNil) assertNumCalls(c, numCalls, 2) localState := resolver.LocalState{ State: operation.State{ Kind: operation.Continue, }, } remoteState := remotestate.Snapshot{ Relations: map[int]remotestate.RelationSnapshot{ 1: remotestate.RelationSnapshot{ Life: params.Alive, Members: map[string]int64{ "wordpress": 1, }, }, }, } relationsResolver := relation.NewRelationsResolver(r) op, err := relationsResolver.NextOp(localState, remoteState, &mockOperations{}) c.Assert(err, jc.ErrorIsNil) assertNumCalls(c, numCalls, 8) c.Assert(op.String(), gc.Equals, "run hook relation-joined on unit with relation 1") // Commit the operation so we save local state for any next operation. _, err = r.PrepareHook(op.(*mockOperation).hookInfo) c.Assert(err, jc.ErrorIsNil) err = r.CommitHook(op.(*mockOperation).hookInfo) c.Assert(err, jc.ErrorIsNil) return r }
func (u *Uniter) init(unitTag names.UnitTag) (err error) { u.unit, err = u.st.Unit(unitTag) if err != nil { return err } if u.unit.Life() == params.Dead { // If we started up already dead, we should not progress further. If we // become Dead immediately after starting up, we may well complete any // operations in progress before detecting it; but that race is fundamental // and inescapable, whereas this one is not. return worker.ErrTerminateAgent } if err = u.setupLocks(); err != nil { return err } if err := jujuc.EnsureSymlinks(u.paths.ToolsDir); err != nil { return err } if err := os.MkdirAll(u.paths.State.RelationsDir, 0755); err != nil { return errors.Trace(err) } relations, err := relation.NewRelations( u.st, unitTag, u.paths.State.CharmDir, u.paths.State.RelationsDir, u.catacomb.Dying(), ) if err != nil { return errors.Annotatef(err, "cannot create relations") } u.relations = relations storageAttachments, err := storage.NewAttachments( u.st, unitTag, u.paths.State.StorageDir, u.catacomb.Dying(), ) if err != nil { return errors.Annotatef(err, "cannot create storage hook source") } u.storage = storageAttachments u.commands = runcommands.NewCommands() u.commandChannel = make(chan string) deployer, err := charm.NewDeployer( u.paths.State.CharmDir, u.paths.State.DeployerDir, charm.NewBundlesDir(u.paths.State.BundlesDir, u.downloader), ) if err != nil { return errors.Annotatef(err, "cannot create deployer") } u.deployer = &deployerProxy{deployer} contextFactory, err := context.NewContextFactory( u.st, unitTag, u.leadershipTracker, u.relations.GetInfo, u.storage, u.paths, u.clock, ) if err != nil { return err } runnerFactory, err := runner.NewFactory( u.st, u.paths, contextFactory, ) if err != nil { return errors.Trace(err) } u.operationFactory = operation.NewFactory(operation.FactoryParams{ Deployer: u.deployer, RunnerFactory: runnerFactory, Callbacks: &operationCallbacks{u}, Abort: u.catacomb.Dying(), MetricSpoolDir: u.paths.GetMetricsSpoolDir(), }) operationExecutor, err := u.newOperationExecutor(u.paths.State.OperationsFile, u.getServiceCharmURL, u.acquireExecutionLock) if err != nil { return errors.Trace(err) } u.operationExecutor = operationExecutor logger.Debugf("starting juju-run listener on unix:%s", u.paths.Runtime.JujuRunSocket) commandRunner, err := NewChannelCommandRunner(ChannelCommandRunnerConfig{ Abort: u.catacomb.Dying(), Commands: u.commands, CommandChannel: u.commandChannel, }) if err != nil { return errors.Annotate(err, "creating command runner") } u.runListener, err = NewRunListener(RunListenerConfig{ SocketPath: u.paths.Runtime.JujuRunSocket, CommandRunner: commandRunner, }) if err != nil { return errors.Trace(err) } rlw := newRunListenerWrapper(u.runListener) if err := u.catacomb.Add(rlw); err != nil { return errors.Trace(err) } // The socket needs to have permissions 777 in order for other users to use it. if jujuos.HostOS() != jujuos.Windows { return os.Chmod(u.paths.Runtime.JujuRunSocket, 0777) } return nil }
func (u *Uniter) init(unitTag names.UnitTag) (err error) { u.unit, err = u.st.Unit(unitTag) if err != nil { return err } if u.unit.Life() == params.Dead { // If we started up already dead, we should not progress further. If we // become Dead immediately after starting up, we may well complete any // operations in progress before detecting it; but that race is fundamental // and inescapable, whereas this one is not. return worker.ErrTerminateAgent } // If initialising for the first time after deploying, update the status. currentStatus, err := u.unit.UnitStatus() if err != nil { return err } // TODO(fwereade/wallyworld): we should have an explicit place in the model // to tell us when we've hit this point, instead of piggybacking on top of // status and/or status history. // If the previous status was waiting for machine, we transition to the next step. if currentStatus.Status == string(status.Waiting) && (currentStatus.Info == status.MessageWaitForMachine || currentStatus.Info == status.MessageInstallingAgent) { if err := u.unit.SetUnitStatus(status.Waiting, status.MessageInitializingAgent, nil); err != nil { return errors.Trace(err) } } if err := jujuc.EnsureSymlinks(u.paths.ToolsDir); err != nil { return err } if err := os.MkdirAll(u.paths.State.RelationsDir, 0755); err != nil { return errors.Trace(err) } relations, err := relation.NewRelations( u.st, unitTag, u.paths.State.CharmDir, u.paths.State.RelationsDir, u.catacomb.Dying(), ) if err != nil { return errors.Annotatef(err, "cannot create relations") } u.relations = relations storageAttachments, err := storage.NewAttachments( u.st, unitTag, u.paths.State.StorageDir, u.catacomb.Dying(), ) if err != nil { return errors.Annotatef(err, "cannot create storage hook source") } u.storage = storageAttachments u.commands = runcommands.NewCommands() u.commandChannel = make(chan string) deployer, err := charm.NewDeployer( u.paths.State.CharmDir, u.paths.State.DeployerDir, charm.NewBundlesDir(u.paths.State.BundlesDir, u.downloader), ) if err != nil { return errors.Annotatef(err, "cannot create deployer") } contextFactory, err := context.NewContextFactory( u.st, unitTag, u.leadershipTracker, u.relations.GetInfo, u.storage, u.paths, u.clock, ) if err != nil { return err } runnerFactory, err := runner.NewFactory( u.st, u.paths, contextFactory, ) if err != nil { return errors.Trace(err) } u.operationFactory = operation.NewFactory(operation.FactoryParams{ Deployer: deployer, RunnerFactory: runnerFactory, Callbacks: &operationCallbacks{u}, Abort: u.catacomb.Dying(), MetricSpoolDir: u.paths.GetMetricsSpoolDir(), }) operationExecutor, err := u.newOperationExecutor(u.paths.State.OperationsFile, u.getServiceCharmURL, u.acquireExecutionLock) if err != nil { return errors.Trace(err) } u.operationExecutor = operationExecutor logger.Debugf("starting juju-run listener on unix:%s", u.paths.Runtime.JujuRunSocket) commandRunner, err := NewChannelCommandRunner(ChannelCommandRunnerConfig{ Abort: u.catacomb.Dying(), Commands: u.commands, CommandChannel: u.commandChannel, }) if err != nil { return errors.Annotate(err, "creating command runner") } u.runListener, err = NewRunListener(RunListenerConfig{ SocketPath: u.paths.Runtime.JujuRunSocket, CommandRunner: commandRunner, }) if err != nil { return errors.Trace(err) } rlw := newRunListenerWrapper(u.runListener) if err := u.catacomb.Add(rlw); err != nil { return errors.Trace(err) } // The socket needs to have permissions 777 in order for other users to use it. if jujuos.HostOS() != jujuos.Windows { return os.Chmod(u.paths.Runtime.JujuRunSocket, 0777) } return nil }