Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(97)

Side by Side Diff: state/state.go

Issue 6296064: state: Second approach to improve the error handling. (Closed)
Patch Set: state: Second approach to improve the error handling. Created 12 years, 9 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« no previous file with comments | « state/internal_test.go ('k') | state/state_test.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // The state package enables reading, observing, and changing 1 // The state package enables reading, observing, and changing
2 // the state stored in ZooKeeper of a whole environment 2 // the state stored in ZooKeeper of a whole environment
3 // managed by juju. 3 // managed by juju.
4 package state 4 package state
5 5
6 import ( 6 import (
7 "fmt" 7 "fmt"
8 "launchpad.net/goyaml" 8 "launchpad.net/goyaml"
9 "launchpad.net/gozk/zookeeper" 9 "launchpad.net/gozk/zookeeper"
10 "launchpad.net/juju-core/juju/charm" 10 "launchpad.net/juju-core/juju/charm"
11 "net/url" 11 "net/url"
12 "strings" 12 "strings"
13 ) 13 )
14 14
15 const ( 15 const (
16 zkEnvironmentPath = "/environment" 16 zkEnvironmentPath = "/environment"
17 zkMachinesPath = "/machines" 17 zkMachinesPath = "/machines"
18 zkTopologyPath = "/topology" 18 zkTopologyPath = "/topology"
19 ) 19 )
20 20
21 // State represents the state of an environment 21 // State represents the state of an environment
22 // managed by juju. 22 // managed by juju.
23 type State struct { 23 type State struct {
24 zk *zookeeper.Conn 24 zk *zookeeper.Conn
25 fwd *sshForwarder 25 fwd *sshForwarder
26 } 26 }
27 27
28 // AddMachine creates a new machine state. 28 // AddMachine creates a new machine state.
29 func (s *State) AddMachine() (*Machine, error) { 29 func (s *State) AddMachine() (m *Machine, err error) {
30 » path, err := s.zk.Create("/machines/machine-", "", zookeeper.SEQUENCE, z kPermAll) 30 » defer errorContext(&err, "can't add a new machine: %v")
31 » if err != nil { 31 » var path string
32 » » return nil, err 32 » if path, err = s.zk.Create("/machines/machine-", "", zookeeper.SEQUENCE, zkPermAll); err != nil {
33 » » return
33 } 34 }
34 key := strings.Split(path, "/")[2] 35 key := strings.Split(path, "/")[2]
35 addMachine := func(t *topology) error { 36 addMachine := func(t *topology) error {
36 return t.AddMachine(key) 37 return t.AddMachine(key)
37 } 38 }
38 if err = retryTopologyChange(s.zk, addMachine); err != nil { 39 if err = retryTopologyChange(s.zk, addMachine); err != nil {
39 » » return nil, err 40 » » return
40 } 41 }
41 return &Machine{s, key}, nil 42 return &Machine{s, key}, nil
42 } 43 }
43 44
44 // RemoveMachine removes the machine with the given id. 45 // RemoveMachine removes the machine with the given id.
45 func (s *State) RemoveMachine(id int) error { 46 func (s *State) RemoveMachine(id int) (err error) {
47 » defer errorContext(&err, "can't remove machine %d: %v", id)
46 key := machineKey(id) 48 key := machineKey(id)
47 removeMachine := func(t *topology) error { 49 removeMachine := func(t *topology) error {
48 if !t.HasMachine(key) { 50 if !t.HasMachine(key) {
49 return fmt.Errorf("machine not found") 51 return fmt.Errorf("machine not found")
50 } 52 }
51 hasUnits, err := t.MachineHasUnits(key) 53 hasUnits, err := t.MachineHasUnits(key)
52 if err != nil { 54 if err != nil {
53 return err 55 return err
54 } 56 }
55 if hasUnits { 57 if hasUnits {
56 return fmt.Errorf("machine has units") 58 return fmt.Errorf("machine has units")
57 } 59 }
58 return t.RemoveMachine(key) 60 return t.RemoveMachine(key)
59 } 61 }
60 » if err := retryTopologyChange(s.zk, removeMachine); err != nil { 62 » if err = retryTopologyChange(s.zk, removeMachine); err != nil {
61 » » return fmt.Errorf("can't remove machine %d: %v", id, err) 63 » » return
62 } 64 }
63 return zkRemoveTree(s.zk, fmt.Sprintf("/machines/%s", key)) 65 return zkRemoveTree(s.zk, fmt.Sprintf("/machines/%s", key))
64 } 66 }
65 67
66 // WatchMachines watches for new Machines added or removed. 68 // WatchMachines watches for new Machines added or removed.
67 func (s *State) WatchMachines() *MachinesWatcher { 69 func (s *State) WatchMachines() *MachinesWatcher {
68 return newMachinesWatcher(s) 70 return newMachinesWatcher(s)
69 } 71 }
70 72
71 // WatchEnvironConfig returns a watcher for observing 73 // WatchEnvironConfig returns a watcher for observing
72 // changes to the environment configuration. 74 // changes to the environment configuration.
73 func (s *State) WatchEnvironConfig() *ConfigWatcher { 75 func (s *State) WatchEnvironConfig() *ConfigWatcher {
74 return newConfigWatcher(s, zkEnvironmentPath) 76 return newConfigWatcher(s, zkEnvironmentPath)
75 } 77 }
76 78
77 // EnvironConfig returns the current configuration of the environment. 79 // EnvironConfig returns the current configuration of the environment.
78 func (s *State) EnvironConfig() (*ConfigNode, error) { 80 func (s *State) EnvironConfig() (*ConfigNode, error) {
79 return readConfigNode(s.zk, zkEnvironmentPath) 81 return readConfigNode(s.zk, zkEnvironmentPath)
80 } 82 }
81 83
82 // Machine returns the machine with the given id. 84 // Machine returns the machine with the given id.
83 func (s *State) Machine(id int) (*Machine, error) { 85 func (s *State) Machine(id int) (*Machine, error) {
84 key := machineKey(id) 86 key := machineKey(id)
85 topology, err := readTopology(s.zk) 87 topology, err := readTopology(s.zk)
86 if err != nil { 88 if err != nil {
87 » » return nil, err 89 » » return nil, fmt.Errorf("can't get machine %d: %v", id, err)
88 } 90 }
89 if !topology.HasMachine(key) { 91 if !topology.HasMachine(key) {
90 return nil, fmt.Errorf("machine %d not found", id) 92 return nil, fmt.Errorf("machine %d not found", id)
91 } 93 }
92 return &Machine{s, key}, nil 94 return &Machine{s, key}, nil
93 } 95 }
94 96
95 // AllMachines returns all machines in the environment. 97 // AllMachines returns all machines in the environment.
96 func (s *State) AllMachines() ([]*Machine, error) { 98 func (s *State) AllMachines() ([]*Machine, error) {
97 topology, err := readTopology(s.zk) 99 topology, err := readTopology(s.zk)
98 if err != nil { 100 if err != nil {
99 » » return nil, err 101 » » return nil, fmt.Errorf("can't get all machines: %v", err)
100 } 102 }
101 machines := []*Machine{} 103 machines := []*Machine{}
102 for _, key := range topology.MachineKeys() { 104 for _, key := range topology.MachineKeys() {
103 machines = append(machines, &Machine{s, key}) 105 machines = append(machines, &Machine{s, key})
104 } 106 }
105 return machines, nil 107 return machines, nil
106 } 108 }
107 109
108 // AddCharm adds the ch charm with curl to the state. 110 // AddCharm adds the ch charm with curl to the state.
109 // bundleUrl must be set to a URL where the bundle for ch 111 // bundleUrl must be set to a URL where the bundle for ch
110 // may be downloaded from. 112 // may be downloaded from.
111 // On success the newly added charm state is returned. 113 // On success the newly added charm state is returned.
112 func (s *State) AddCharm(ch charm.Charm, curl *charm.URL, bundleURL *url.URL, bu ndleSha256 string) (*Charm, error) { 114 func (s *State) AddCharm(ch charm.Charm, curl *charm.URL, bundleURL *url.URL, bu ndleSha256 string) (stch *Charm, err error) {
115 » defer errorContext(&err, "can't add charm %q: %v", curl)
113 data := &charmData{ 116 data := &charmData{
114 Meta: ch.Meta(), 117 Meta: ch.Meta(),
115 Config: ch.Config(), 118 Config: ch.Config(),
116 BundleURL: bundleURL.String(), 119 BundleURL: bundleURL.String(),
117 BundleSha256: bundleSha256, 120 BundleSha256: bundleSha256,
118 } 121 }
119 yaml, err := goyaml.Marshal(data) 122 yaml, err := goyaml.Marshal(data)
120 if err != nil { 123 if err != nil {
121 » » return nil, err 124 » » return
122 } 125 }
123 path, err := charmPath(curl) 126 path, err := charmPath(curl)
124 if err != nil { 127 if err != nil {
125 » » return nil, err 128 » » return
126 } 129 }
127 _, err = s.zk.Create(path, string(yaml), 0, zkPermAll) 130 _, err = s.zk.Create(path, string(yaml), 0, zkPermAll)
128 if err != nil { 131 if err != nil {
129 » » return nil, err 132 » » return
130 } 133 }
131 » return newCharm(s, curl, data) 134 » stch, err = newCharm(s, curl, data)
niemeyer 2012/06/13 12:32:38 Please revert. The original version is fine.
TheMue 2012/06/13 12:54:34 Done.
135 » if err != nil {
136 » » return
137 » }
138 » return stch, nil
132 } 139 }
133 140
134 // Charm returns a charm by the given id. 141 // Charm returns a charm by the given id.
135 func (s *State) Charm(curl *charm.URL) (*Charm, error) { 142 func (s *State) Charm(curl *charm.URL) (stch *Charm, err error) {
143 » defer errorContext(&err, "can't get charm %q: %v", curl)
136 path, err := charmPath(curl) 144 path, err := charmPath(curl)
137 if err != nil { 145 if err != nil {
138 » » return nil, err 146 » » return
139 } 147 }
140 yaml, _, err := s.zk.Get(path) 148 yaml, _, err := s.zk.Get(path)
141 if zookeeper.IsError(err, zookeeper.ZNONODE) { 149 if zookeeper.IsError(err, zookeeper.ZNONODE) {
142 » » return nil, fmt.Errorf("charm not found: %q", curl) 150 » » return
143 } 151 }
144 if err != nil { 152 if err != nil {
145 » » return nil, err 153 » » return
146 } 154 }
147 data := &charmData{} 155 data := &charmData{}
148 » if err := goyaml.Unmarshal([]byte(yaml), data); err != nil { 156 » if err = goyaml.Unmarshal([]byte(yaml), data); err != nil {
149 » » return nil, err 157 » » return
150 } 158 }
151 » return newCharm(s, curl, data) 159 » stch, err = newCharm(s, curl, data)
niemeyer 2012/06/13 12:32:38 Ditto.
TheMue 2012/06/13 12:54:34 Done.
160 » if err != nil {
161 » » return
162 » }
163 » return stch, nil
152 } 164 }
153 165
154 // AddService creates a new service state with the given unique name 166 // AddService creates a new service state with the given unique name
155 // and the charm state. 167 // and the charm state.
156 func (s *State) AddService(name string, ch *Charm) (*Service, error) { 168 func (s *State) AddService(name string, ch *Charm) (service *Service, err error) {
169 » defer errorContext(&err, "can't add service %q: %v", name)
157 details := map[string]interface{}{"charm": ch.URL().String()} 170 details := map[string]interface{}{"charm": ch.URL().String()}
158 yaml, err := goyaml.Marshal(details) 171 yaml, err := goyaml.Marshal(details)
159 if err != nil { 172 if err != nil {
160 » » return nil, err 173 » » return
161 } 174 }
162 path, err := s.zk.Create("/services/service-", string(yaml), zookeeper.S EQUENCE, zkPermAll) 175 path, err := s.zk.Create("/services/service-", string(yaml), zookeeper.S EQUENCE, zkPermAll)
163 if err != nil { 176 if err != nil {
164 » » return nil, err 177 » » return
165 } 178 }
166 key := strings.Split(path, "/")[2] 179 key := strings.Split(path, "/")[2]
167 » service := &Service{s, key, name} 180 » service = &Service{s, key, name}
168 // Create an empty configuration node. 181 // Create an empty configuration node.
169 _, err = createConfigNode(s.zk, service.zkConfigPath(), map[string]inter face{}{}) 182 _, err = createConfigNode(s.zk, service.zkConfigPath(), map[string]inter face{}{})
170 if err != nil { 183 if err != nil {
171 » » return nil, err 184 » » return
niemeyer 2012/06/13 12:32:38 Please revert all of these changes. The original "
TheMue 2012/06/13 12:54:34 Done.
172 } 185 }
173 // Create a parent node for the service units 186 // Create a parent node for the service units
174 _, err = s.zk.Create(service.zkPath()+"/units", "", 0, zkPermAll) 187 _, err = s.zk.Create(service.zkPath()+"/units", "", 0, zkPermAll)
175 if err != nil { 188 if err != nil {
176 » » return nil, err 189 » » return
177 } 190 }
178 addService := func(t *topology) error { 191 addService := func(t *topology) error {
179 if _, err := t.ServiceKey(name); err == nil { 192 if _, err := t.ServiceKey(name); err == nil {
180 // No error, so service name already in use. 193 // No error, so service name already in use.
181 » » » return fmt.Errorf("service name %q is already in use", n ame) 194 » » » return fmt.Errorf("service name is already in use")
182 } 195 }
183 return t.AddService(key, name) 196 return t.AddService(key, name)
184 } 197 }
185 if err = retryTopologyChange(s.zk, addService); err != nil { 198 if err = retryTopologyChange(s.zk, addService); err != nil {
186 » » return nil, err 199 » » return
187 } 200 }
188 return service, nil 201 return service, nil
189 } 202 }
190 203
191 // RemoveService removes a service from the state. It will 204 // RemoveService removes a service from the state. It will
192 // also remove all its units and break any of its existing 205 // also remove all its units and break any of its existing
193 // relations. 206 // relations.
194 func (s *State) RemoveService(svc *Service) error { 207 func (s *State) RemoveService(svc *Service) (err error) {
208 » defer errorContext(&err, "can't remove service %q: %v", svc.Name())
195 // TODO Remove relations first, to prevent spurious hook execution. 209 // TODO Remove relations first, to prevent spurious hook execution.
196 210
197 // Remove the units. 211 // Remove the units.
198 units, err := svc.AllUnits() 212 units, err := svc.AllUnits()
199 if err != nil { 213 if err != nil {
200 » » return err 214 » » return
201 } 215 }
202 for _, unit := range units { 216 for _, unit := range units {
203 if err = svc.RemoveUnit(unit); err != nil { 217 if err = svc.RemoveUnit(unit); err != nil {
204 » » » return err 218 » » » return fmt.Errorf("can't remove unit %q: %v", unit.Name( ), err)
niemeyer 2012/06/13 12:32:38 No need to adorn it further. RemoveUnit itself wil
TheMue 2012/06/13 12:54:34 Done.
205 } 219 }
206 } 220 }
207 // Remove the service from the topology. 221 // Remove the service from the topology.
208 removeService := func(t *topology) error { 222 removeService := func(t *topology) error {
209 if !t.HasService(svc.key) { 223 if !t.HasService(svc.key) {
210 return stateChanged 224 return stateChanged
211 } 225 }
212 t.RemoveService(svc.key) 226 t.RemoveService(svc.key)
213 return nil 227 return nil
214 } 228 }
215 if err = retryTopologyChange(s.zk, removeService); err != nil { 229 if err = retryTopologyChange(s.zk, removeService); err != nil {
216 » » return err 230 » » return
217 } 231 }
218 » return zkRemoveTree(s.zk, svc.zkPath()) 232 » if err = zkRemoveTree(s.zk, svc.zkPath()); err != nil {
niemeyer 2012/06/13 12:32:38 Please revert.
TheMue 2012/06/13 12:54:34 Done.
233 » » return
234 » }
235 » return nil
219 } 236 }
220 237
221 // Service returns a service state by name. 238 // Service returns a service state by name.
222 func (s *State) Service(name string) (*Service, error) { 239 func (s *State) Service(name string) (*Service, error) {
223 topology, err := readTopology(s.zk) 240 topology, err := readTopology(s.zk)
224 if err != nil { 241 if err != nil {
225 » » return nil, err 242 » » return nil, fmt.Errorf("can't get service %q: %v", name, err)
226 } 243 }
227 key, err := topology.ServiceKey(name) 244 key, err := topology.ServiceKey(name)
228 if err != nil { 245 if err != nil {
229 return nil, err 246 return nil, err
niemeyer 2012/06/13 12:32:38 This error may come out as "can't find service key
TheMue 2012/06/13 12:54:34 Done.
230 } 247 }
231 return &Service{s, key, name}, nil 248 return &Service{s, key, name}, nil
232 } 249 }
233 250
234 // AllServices returns all deployed services in the environment. 251 // AllServices returns all deployed services in the environment.
235 func (s *State) AllServices() ([]*Service, error) { 252 func (s *State) AllServices() (services []*Service, err error) {
253 » defer errorContext(&err, "can't get all services: %v")
236 topology, err := readTopology(s.zk) 254 topology, err := readTopology(s.zk)
237 if err != nil { 255 if err != nil {
238 » » return nil, err 256 » » return
239 } 257 }
240 » services := []*Service{} 258 » services = []*Service{}
241 for _, key := range topology.ServiceKeys() { 259 for _, key := range topology.ServiceKeys() {
242 » » name, err := topology.ServiceName(key) 260 » » var name string
niemeyer 2012/06/13 12:32:38 Please revert and have "return nil, err" below.
TheMue 2012/06/13 12:54:34 Done.
261 » » name, err = topology.ServiceName(key)
243 if err != nil { 262 if err != nil {
244 » » » return nil, err 263 » » » return
245 } 264 }
246 services = append(services, &Service{s, key, name}) 265 services = append(services, &Service{s, key, name})
247 } 266 }
248 return services, nil 267 return services, nil
249 } 268 }
250 269
251 // Unit returns a unit by name. 270 // Unit returns a unit by name.
252 func (s *State) Unit(name string) (*Unit, error) { 271 func (s *State) Unit(name string) (unit *Unit, err error) {
272 » defer errorContext(&err, "can't get unit %q: %v", name)
253 serviceName, _, err := parseUnitName(name) 273 serviceName, _, err := parseUnitName(name)
254 if err != nil { 274 if err != nil {
255 » » return nil, err 275 » » return
256 } 276 }
257 service, err := s.Service(serviceName) 277 service, err := s.Service(serviceName)
258 if err != nil { 278 if err != nil {
259 » » return nil, err 279 » » return
260 } 280 }
261 return service.Unit(name) 281 return service.Unit(name)
262 } 282 }
263 283
264 // addRelationNode creates the relation node. 284 // addRelationNode creates the relation node.
265 func (s *State) addRelationNode(scope RelationScope) (string, error) { 285 func (s *State) addRelationNode(scope RelationScope) (string, error) {
266 path, err := s.zk.Create("/relations/relation-", "", zookeeper.SEQUENCE, zkPermAll) 286 path, err := s.zk.Create("/relations/relation-", "", zookeeper.SEQUENCE, zkPermAll)
267 if err != nil { 287 if err != nil {
268 return "", err 288 return "", err
269 } 289 }
270 relationKey := strings.Split(path, "/")[2] 290 relationKey := strings.Split(path, "/")[2]
271 // Create the settings node only if the scope is global. 291 // Create the settings node only if the scope is global.
272 // In case of container scoped relations the creation per 292 // In case of container scoped relations the creation per
273 // container occurs in ServiceRelation.AddUnit. 293 // container occurs in ServiceRelation.AddUnit.
274 if scope == ScopeGlobal { 294 if scope == ScopeGlobal {
275 _, err = s.zk.Create(path+"/settings", "", 0, zkPermAll) 295 _, err = s.zk.Create(path+"/settings", "", 0, zkPermAll)
276 if err != nil { 296 if err != nil {
277 return "", err 297 return "", err
278 } 298 }
279 } 299 }
280 return relationKey, nil 300 return relationKey, nil
281 } 301 }
282 302
283 // addRelationEndpointNode creates the endpoint role node below its relation nod e· 303 // addRelationEndpointNode creates the endpoint role node below its relation nod e·
284 // for the given relation endpoint. 304 // for the given relation endpoint.
285 func (s *State) addRelationEndpointNode(relationKey string, endpoint RelationEnd point) error { 305 func (s *State) addRelationEndpointNode(relationKey string, endpoint RelationEnd point) error {
286 path := fmt.Sprintf("/relations/%s/%s", relationKey, string(endpoint.Rel ationRole)) 306 path := fmt.Sprintf("/relations/%s/%s", relationKey, string(endpoint.Rel ationRole))
287 » _, err := s.zk.Create(path, "", 0, zkPermAll) 307 » if _, err := s.zk.Create(path, "", 0, zkPermAll); err != nil {
niemeyer 2012/06/13 12:32:38 Please revert.
TheMue 2012/06/13 12:54:34 Done.
288 » return err 308 » » return err
309 » }
310 » return nil
289 } 311 }
290 312
291 // AddRelation creates a new relation with the given endpoints.·· 313 // AddRelation creates a new relation with the given endpoints.··
292 func (s *State) AddRelation(endpoints ...RelationEndpoint) (*Relation, []*Servic eRelation, error) { 314 func (s *State) AddRelation(endpoints ...RelationEndpoint) (rel *Relation, svcre ls []*ServiceRelation, err error) {
315 » defer errorContext(&err, "can't add relation: %v")
293 switch len(endpoints) { 316 switch len(endpoints) {
294 case 1: 317 case 1:
295 if endpoints[0].RelationRole != RolePeer { 318 if endpoints[0].RelationRole != RolePeer {
296 » » » return nil, nil, fmt.Errorf("can't add non-peer relation with a single service") 319 » » » return nil, nil, fmt.Errorf("non-peer relation with a si ngle service")
297 } 320 }
298 case 2: 321 case 2:
299 if !endpoints[0].CanRelateTo(&endpoints[1]) { 322 if !endpoints[0].CanRelateTo(&endpoints[1]) {
300 » » » return nil, nil, fmt.Errorf("can't add relation between %s and %s", endpoints[0], endpoints[1]) 323 » » » return nil, nil, fmt.Errorf("relation between %s and %s is invalid", endpoints[0], endpoints[1])
301 } 324 }
302 default: 325 default:
303 » » return nil, nil, fmt.Errorf("can't add relations between %d serv ices", len(endpoints)) 326 » » return nil, nil, fmt.Errorf("relations between %d services are i nvalid", len(endpoints))
304 } 327 }
305 t, err := readTopology(s.zk) 328 t, err := readTopology(s.zk)
306 if err != nil { 329 if err != nil {
307 » » return nil, nil, err 330 » » return
308 } 331 }
309 // Check if the relation already exists. 332 // Check if the relation already exists.
310 relationKey, err := t.RelationKey(endpoints...) 333 relationKey, err := t.RelationKey(endpoints...)
311 if err != nil { 334 if err != nil {
312 if _, ok := err.(*NoRelationError); !ok { 335 if _, ok := err.(*NoRelationError); !ok {
313 » » » return nil, nil, err 336 » » » return
314 } 337 }
315 } 338 }
316 if relationKey != "" { 339 if relationKey != "" {
317 return nil, nil, fmt.Errorf("relation already exists") 340 return nil, nil, fmt.Errorf("relation already exists")
318 } 341 }
319 scope := ScopeGlobal 342 scope := ScopeGlobal
320 for _, endpoint := range endpoints { 343 for _, endpoint := range endpoints {
321 if endpoint.RelationScope == ScopeContainer { 344 if endpoint.RelationScope == ScopeContainer {
322 scope = ScopeContainer 345 scope = ScopeContainer
323 break 346 break
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
366 service := &topoRelationService{ 389 service := &topoRelationService{
367 RelationRole: serviceRelation.RelationRole(), 390 RelationRole: serviceRelation.RelationRole(),
368 RelationName: serviceRelation.RelationName(), 391 RelationName: serviceRelation.RelationName(),
369 } 392 }
370 relation.Services[serviceRelation.serviceKey] = service 393 relation.Services[serviceRelation.serviceKey] = service
371 } 394 }
372 return t.AddRelation(relationKey, relation) 395 return t.AddRelation(relationKey, relation)
373 } 396 }
374 err = retryTopologyChange(s.zk, addRelation) 397 err = retryTopologyChange(s.zk, addRelation)
375 if err != nil { 398 if err != nil {
376 » » return nil, nil, err 399 » » return
377 } 400 }
378 return &Relation{s, relationKey}, serviceRelations, nil 401 return &Relation{s, relationKey}, serviceRelations, nil
379 } 402 }
380 403
381 // RemoveRelation removes the relation. 404 // RemoveRelation removes the relation.
382 func (s *State) RemoveRelation(relation *Relation) error { 405 func (s *State) RemoveRelation(relation *Relation) error {
383 removeRelation := func(t *topology) error { 406 removeRelation := func(t *topology) error {
384 » » _, err := t.Relation(relation.key) 407 » » if _, err := t.Relation(relation.key); err != nil {
385 » » if err != nil { 408 » » » return err
386 » » » return fmt.Errorf("can't remove relation: %v", err)
387 } 409 }
388 return t.RemoveRelation(relation.key) 410 return t.RemoveRelation(relation.key)
389 } 411 }
390 » // TODO: Improve high-level errors, no passing of low-level· 412 » if err := retryTopologyChange(s.zk, removeRelation); err != nil {
391 » // errors directly to the caller. 413 » » return fmt.Errorf("can't remove relation: %v", err)
392 » return retryTopologyChange(s.zk, removeRelation) 414 » }
415 » return nil
393 } 416 }
OLDNEW
« no previous file with comments | « state/internal_test.go ('k') | state/state_test.go » ('j') | no next file with comments »

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld f62528b