Left: | ||
Right: |
OLD | NEW |
---|---|
1 package main | 1 package main |
2 | 2 |
3 import ( | 3 import ( |
4 "fmt" | 4 "fmt" |
5 | |
5 "launchpad.net/gnuflag" | 6 "launchpad.net/gnuflag" |
6 "launchpad.net/juju/go/cmd" | 7 "launchpad.net/juju/go/cmd" |
8 "launchpad.net/juju/go/environs" | |
9 "launchpad.net/juju/go/log" | |
10 "launchpad.net/juju/go/state" | |
11 | |
12 // register providers | |
13 _ "launchpad.net/juju/go/environs/dummy" | |
14 _ "launchpad.net/juju/go/environs/ec2" | |
7 ) | 15 ) |
8 | 16 |
17 const PROVIDER_MACHINE_ID = "provider-machine-id" | |
niemeyer
2012/05/30 21:09:21
providerMachineId would be the convention, I think
dfc
2012/05/31 07:10:01
Done.
| |
18 | |
9 // ProvisioningAgent is a cmd.Command responsible for running a provisioning age nt. | 19 // ProvisioningAgent is a cmd.Command responsible for running a provisioning age nt. |
10 type ProvisioningAgent struct { | 20 type ProvisioningAgent struct { |
11 » Conf AgentConf | 21 » Conf AgentConf |
22 » environ environs.Environ // the provider this agent operates against. | |
23 » State *state.State | |
24 | |
25 » providerIdToInstance map[string]environs.Instance | |
26 » machineIdToProviderId map[int]string | |
27 } | |
28 | |
29 func NewProvisioningAgent() *ProvisioningAgent { | |
30 » return &ProvisioningAgent{ | |
31 » » providerIdToInstance: make(map[string]environs.Instance), | |
32 » » machineIdToProviderId: make(map[int]string), | |
33 » } | |
12 } | 34 } |
13 | 35 |
14 // Info returns usage information for the command. | 36 // Info returns usage information for the command. |
15 func (a *ProvisioningAgent) Info() *cmd.Info { | 37 func (a *ProvisioningAgent) Info() *cmd.Info { |
16 return &cmd.Info{"provisioning", "", "run a juju provisioning agent", "" } | 38 return &cmd.Info{"provisioning", "", "run a juju provisioning agent", "" } |
17 } | 39 } |
18 | 40 |
19 // Init initializes the command for running. | 41 // Init initializes the command for running. |
20 func (a *ProvisioningAgent) Init(f *gnuflag.FlagSet, args []string) error { | 42 func (a *ProvisioningAgent) Init(f *gnuflag.FlagSet, args []string) error { |
21 a.Conf.addFlags(f) | 43 a.Conf.addFlags(f) |
22 if err := f.Parse(true, args); err != nil { | 44 if err := f.Parse(true, args); err != nil { |
23 return err | 45 return err |
24 } | 46 } |
25 return a.Conf.checkArgs(f.Args()) | 47 return a.Conf.checkArgs(f.Args()) |
26 } | 48 } |
27 | 49 |
28 // Run runs a provisioning agent. | 50 // Run runs a provisioning agent. |
29 func (a *ProvisioningAgent) Run(_ *cmd.Context) error { | 51 func (a *ProvisioningAgent) Run(_ *cmd.Context) error { |
30 » return fmt.Errorf("MachineAgent.Run not implemented") | 52 » var err error |
53 » a.State, err = state.Open(&a.Conf.StateInfo) | |
niemeyer
2012/05/30 21:27:20
This is a rather extensive function that might be
| |
54 » if err != nil { | |
55 » » return err | |
56 » } | |
57 | |
58 » // step 1. wait for a valid environment | |
59 » configWatcher := a.State.WatchEnvironConfig() | |
60 » for { | |
61 » » log.Printf("provisioning: waiting for valid environment") | |
62 » » config, ok := <-configWatcher.Changes() | |
63 » » if !ok { | |
64 » » » return fmt.Errorf("environment watcher has shutdown: %v" , configWatcher.Stop()) | |
65 » » } | |
66 » » var err error | |
67 » » a.environ, err = environs.NewEnviron(config.Map()) | |
68 » » if err == nil { | |
69 » » » break | |
70 » » } | |
71 » » log.Printf("provisioning: unable to create environment from supp lied configuration: %v", err) | |
72 » } | |
73 » log.Printf("provisioning: valid environment configured") | |
74 | |
75 » // step 2. listen for changes to the environment or the machine topology and action both. | |
niemeyer
2012/05/30 21:27:20
Step 1 needs to be able to be more resilient, and
dfc
2012/05/31 07:10:01
Understood, will redo.
On 2012/05/30 21:27:20, ni
| |
76 » machinesWatcher := a.State.WatchMachines() | |
77 » for { | |
78 » » select { | |
79 » » case changes, ok := <-configWatcher.Changes(): | |
80 » » » if !ok { | |
81 » » » » return fmt.Errorf("environment watcher has shutd own: %v", configWatcher.Stop()) | |
82 » » » } | |
83 » » » config, err := environs.NewConfig(changes.Map()) | |
84 » » » if err != nil { | |
85 » » » » log.Printf("provisioning: new configuration rece ived, but was not valid: %v", err) | |
86 » » » » continue | |
87 » » » } | |
88 » » » a.environ.SetConfig(config) | |
89 » » » log.Printf("provisioning: new configuartion applied") | |
90 » » case changes, ok := <-machinesWatcher.Changes(): | |
91 » » » if !ok { | |
92 » » » » return fmt.Errorf("machines watcher has shutdown : %v", configWatcher.Stop()) | |
93 » » » } | |
94 » » » for _, added := range changes.Added { | |
95 » » » » if err := a.addMachine(added); err != nil { | |
96 » » » » » // TODO(dfc) implement retry logic | |
97 » » » » » return err | |
98 » » » » } | |
99 » » » » log.Printf("provisioning: machine %d added", add ed.Id()) | |
100 » » » } | |
101 » » » for _, deleted := range changes.Deleted { | |
102 » » » » if err := a.deleteMachine(deleted); err != nil { | |
103 » » » » » // TODO(dfc) implement retry logic | |
104 » » » » » return err | |
105 » » » » } | |
106 » » » » log.Printf("provisioning: machine %d deleted", d eleted.Id()) | |
107 » » » } | |
108 » » } | |
109 » } | |
110 » if err = configWatcher.Stop(); err == nil { | |
111 » » err = machinesWatcher.Stop() | |
112 » } | |
113 » return err | |
31 } | 114 } |
115 | |
116 func (a *ProvisioningAgent) addMachine(m *state.Machine) error { | |
117 id, err := m.InstanceId() | |
118 if err != nil { | |
119 return err | |
120 } | |
121 if id != "" { | |
122 return fmt.Errorf("machine-%010d already reports a provider id % q, skipping", m.Id(), id) | |
niemeyer
2012/05/30 21:27:20
We should never talk about internal ZooKeeper keys
dfc
2012/05/31 07:10:01
I think it is time that state.Machine grows a Stri
| |
123 } | |
124 | |
125 // TODO(dfc) the state.Info passed to environ.StartInstance remains cont entious | |
126 // however as the PA only knows one state.Info, and that info is used by MAs and· | |
127 // UAs to locate the ZK for this environment, it is logical to use the s ame· | |
128 // state.Info as the PA.· | |
129 inst, err := a.environ.StartInstance(m.Id(), &a.Conf.StateInfo) | |
130 if err != nil { | |
131 return err | |
132 } | |
133 | |
134 // store the reference from the provider in ZK | |
niemeyer
2012/05/30 21:27:20
Let's please not reference ZooKeeper all the time.
dfc
2012/05/31 07:10:01
Done.
| |
135 if err := m.SetInstanceId(inst.Id()); err != nil { | |
136 return fmt.Errorf("unable to store provider id: %v", err) | |
137 } | |
138 | |
139 // data is stashed in ZK, populate the caches | |
140 a.machineIdToProviderId[m.Id()] = inst.Id() | |
141 a.providerIdToInstance[inst.Id()] = inst | |
142 return nil | |
143 } | |
144 | |
145 func (a *ProvisioningAgent) deleteMachine(m *state.Machine) error { | |
146 insts, err := a.InstancesForMachines(m) | |
147 if err != nil { | |
148 return fmt.Errorf("machine-%010d has no refrence to a provider i d, skipping", m.Id()) | |
149 } | |
150 return a.environ.StopInstances(insts) | |
151 } | |
152 | |
153 // InstanceForMachine returns the environs.Instance that represents this machine s' running | |
154 // instance. | |
155 func (a *ProvisioningAgent) InstanceForMachine(m *state.Machine) (environs.Insta nce, error) { | |
156 id, ok := a.machineIdToProviderId[m.Id()] | |
157 if !ok { | |
158 // not cached locally, ask the provider. | |
159 var err error | |
160 id, err = m.InstanceId() | |
161 if err != nil { | |
162 return nil, err | |
163 } | |
164 if id == "" { | |
165 // nobody knows about this machine, give up. | |
166 return nil, fmt.Errorf("instance not found") | |
167 } | |
168 a.machineIdToProviderId[m.Id()] = id | |
169 } | |
170 inst, ok := a.providerIdToInstance[id] | |
171 if !ok { | |
172 // not cached locally, ask the provider | |
173 var err error | |
174 inst, err = a.findInstance(id) | |
175 if err != nil { | |
176 // the provider doesn't know about this instance, give u p. | |
177 return nil, err | |
178 } | |
179 return nil, nil | |
180 } | |
181 return inst, nil | |
182 } | |
183 | |
184 // InstancesForMachines returns a list of environs.Instance that represent the l ist of machines running | |
185 // in the provider. | |
186 func (a *ProvisioningAgent) InstancesForMachines(machines ...*state.Machine) ([] environs.Instance, error) { | |
187 var insts []environs.Instance | |
188 for _, m := range machines { | |
189 inst, err := a.InstanceForMachine(m) | |
190 if err != nil { | |
191 return nil, err | |
192 } | |
193 insts = append(insts, inst) | |
194 } | |
195 return insts, nil | |
196 } | |
197 | |
198 func (a *ProvisioningAgent) findInstance(id string) (environs.Instance, error) { | |
199 insts, err := a.environ.Instances([]string{id}) | |
200 if err != nil { | |
201 return nil, err | |
202 } | |
203 if len(insts) < 1 { | |
204 return nil, fmt.Errorf("instance not found") | |
205 } | |
206 return insts[0], nil | |
207 } | |
OLD | NEW |