github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/uniter/runcommands/runcommands.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package runcommands 5 6 import ( 7 "fmt" 8 "sync" 9 10 "github.com/juju/juju/worker/uniter/operation" 11 "github.com/juju/juju/worker/uniter/remotestate" 12 "github.com/juju/juju/worker/uniter/resolver" 13 ) 14 15 // Commands is an interface providing a means of storing and retrieving 16 // arguments for running commands. 17 type Commands interface { 18 // AddCommand adds the given command arguments and response function 19 // and returns a unique identifier. 20 AddCommand(operation.CommandArgs, operation.CommandResponseFunc) string 21 22 // GetCommand returns the command arguments and response function 23 // with the specified ID, as registered in AddCommand. 24 GetCommand(id string) (operation.CommandArgs, operation.CommandResponseFunc) 25 26 // RemoveCommand removes the command arguments and response function 27 // associated with the specified ID. 28 RemoveCommand(id string) 29 } 30 31 type commands struct { 32 mu sync.Mutex 33 nextId int 34 pending map[string]command 35 } 36 37 type command struct { 38 args operation.CommandArgs 39 response operation.CommandResponseFunc 40 } 41 42 func NewCommands() Commands { 43 return &commands{pending: make(map[string]command)} 44 } 45 46 func (c *commands) AddCommand(args operation.CommandArgs, response operation.CommandResponseFunc) string { 47 c.mu.Lock() 48 defer c.mu.Unlock() 49 id := fmt.Sprint(c.nextId) 50 c.nextId++ 51 c.pending[id] = command{args, response} 52 return id 53 } 54 55 func (c *commands) RemoveCommand(id string) { 56 c.mu.Lock() 57 delete(c.pending, id) 58 c.mu.Unlock() 59 } 60 61 func (c *commands) GetCommand(id string) (operation.CommandArgs, operation.CommandResponseFunc) { 62 c.mu.Lock() 63 defer c.mu.Unlock() 64 command := c.pending[id] 65 return command.args, command.response 66 } 67 68 // commandsResolver is a Resolver that returns operations to run pending 69 // commands. When a command is completed, the "commandCompleted" callback 70 // is invoked to remove the pending command from the remote state. 71 type commandsResolver struct { 72 commands Commands 73 commandCompleted func(id string) 74 } 75 76 // NewCommandsResolver returns a new Resolver that returns operations to 77 // execute "juju run" commands. 78 // 79 // The returned resolver's NextOp method will return operations to execute 80 // run commands whenever the remote state's "Commands" is non-empty, by 81 // taking the first ID in the sequence and fetching the command arguments 82 // from the Commands interface passed into this function. When the command 83 // execution operation is committed, the ID of the command is passed to the 84 // "commandCompleted" callback. 85 func NewCommandsResolver(commands Commands, commandCompleted func(string)) resolver.Resolver { 86 return &commandsResolver{commands, commandCompleted} 87 } 88 89 // NextOp is part of the resolver.Resolver interface. 90 func (s *commandsResolver) NextOp( 91 localState resolver.LocalState, 92 remoteState remotestate.Snapshot, 93 opFactory operation.Factory, 94 ) (operation.Operation, error) { 95 if len(remoteState.Commands) == 0 { 96 return nil, resolver.ErrNoOperation 97 } 98 id := remoteState.Commands[0] 99 op, err := opFactory.NewCommands(s.commands.GetCommand(id)) 100 if err != nil { 101 return nil, err 102 } 103 commandCompleted := func() { 104 s.commands.RemoveCommand(id) 105 s.commandCompleted(id) 106 } 107 return &commandCompleter{op, commandCompleted}, nil 108 } 109 110 type commandCompleter struct { 111 operation.Operation 112 commandCompleted func() 113 } 114 115 func (c *commandCompleter) Commit(st operation.State) (*operation.State, error) { 116 result, err := c.Operation.Commit(st) 117 if err == nil { 118 c.commandCompleted() 119 } 120 return result, err 121 }