github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/api/uniter/uniter.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter 5 6 import ( 7 "fmt" 8 9 "github.com/juju/errors" 10 "gopkg.in/juju/charm.v6-unstable" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/api/base" 14 "github.com/juju/juju/api/common" 15 apiwatcher "github.com/juju/juju/api/watcher" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/network" 18 "github.com/juju/juju/watcher" 19 ) 20 21 const uniterFacade = "Uniter" 22 23 // State provides access to the Uniter API facade. 24 type State struct { 25 *common.ModelWatcher 26 *common.APIAddresser 27 *StorageAccessor 28 29 LeadershipSettings *LeadershipSettingsAccessor 30 facade base.FacadeCaller 31 // unitTag contains the authenticated unit's tag. 32 unitTag names.UnitTag 33 } 34 35 // newStateForVersion creates a new client-side Uniter facade for the 36 // given version. 37 func newStateForVersion( 38 caller base.APICaller, 39 authTag names.UnitTag, 40 version int, 41 ) *State { 42 facadeCaller := base.NewFacadeCallerForVersion( 43 caller, 44 uniterFacade, 45 version, 46 ) 47 state := &State{ 48 ModelWatcher: common.NewModelWatcher(facadeCaller), 49 APIAddresser: common.NewAPIAddresser(facadeCaller), 50 StorageAccessor: NewStorageAccessor(facadeCaller), 51 facade: facadeCaller, 52 unitTag: authTag, 53 } 54 55 newWatcher := func(result params.NotifyWatchResult) watcher.NotifyWatcher { 56 return apiwatcher.NewNotifyWatcher(caller, result) 57 } 58 state.LeadershipSettings = NewLeadershipSettingsAccessor( 59 facadeCaller.FacadeCall, 60 newWatcher, 61 ErrIfNotVersionFn(2, state.BestAPIVersion()), 62 ) 63 return state 64 } 65 66 func newStateForVersionFn(version int) func(base.APICaller, names.UnitTag) *State { 67 return func(caller base.APICaller, authTag names.UnitTag) *State { 68 return newStateForVersion(caller, authTag, version) 69 } 70 } 71 72 // newStateV4 creates a new client-side Uniter facade, version 4. 73 var newStateV4 = newStateForVersionFn(4) 74 75 // NewState creates a new client-side Uniter facade. 76 // Defined like this to allow patching during tests. 77 var NewState = newStateV4 78 79 // BestAPIVersion returns the API version that we were able to 80 // determine is supported by both the client and the API Server. 81 func (st *State) BestAPIVersion() int { 82 return st.facade.BestAPIVersion() 83 } 84 85 // Facade returns the current facade. 86 func (st *State) Facade() base.FacadeCaller { 87 return st.facade 88 } 89 90 // life requests the lifecycle of the given entity from the server. 91 func (st *State) life(tag names.Tag) (params.Life, error) { 92 return common.Life(st.facade, tag) 93 } 94 95 // relation requests relation information from the server. 96 func (st *State) relation(relationTag, unitTag names.Tag) (params.RelationResult, error) { 97 nothing := params.RelationResult{} 98 var result params.RelationResults 99 args := params.RelationUnits{ 100 RelationUnits: []params.RelationUnit{ 101 {Relation: relationTag.String(), Unit: unitTag.String()}, 102 }, 103 } 104 err := st.facade.FacadeCall("Relation", args, &result) 105 if err != nil { 106 return nothing, err 107 } 108 if len(result.Results) != 1 { 109 return nothing, fmt.Errorf("expected 1 result, got %d", len(result.Results)) 110 } 111 if err := result.Results[0].Error; err != nil { 112 return nothing, err 113 } 114 return result.Results[0], nil 115 } 116 117 // getOneAction retrieves a single Action from the controller. 118 func (st *State) getOneAction(tag *names.ActionTag) (params.ActionResult, error) { 119 nothing := params.ActionResult{} 120 121 args := params.Entities{ 122 Entities: []params.Entity{ 123 {Tag: tag.String()}, 124 }, 125 } 126 127 var results params.ActionResults 128 err := st.facade.FacadeCall("Actions", args, &results) 129 if err != nil { 130 return nothing, err 131 } 132 133 if len(results.Results) > 1 { 134 return nothing, fmt.Errorf("expected only 1 action query result, got %d", len(results.Results)) 135 } 136 137 // handle server errors 138 result := results.Results[0] 139 if err := result.Error; err != nil { 140 return nothing, err 141 } 142 143 return result, nil 144 } 145 146 // Unit provides access to methods of a state.Unit through the facade. 147 func (st *State) Unit(tag names.UnitTag) (*Unit, error) { 148 life, err := st.life(tag) 149 if err != nil { 150 return nil, err 151 } 152 return &Unit{ 153 tag: tag, 154 life: life, 155 st: st, 156 }, nil 157 } 158 159 // Application returns an application state by tag. 160 func (st *State) Application(tag names.ApplicationTag) (*Application, error) { 161 life, err := st.life(tag) 162 if err != nil { 163 return nil, err 164 } 165 return &Application{ 166 tag: tag, 167 life: life, 168 st: st, 169 }, nil 170 } 171 172 // ProviderType returns a provider type used by the current juju model. 173 // 174 // TODO(dimitern): We might be able to drop this, once we have machine 175 // addresses implemented fully. See also LP bug 1221798. 176 func (st *State) ProviderType() (string, error) { 177 var result params.StringResult 178 err := st.facade.FacadeCall("ProviderType", nil, &result) 179 if err != nil { 180 return "", err 181 } 182 if err := result.Error; err != nil { 183 return "", err 184 } 185 return result.Result, nil 186 } 187 188 // Charm returns the charm with the given URL. 189 func (st *State) Charm(curl *charm.URL) (*Charm, error) { 190 if curl == nil { 191 return nil, fmt.Errorf("charm url cannot be nil") 192 } 193 return &Charm{ 194 st: st, 195 curl: curl, 196 }, nil 197 } 198 199 // Relation returns the existing relation with the given tag. 200 func (st *State) Relation(relationTag names.RelationTag) (*Relation, error) { 201 result, err := st.relation(relationTag, st.unitTag) 202 if err != nil { 203 return nil, err 204 } 205 return &Relation{ 206 id: result.Id, 207 tag: relationTag, 208 life: result.Life, 209 st: st, 210 }, nil 211 } 212 213 // Action returns the Action with the given tag. 214 func (st *State) Action(tag names.ActionTag) (*Action, error) { 215 result, err := st.getOneAction(&tag) 216 if err != nil { 217 return nil, err 218 } 219 return &Action{ 220 name: result.Action.Name, 221 params: result.Action.Parameters, 222 }, nil 223 } 224 225 // ActionBegin marks an action as running. 226 func (st *State) ActionBegin(tag names.ActionTag) error { 227 var outcome params.ErrorResults 228 229 args := params.Entities{ 230 Entities: []params.Entity{ 231 {Tag: tag.String()}, 232 }, 233 } 234 235 err := st.facade.FacadeCall("BeginActions", args, &outcome) 236 if err != nil { 237 return err 238 } 239 if len(outcome.Results) != 1 { 240 return fmt.Errorf("expected 1 result, got %d", len(outcome.Results)) 241 } 242 result := outcome.Results[0] 243 if result.Error != nil { 244 return result.Error 245 } 246 return nil 247 } 248 249 // ActionFinish captures the structured output of an action. 250 func (st *State) ActionFinish(tag names.ActionTag, status string, results map[string]interface{}, message string) error { 251 var outcome params.ErrorResults 252 253 args := params.ActionExecutionResults{ 254 Results: []params.ActionExecutionResult{ 255 { 256 ActionTag: tag.String(), 257 Status: status, 258 Results: results, 259 Message: message, 260 }, 261 }, 262 } 263 264 err := st.facade.FacadeCall("FinishActions", args, &outcome) 265 if err != nil { 266 return err 267 } 268 if len(outcome.Results) != 1 { 269 return fmt.Errorf("expected 1 result, got %d", len(outcome.Results)) 270 } 271 result := outcome.Results[0] 272 if result.Error != nil { 273 return result.Error 274 } 275 return nil 276 } 277 278 // RelationById returns the existing relation with the given id. 279 func (st *State) RelationById(id int) (*Relation, error) { 280 var results params.RelationResults 281 args := params.RelationIds{ 282 RelationIds: []int{id}, 283 } 284 err := st.facade.FacadeCall("RelationById", args, &results) 285 if err != nil { 286 return nil, err 287 } 288 if len(results.Results) != 1 { 289 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 290 } 291 result := results.Results[0] 292 if err := result.Error; err != nil { 293 return nil, err 294 } 295 relationTag := names.NewRelationTag(result.Key) 296 return &Relation{ 297 id: result.Id, 298 tag: relationTag, 299 life: result.Life, 300 st: st, 301 }, nil 302 } 303 304 // Model returns the model entity. 305 func (st *State) Model() (*Model, error) { 306 var result params.ModelResult 307 err := st.facade.FacadeCall("CurrentModel", nil, &result) 308 if err != nil { 309 return nil, err 310 } 311 if err := result.Error; err != nil { 312 return nil, err 313 } 314 return &Model{ 315 name: result.Name, 316 uuid: result.UUID, 317 }, nil 318 } 319 320 // AllMachinePorts returns all port ranges currently open on the given 321 // machine, mapped to the tags of the unit that opened them and the 322 // relation that applies. 323 func (st *State) AllMachinePorts(machineTag names.MachineTag) (map[network.PortRange]params.RelationUnit, error) { 324 if st.BestAPIVersion() < 1 { 325 // AllMachinePorts() was introduced in UniterAPIV1. 326 return nil, errors.NotImplementedf("AllMachinePorts() (need V1+)") 327 } 328 var results params.MachinePortsResults 329 args := params.Entities{ 330 Entities: []params.Entity{{Tag: machineTag.String()}}, 331 } 332 err := st.facade.FacadeCall("AllMachinePorts", args, &results) 333 if err != nil { 334 return nil, err 335 } 336 if len(results.Results) != 1 { 337 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 338 } 339 result := results.Results[0] 340 if result.Error != nil { 341 return nil, result.Error 342 } 343 portsMap := make(map[network.PortRange]params.RelationUnit) 344 for _, ports := range result.Ports { 345 portRange := ports.PortRange.NetworkPortRange() 346 portsMap[portRange] = params.RelationUnit{ 347 Unit: ports.UnitTag, 348 Relation: ports.RelationTag, 349 } 350 } 351 return portsMap, nil 352 } 353 354 // WatchRelationUnits returns a watcher that notifies of changes to the 355 // counterpart units in the relation for the given unit. 356 func (st *State) WatchRelationUnits( 357 relationTag names.RelationTag, 358 unitTag names.UnitTag, 359 ) (watcher.RelationUnitsWatcher, error) { 360 var results params.RelationUnitsWatchResults 361 args := params.RelationUnits{ 362 RelationUnits: []params.RelationUnit{{ 363 Relation: relationTag.String(), 364 Unit: unitTag.String(), 365 }}, 366 } 367 err := st.facade.FacadeCall("WatchRelationUnits", args, &results) 368 if err != nil { 369 return nil, err 370 } 371 if len(results.Results) != 1 { 372 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 373 } 374 result := results.Results[0] 375 if result.Error != nil { 376 return nil, result.Error 377 } 378 w := apiwatcher.NewRelationUnitsWatcher(st.facade.RawAPICaller(), result) 379 return w, nil 380 } 381 382 // ErrIfNotVersionFn returns a function which can be used to check for 383 // the minimum supported version, and, if appropriate, generate an 384 // error. 385 func ErrIfNotVersionFn(minVersion int, bestAPIVersion int) func(string) error { 386 return func(fnName string) error { 387 if minVersion <= bestAPIVersion { 388 return nil 389 } 390 return errors.NotImplementedf("%s(...) requires v%d+", fnName, minVersion) 391 } 392 }