github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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" 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/core/application" 18 "github.com/juju/juju/core/model" 19 corenetwork "github.com/juju/juju/core/network" 20 "github.com/juju/juju/core/relation" 21 "github.com/juju/juju/core/watcher" 22 ) 23 24 const uniterFacade = "Uniter" 25 26 // State provides access to the Uniter API facade. 27 type State struct { 28 *common.ModelWatcher 29 *common.APIAddresser 30 *common.UpgradeSeriesAPI 31 *LXDProfileAPI 32 *StorageAccessor 33 34 LeadershipSettings *LeadershipSettingsAccessor 35 facade base.FacadeCaller 36 // unitTag contains the authenticated unit's tag. 37 unitTag names.UnitTag 38 } 39 40 // newStateForVersion creates a new client-side Uniter facade for the 41 // given version. 42 func newStateForVersion( 43 caller base.APICaller, 44 authTag names.UnitTag, 45 version int, 46 ) *State { 47 facadeCaller := base.NewFacadeCallerForVersion( 48 caller, 49 uniterFacade, 50 version, 51 ) 52 state := &State{ 53 ModelWatcher: common.NewModelWatcher(facadeCaller), 54 APIAddresser: common.NewAPIAddresser(facadeCaller), 55 UpgradeSeriesAPI: common.NewUpgradeSeriesAPI(facadeCaller, authTag), 56 LXDProfileAPI: NewLXDProfileAPI(facadeCaller, authTag), 57 StorageAccessor: NewStorageAccessor(facadeCaller), 58 facade: facadeCaller, 59 unitTag: authTag, 60 } 61 62 newWatcher := func(result params.NotifyWatchResult) watcher.NotifyWatcher { 63 return apiwatcher.NewNotifyWatcher(caller, result) 64 } 65 state.LeadershipSettings = NewLeadershipSettingsAccessor( 66 facadeCaller.FacadeCall, 67 newWatcher, 68 ErrIfNotVersionFn(2, state.BestAPIVersion()), 69 ) 70 return state 71 } 72 73 func newStateForVersionFn(version int) func(base.APICaller, names.UnitTag) *State { 74 return func(caller base.APICaller, authTag names.UnitTag) *State { 75 return newStateForVersion(caller, authTag, version) 76 } 77 } 78 79 // newStateV9 creates a new client-side Uniter facade, version 9 80 var newStateV9 = newStateForVersionFn(9) 81 82 // NewState creates a new client-side Uniter facade. 83 // Defined like this to allow patching during tests. 84 var NewState = newStateV9 85 86 // BestAPIVersion returns the API version that we were able to 87 // determine is supported by both the client and the API Server. 88 func (st *State) BestAPIVersion() int { 89 return st.facade.BestAPIVersion() 90 } 91 92 // Facade returns the current facade. 93 func (st *State) Facade() base.FacadeCaller { 94 return st.facade 95 } 96 97 // life requests the lifecycle of the given entity from the server. 98 func (st *State) life(tag names.Tag) (params.Life, error) { 99 return common.OneLife(st.facade, tag) 100 } 101 102 // relation requests relation information from the server. 103 func (st *State) relation(relationTag, unitTag names.Tag) (params.RelationResult, error) { 104 nothing := params.RelationResult{} 105 var result params.RelationResults 106 args := params.RelationUnits{ 107 RelationUnits: []params.RelationUnit{ 108 {Relation: relationTag.String(), Unit: unitTag.String()}, 109 }, 110 } 111 err := st.facade.FacadeCall("Relation", args, &result) 112 if err != nil { 113 return nothing, err 114 } 115 if len(result.Results) != 1 { 116 return nothing, fmt.Errorf("expected 1 result, got %d", len(result.Results)) 117 } 118 if err := result.Results[0].Error; err != nil { 119 return nothing, err 120 } 121 return result.Results[0], nil 122 } 123 124 func (st *State) setRelationStatus(id int, status relation.Status) error { 125 args := params.RelationStatusArgs{ 126 Args: []params.RelationStatusArg{{ 127 UnitTag: st.unitTag.String(), 128 RelationId: id, 129 Status: params.RelationStatusValue(status), 130 }}, 131 } 132 var results params.ErrorResults 133 if err := st.facade.FacadeCall("SetRelationStatus", args, &results); err != nil { 134 return errors.Trace(err) 135 } 136 return results.OneError() 137 } 138 139 // getOneAction retrieves a single Action from the controller. 140 func (st *State) getOneAction(tag *names.ActionTag) (params.ActionResult, error) { 141 nothing := params.ActionResult{} 142 143 args := params.Entities{ 144 Entities: []params.Entity{ 145 {Tag: tag.String()}, 146 }, 147 } 148 149 var results params.ActionResults 150 err := st.facade.FacadeCall("Actions", args, &results) 151 if err != nil { 152 return nothing, err 153 } 154 155 if len(results.Results) > 1 { 156 return nothing, fmt.Errorf("expected only 1 action query result, got %d", len(results.Results)) 157 } 158 159 // handle server errors 160 result := results.Results[0] 161 if err := result.Error; err != nil { 162 return nothing, err 163 } 164 165 return result, nil 166 } 167 168 // Unit provides access to methods of a state.Unit through the facade. 169 func (st *State) Unit(tag names.UnitTag) (*Unit, error) { 170 unit := &Unit{ 171 tag: tag, 172 st: st, 173 } 174 err := unit.Refresh() 175 if err != nil { 176 return nil, err 177 } 178 return unit, nil 179 } 180 181 // Application returns an application state by tag. 182 func (st *State) Application(tag names.ApplicationTag) (*Application, error) { 183 life, err := st.life(tag) 184 if err != nil { 185 return nil, err 186 } 187 return &Application{ 188 tag: tag, 189 life: life, 190 st: st, 191 }, nil 192 } 193 194 // ProviderType returns a provider type used by the current juju model. 195 // 196 // TODO(dimitern): We might be able to drop this, once we have machine 197 // addresses implemented fully. See also LP bug 1221798. 198 func (st *State) ProviderType() (string, error) { 199 var result params.StringResult 200 err := st.facade.FacadeCall("ProviderType", nil, &result) 201 if err != nil { 202 return "", err 203 } 204 if err := result.Error; err != nil { 205 return "", err 206 } 207 return result.Result, nil 208 } 209 210 // Charm returns the charm with the given URL. 211 func (st *State) Charm(curl *charm.URL) (*Charm, error) { 212 if curl == nil { 213 return nil, fmt.Errorf("charm url cannot be nil") 214 } 215 return &Charm{ 216 st: st, 217 curl: curl, 218 }, nil 219 } 220 221 // Relation returns the existing relation with the given tag. 222 func (st *State) Relation(relationTag names.RelationTag) (*Relation, error) { 223 result, err := st.relation(relationTag, st.unitTag) 224 if err != nil { 225 return nil, err 226 } 227 return &Relation{ 228 id: result.Id, 229 tag: relationTag, 230 life: result.Life, 231 suspended: result.Suspended, 232 st: st, 233 otherApp: result.OtherApplication, 234 }, nil 235 } 236 237 // Action returns the Action with the given tag. 238 func (st *State) Action(tag names.ActionTag) (*Action, error) { 239 result, err := st.getOneAction(&tag) 240 if err != nil { 241 return nil, err 242 } 243 return &Action{ 244 name: result.Action.Name, 245 params: result.Action.Parameters, 246 }, nil 247 } 248 249 // ActionBegin marks an action as running. 250 func (st *State) ActionBegin(tag names.ActionTag) error { 251 var outcome params.ErrorResults 252 253 args := params.Entities{ 254 Entities: []params.Entity{ 255 {Tag: tag.String()}, 256 }, 257 } 258 259 err := st.facade.FacadeCall("BeginActions", args, &outcome) 260 if err != nil { 261 return err 262 } 263 if len(outcome.Results) != 1 { 264 return fmt.Errorf("expected 1 result, got %d", len(outcome.Results)) 265 } 266 result := outcome.Results[0] 267 if result.Error != nil { 268 return result.Error 269 } 270 return nil 271 } 272 273 // ActionFinish captures the structured output of an action. 274 func (st *State) ActionFinish(tag names.ActionTag, status string, results map[string]interface{}, message string) error { 275 var outcome params.ErrorResults 276 277 args := params.ActionExecutionResults{ 278 Results: []params.ActionExecutionResult{ 279 { 280 ActionTag: tag.String(), 281 Status: status, 282 Results: results, 283 Message: message, 284 }, 285 }, 286 } 287 288 err := st.facade.FacadeCall("FinishActions", args, &outcome) 289 if err != nil { 290 return err 291 } 292 if len(outcome.Results) != 1 { 293 return fmt.Errorf("expected 1 result, got %d", len(outcome.Results)) 294 } 295 result := outcome.Results[0] 296 if result.Error != nil { 297 return result.Error 298 } 299 return nil 300 } 301 302 // RelationById returns the existing relation with the given id. 303 func (st *State) RelationById(id int) (*Relation, error) { 304 var results params.RelationResults 305 args := params.RelationIds{ 306 RelationIds: []int{id}, 307 } 308 err := st.facade.FacadeCall("RelationById", args, &results) 309 if err != nil { 310 return nil, err 311 } 312 if len(results.Results) != 1 { 313 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 314 } 315 result := results.Results[0] 316 if err := result.Error; err != nil { 317 return nil, err 318 } 319 relationTag := names.NewRelationTag(result.Key) 320 return &Relation{ 321 id: result.Id, 322 tag: relationTag, 323 life: result.Life, 324 suspended: result.Suspended, 325 st: st, 326 otherApp: result.OtherApplication, 327 }, nil 328 } 329 330 // Model returns the model entity. 331 func (st *State) Model() (*model.Model, error) { 332 var result params.ModelResult 333 err := st.facade.FacadeCall("CurrentModel", nil, &result) 334 if err != nil { 335 return nil, err 336 } 337 if err := result.Error; err != nil { 338 return nil, err 339 } 340 modelType := model.ModelType(result.Type) 341 if modelType == "" { 342 modelType = model.IAAS 343 } 344 return &model.Model{ 345 Name: result.Name, 346 UUID: result.UUID, 347 ModelType: modelType, 348 }, nil 349 } 350 351 // AllMachinePorts returns all port ranges currently open on the given 352 // machine, mapped to the tags of the unit that opened them and the 353 // relation that applies. 354 func (st *State) AllMachinePorts(machineTag names.MachineTag) (map[corenetwork.PortRange]params.RelationUnit, error) { 355 if st.BestAPIVersion() < 1 { 356 // AllMachinePorts() was introduced in UniterAPIV1. 357 return nil, errors.NotImplementedf("AllMachinePorts() (need V1+)") 358 } 359 var results params.MachinePortsResults 360 args := params.Entities{ 361 Entities: []params.Entity{{Tag: machineTag.String()}}, 362 } 363 err := st.facade.FacadeCall("AllMachinePorts", args, &results) 364 if err != nil { 365 return nil, err 366 } 367 if len(results.Results) != 1 { 368 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 369 } 370 result := results.Results[0] 371 if result.Error != nil { 372 return nil, result.Error 373 } 374 portsMap := make(map[corenetwork.PortRange]params.RelationUnit) 375 for _, ports := range result.Ports { 376 portRange := ports.PortRange.NetworkPortRange() 377 portsMap[portRange] = params.RelationUnit{ 378 Unit: ports.UnitTag, 379 Relation: ports.RelationTag, 380 } 381 } 382 return portsMap, nil 383 } 384 385 // WatchRelationUnits returns a watcher that notifies of changes to the 386 // counterpart units in the relation for the given unit. 387 func (st *State) WatchRelationUnits( 388 relationTag names.RelationTag, 389 unitTag names.UnitTag, 390 ) (watcher.RelationUnitsWatcher, error) { 391 var results params.RelationUnitsWatchResults 392 args := params.RelationUnits{ 393 RelationUnits: []params.RelationUnit{{ 394 Relation: relationTag.String(), 395 Unit: unitTag.String(), 396 }}, 397 } 398 err := st.facade.FacadeCall("WatchRelationUnits", args, &results) 399 if err != nil { 400 return nil, err 401 } 402 if len(results.Results) != 1 { 403 return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results)) 404 } 405 result := results.Results[0] 406 if result.Error != nil { 407 return nil, result.Error 408 } 409 w := apiwatcher.NewRelationUnitsWatcher(st.facade.RawAPICaller(), result) 410 return w, nil 411 } 412 413 // ErrIfNotVersionFn returns a function which can be used to check for 414 // the minimum supported version, and, if appropriate, generate an 415 // error. 416 func ErrIfNotVersionFn(minVersion int, bestAPIVersion int) func(string) error { 417 return func(fnName string) error { 418 if minVersion <= bestAPIVersion { 419 return nil 420 } 421 return errors.NotImplementedf("%s(...) requires v%d+", fnName, minVersion) 422 } 423 } 424 425 // SLALevel returns the SLA level set on the model. 426 func (st *State) SLALevel() (string, error) { 427 if st.BestAPIVersion() < 5 { 428 return "unsupported", nil 429 } 430 var result params.StringResult 431 err := st.facade.FacadeCall("SLALevel", nil, &result) 432 if err != nil { 433 return "", errors.Trace(err) 434 } 435 if err := result.Error; err != nil { 436 return "", errors.Trace(err) 437 } 438 return result.Result, nil 439 } 440 441 // GoalState returns a GoalState struct with the charm's 442 // peers and related units information. 443 func (st *State) GoalState() (application.GoalState, error) { 444 var result params.GoalStateResults 445 446 gs := application.GoalState{} 447 448 args := params.Entities{ 449 Entities: []params.Entity{ 450 {Tag: st.unitTag.String()}, 451 }, 452 } 453 454 err := st.facade.FacadeCall("GoalStates", args, &result) 455 if err != nil { 456 return gs, err 457 } 458 if len(result.Results) != 1 { 459 return gs, errors.Errorf("expected 1 result, got %d", len(result.Results)) 460 } 461 if err := result.Results[0].Error; err != nil { 462 return gs, err 463 } 464 gs = goalStateFromParams(result.Results[0].Result) 465 return gs, nil 466 } 467 468 func goalStateFromParams(paramsGoalState *params.GoalState) application.GoalState { 469 goalState := application.GoalState{} 470 471 copyUnits := func(units params.UnitsGoalState) application.UnitsGoalState { 472 copiedUnits := application.UnitsGoalState{} 473 for name, gs := range units { 474 copiedUnits[name] = application.GoalStateStatus{ 475 Status: gs.Status, 476 Since: gs.Since, 477 } 478 } 479 return copiedUnits 480 } 481 482 goalState.Units = copyUnits(paramsGoalState.Units) 483 484 if paramsGoalState.Relations != nil { 485 goalState.Relations = make(map[string]application.UnitsGoalState) 486 for relation, relationUnits := range paramsGoalState.Relations { 487 goalState.Relations[relation] = copyUnits(relationUnits) 488 } 489 } 490 491 return goalState 492 } 493 494 // SetPodSpec sets the pod spec of the specified application. 495 func (st *State) SetPodSpec(appName string, spec string) error { 496 if !names.IsValidApplication(appName) { 497 return errors.NotValidf("application name %q", appName) 498 } 499 tag := names.NewApplicationTag(appName) 500 var result params.ErrorResults 501 args := params.SetPodSpecParams{ 502 Specs: []params.EntityString{{ 503 Tag: tag.String(), 504 Value: spec, 505 }}, 506 } 507 if err := st.facade.FacadeCall("SetPodSpec", args, &result); err != nil { 508 return errors.Trace(err) 509 } 510 return result.OneError() 511 } 512 513 // CloudSpec returns the cloud spec for the model that calling unit or 514 // application resides in. 515 // If the application has not been authorised to access its cloud spec, 516 // then an authorisation error will be returned. 517 func (st *State) CloudSpec() (*params.CloudSpec, error) { 518 var result params.CloudSpecResult 519 520 err := st.facade.FacadeCall("CloudSpec", nil, &result) 521 if err != nil { 522 return nil, err 523 } 524 if err := result.Error; err != nil { 525 return nil, err 526 } 527 return result.Result, nil 528 }