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