| Left: | ||
| Right: |
| OLD | NEW |
|---|---|
| 1 package main | 1 package main |
| 2 | 2 |
| 3 import ( | 3 import ( |
| 4 "fmt" | |
| 5 "launchpad.net/gnuflag" | 4 "launchpad.net/gnuflag" |
| 6 "launchpad.net/juju/go/cmd" | 5 "launchpad.net/juju/go/cmd" |
| 6 "launchpad.net/juju/go/environs" | |
| 7 "launchpad.net/juju/go/log" | |
| 8 "launchpad.net/juju/go/state" | |
| 9 "launchpad.net/tomb" | |
| 10 | |
| 11 // register providers | |
| 12 _ "launchpad.net/juju/go/environs/dummy" | |
| 13 _ "launchpad.net/juju/go/environs/ec2" | |
| 7 ) | 14 ) |
| 8 | 15 |
| 9 // ProvisioningAgent is a cmd.Command responsible for running a provisioning age nt. | 16 // ProvisioningAgent is a cmd.Command responsible for running a provisioning age nt. |
| 10 type ProvisioningAgent struct { | 17 type ProvisioningAgent struct { |
| 11 Conf AgentConf | 18 Conf AgentConf |
| 12 } | 19 } |
| 13 | 20 |
| 14 // Info returns usage information for the command. | 21 // Info returns usage information for the command. |
| 15 func (a *ProvisioningAgent) Info() *cmd.Info { | 22 func (a *ProvisioningAgent) Info() *cmd.Info { |
| 16 return &cmd.Info{"provisioning", "", "run a juju provisioning agent", "" } | 23 return &cmd.Info{"provisioning", "", "run a juju provisioning agent", "" } |
| 17 } | 24 } |
| 18 | 25 |
| 19 // Init initializes the command for running. | 26 // Init initializes the command for running. |
| 20 func (a *ProvisioningAgent) Init(f *gnuflag.FlagSet, args []string) error { | 27 func (a *ProvisioningAgent) Init(f *gnuflag.FlagSet, args []string) error { |
| 21 a.Conf.addFlags(f) | 28 a.Conf.addFlags(f) |
| 22 if err := f.Parse(true, args); err != nil { | 29 if err := f.Parse(true, args); err != nil { |
| 23 return err | 30 return err |
| 24 } | 31 } |
| 25 return a.Conf.checkArgs(f.Args()) | 32 return a.Conf.checkArgs(f.Args()) |
| 26 } | 33 } |
| 27 | 34 |
| 28 // Run runs a provisioning agent. | 35 // Run runs a provisioning agent. |
| 29 func (a *ProvisioningAgent) Run(_ *cmd.Context) error { | 36 func (a *ProvisioningAgent) Run(_ *cmd.Context) error { |
| 30 » return fmt.Errorf("MachineAgent.Run not implemented") | 37 » st, err := state.Open(&a.Conf.StateInfo) |
|
niemeyer
2012/05/31 11:46:59
What happens if the connection is broken? We'll ne
dfc
2012/05/31 22:40:03
I have been operating under the assumption (I thin
dfc
2012/06/01 01:58:27
I looked into this more this morning and it looks
niemeyer
2012/06/04 23:22:23
We've covered this on juju-dev@. A TODO would be g
| |
| 38 » if err != nil { | |
| 39 » » return err | |
| 40 » } | |
| 41 » p := NewProvisioner(st) | |
| 42 » return p.tomb.Wait() | |
| 31 } | 43 } |
| 44 | |
| 45 type Provisioner struct { | |
| 46 st *state.State | |
| 47 environ environs.Environ | |
| 48 tomb tomb.Tomb | |
| 49 | |
| 50 environment | |
| 51 machines | |
| 52 } | |
| 53 | |
| 54 // environment ensures that the watcher for the environ | |
| 55 // configuration is valid. | |
| 56 type environment struct { | |
|
niemeyer
2012/05/31 11:46:59
Those two types feel unnecessary. It looks like yo
dfc
2012/05/31 22:40:03
This was an indulgence. I am besotted with the abi
| |
| 57 st *state.State | |
| 58 watcher *state.ConfigWatcher | |
| 59 } | |
| 60 | |
| 61 // changes returns a channel that will receive the new *ConfigNode when a | |
| 62 // change is detected.· | |
| 63 func (e *environment) changes() <-chan *state.ConfigNode { | |
| 64 if e.watcher == nil { | |
| 65 e.watcher = e.st.WatchEnvironConfig() | |
| 66 } | |
| 67 return e.watcher.Changes() | |
| 68 } | |
| 69 | |
| 70 // invalidate stops the current watcher. | |
| 71 func (e *environment) invalidate() { | |
|
niemeyer
2012/05/31 11:46:59
This should return the error it finds. Call sites
dfc
2012/05/31 22:40:03
Done.
| |
| 72 if e.watcher != nil { | |
| 73 log.Printf("provisioner: environment watcher exited: %v", e.watc her.Stop()) | |
|
niemeyer
2012/05/31 11:46:59
That Stop call is the most critical task done by t
dfc
2012/05/31 22:40:03
Done.
| |
| 74 } | |
| 75 e.watcher = nil | |
| 76 } | |
| 77 | |
| 78 // machines ensures that the watcher for machines changes | |
| 79 // is valid. | |
| 80 type machines struct { | |
| 81 st *state.State | |
| 82 watcher *state.MachinesWatcher | |
| 83 } | |
| 84 | |
| 85 // changes returns a channel that will receive the new *ConfigNode when a | |
| 86 // change is detected.· | |
| 87 func (m *machines) changes() <-chan *state.MachinesChange { | |
| 88 if m.watcher == nil { | |
| 89 m.watcher = m.st.WatchMachines() | |
| 90 } | |
| 91 return m.watcher.Changes() | |
| 92 } | |
| 93 | |
| 94 // invalidate stops the current watcher. | |
| 95 func (m *machines) invalidate() { | |
| 96 if m.watcher != nil { | |
| 97 log.Printf("provisioner: machines watcher exited: %v", m.watcher .Stop()) | |
| 98 } | |
| 99 m.watcher = nil | |
| 100 } | |
| 101 | |
| 102 // NewProvisioner returns a Provisioner. | |
| 103 func NewProvisioner(st *state.State) *Provisioner { | |
| 104 p := &Provisioner{ | |
| 105 st: st, | |
| 106 environment: environment{st: st}, | |
| 107 machines: machines{st: st}, | |
| 108 } | |
| 109 go p.loop() | |
| 110 return p | |
| 111 } | |
| 112 | |
| 113 func (p *Provisioner) loop() { | |
| 114 defer p.tomb.Done() | |
| 115 for { | |
| 116 select { | |
| 117 case <-p.tomb.Dying(): | |
| 118 return | |
| 119 case config, ok := <-p.environment.changes(): | |
| 120 if !ok { | |
| 121 p.environment.invalidate() | |
| 122 continue | |
| 123 } | |
| 124 var err error | |
| 125 p.environ, err = environs.NewEnviron(config.Map()) | |
| 126 if err != nil { | |
| 127 log.Printf("provisioner: unable to create enviro nment from supplied configuration: %v", err) | |
| 128 continue | |
| 129 } | |
| 130 log.Printf("provisioning: valid environment configured") | |
| 131 p.innerLoop() | |
| 132 } | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 func (p *Provisioner) innerLoop() { | |
|
niemeyer
2012/05/31 11:46:59
Nice organization.
dfc
2012/05/31 22:40:03
Thank you. As much as I would like to have only on
| |
| 137 for { | |
| 138 select { | |
| 139 case <-p.tomb.Dying(): | |
| 140 return | |
| 141 case change, ok := <-p.environment.changes(): | |
| 142 if !ok { | |
| 143 p.environment.invalidate() | |
| 144 continue | |
| 145 } | |
| 146 config, err := environs.NewConfig(change.Map()) | |
| 147 if err != nil { | |
| 148 log.Printf("provisioning: new configuration rece ived, but was not valid: %v", err) | |
| 149 continue | |
| 150 } | |
| 151 p.environ.SetConfig(config) | |
| 152 log.Printf("provisioning: new configuartion applied") | |
|
niemeyer
2012/05/31 11:46:59
s/configuartion/environment configuration/ (note t
dfc
2012/05/31 22:40:03
Done.
| |
| 153 case machines, ok := <-p.machines.changes(): | |
| 154 if !ok { | |
| 155 p.machines.invalidate() | |
| 156 continue | |
| 157 } | |
| 158 p.processMachines(machines) | |
| 159 } | |
|
niemeyer
2012/05/31 11:46:59
I'm wondering if rather than "continue" in the pro
dfc
2012/05/31 22:40:03
At the moment, by my reading, a watcher is always
| |
| 160 } | |
| 161 } | |
| 162 | |
| 163 // Stop stops the Provisioner and returns any error encountered while | |
| 164 // provisioning. | |
| 165 func (p *Provisioner) Stop() error { | |
| 166 p.tomb.Kill(nil) | |
| 167 p.environment.invalidate() | |
|
niemeyer
2012/05/31 11:46:59
I suggest logic like this here:
p.tomb.Kill(nil)
dfc
2012/05/31 22:40:03
SGTM. I'm never sure how to merge several error va
| |
| 168 p.machines.invalidate() | |
| 169 return p.tomb.Wait() | |
| 170 } | |
| 171 | |
| 172 func (p *Provisioner) processMachines(changes *state.MachinesChange) {} | |
| OLD | NEW |