github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/client/client.go (about) 1 // Copyright 2013, 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package client 5 6 import ( 7 "fmt" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/names" 12 "gopkg.in/juju/charm.v6-unstable" 13 14 "github.com/juju/juju/api" 15 "github.com/juju/juju/apiserver/common" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/apiserver/service" 18 "github.com/juju/juju/environs" 19 "github.com/juju/juju/environs/config" 20 "github.com/juju/juju/environs/manual" 21 "github.com/juju/juju/instance" 22 "github.com/juju/juju/network" 23 "github.com/juju/juju/state" 24 jujuversion "github.com/juju/juju/version" 25 ) 26 27 func init() { 28 common.RegisterStandardFacade("Client", 1, NewClient) 29 } 30 31 var logger = loggo.GetLogger("juju.apiserver.client") 32 33 type API struct { 34 stateAccessor stateInterface 35 auth common.Authorizer 36 resources *common.Resources 37 client *Client 38 // statusSetter provides common methods for updating an entity's provisioning status. 39 statusSetter *common.StatusSetter 40 toolsFinder *common.ToolsFinder 41 } 42 43 // TODO(wallyworld) - remove this method 44 // state returns a state.State instance for this API. 45 // Until all code is refactored to use interfaces, we 46 // need this helper to keep older code happy. 47 func (api *API) state() *state.State { 48 return api.stateAccessor.(*stateShim).State 49 } 50 51 // Client serves client-specific API methods. 52 type Client struct { 53 api *API 54 check *common.BlockChecker 55 } 56 57 var getState = func(st *state.State) stateInterface { 58 return &stateShim{st} 59 } 60 61 // NewClient creates a new instance of the Client Facade. 62 func NewClient(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*Client, error) { 63 if !authorizer.AuthClient() { 64 return nil, common.ErrPerm 65 } 66 apiState := getState(st) 67 urlGetter := common.NewToolsURLGetter(apiState.ModelUUID(), apiState) 68 client := &Client{ 69 api: &API{ 70 stateAccessor: apiState, 71 auth: authorizer, 72 resources: resources, 73 statusSetter: common.NewStatusSetter(st, common.AuthAlways()), 74 toolsFinder: common.NewToolsFinder(st, st, urlGetter), 75 }, 76 check: common.NewBlockChecker(st)} 77 return client, nil 78 } 79 80 func (c *Client) WatchAll() (params.AllWatcherId, error) { 81 w := c.api.stateAccessor.Watch() 82 return params.AllWatcherId{ 83 AllWatcherId: c.api.resources.Register(w), 84 }, nil 85 } 86 87 // Resolved implements the server side of Client.Resolved. 88 func (c *Client) Resolved(p params.Resolved) error { 89 if err := c.check.ChangeAllowed(); err != nil { 90 return errors.Trace(err) 91 } 92 unit, err := c.api.stateAccessor.Unit(p.UnitName) 93 if err != nil { 94 return err 95 } 96 return unit.Resolve(p.Retry) 97 } 98 99 // PublicAddress implements the server side of Client.PublicAddress. 100 func (c *Client) PublicAddress(p params.PublicAddress) (results params.PublicAddressResults, err error) { 101 switch { 102 case names.IsValidMachine(p.Target): 103 machine, err := c.api.stateAccessor.Machine(p.Target) 104 if err != nil { 105 return results, err 106 } 107 addr, err := machine.PublicAddress() 108 if err != nil { 109 return results, errors.Annotatef(err, "error fetching address for machine %q", machine) 110 } 111 return params.PublicAddressResults{PublicAddress: addr.Value}, nil 112 113 case names.IsValidUnit(p.Target): 114 unit, err := c.api.stateAccessor.Unit(p.Target) 115 if err != nil { 116 return results, err 117 } 118 addr, err := unit.PublicAddress() 119 if err != nil { 120 return results, errors.Annotatef(err, "error fetching address for unit %q", unit) 121 } 122 return params.PublicAddressResults{PublicAddress: addr.Value}, nil 123 } 124 return results, errors.Errorf("unknown unit or machine %q", p.Target) 125 } 126 127 // PrivateAddress implements the server side of Client.PrivateAddress. 128 func (c *Client) PrivateAddress(p params.PrivateAddress) (results params.PrivateAddressResults, err error) { 129 switch { 130 case names.IsValidMachine(p.Target): 131 machine, err := c.api.stateAccessor.Machine(p.Target) 132 if err != nil { 133 return results, err 134 } 135 addr, err := machine.PrivateAddress() 136 if err != nil { 137 return results, errors.Annotatef(err, "error fetching address for machine %q", machine) 138 } 139 return params.PrivateAddressResults{PrivateAddress: addr.Value}, nil 140 141 case names.IsValidUnit(p.Target): 142 unit, err := c.api.stateAccessor.Unit(p.Target) 143 if err != nil { 144 return results, err 145 } 146 addr, err := unit.PrivateAddress() 147 if err != nil { 148 return results, errors.Annotatef(err, "error fetching address for unit %q", unit) 149 } 150 return params.PrivateAddressResults{PrivateAddress: addr.Value}, nil 151 } 152 return results, fmt.Errorf("unknown unit or machine %q", p.Target) 153 154 } 155 156 // GetModelConstraints returns the constraints for the model. 157 func (c *Client) GetModelConstraints() (params.GetConstraintsResults, error) { 158 cons, err := c.api.stateAccessor.ModelConstraints() 159 if err != nil { 160 return params.GetConstraintsResults{}, err 161 } 162 return params.GetConstraintsResults{cons}, nil 163 } 164 165 // SetModelConstraints sets the constraints for the model. 166 func (c *Client) SetModelConstraints(args params.SetConstraints) error { 167 if err := c.check.ChangeAllowed(); err != nil { 168 return errors.Trace(err) 169 } 170 return c.api.stateAccessor.SetModelConstraints(args.Constraints) 171 } 172 173 // AddMachines adds new machines with the supplied parameters. 174 func (c *Client) AddMachines(args params.AddMachines) (params.AddMachinesResults, error) { 175 return c.AddMachinesV2(args) 176 } 177 178 // AddMachinesV2 adds new machines with the supplied parameters. 179 func (c *Client) AddMachinesV2(args params.AddMachines) (params.AddMachinesResults, error) { 180 results := params.AddMachinesResults{ 181 Machines: make([]params.AddMachinesResult, len(args.MachineParams)), 182 } 183 if err := c.check.ChangeAllowed(); err != nil { 184 return results, errors.Trace(err) 185 } 186 for i, p := range args.MachineParams { 187 m, err := c.addOneMachine(p) 188 results.Machines[i].Error = common.ServerError(err) 189 if err == nil { 190 results.Machines[i].Machine = m.Id() 191 } 192 } 193 return results, nil 194 } 195 196 // InjectMachines injects a machine into state with provisioned status. 197 func (c *Client) InjectMachines(args params.AddMachines) (params.AddMachinesResults, error) { 198 return c.AddMachines(args) 199 } 200 201 func (c *Client) addOneMachine(p params.AddMachineParams) (*state.Machine, error) { 202 if p.ParentId != "" && p.ContainerType == "" { 203 return nil, fmt.Errorf("parent machine specified without container type") 204 } 205 if p.ContainerType != "" && p.Placement != nil { 206 return nil, fmt.Errorf("container type and placement are mutually exclusive") 207 } 208 if p.Placement != nil { 209 // Extract container type and parent from container placement directives. 210 containerType, err := instance.ParseContainerType(p.Placement.Scope) 211 if err == nil { 212 p.ContainerType = containerType 213 p.ParentId = p.Placement.Directive 214 p.Placement = nil 215 } 216 } 217 218 if p.ContainerType != "" || p.Placement != nil { 219 // Guard against dubious client by making sure that 220 // the following attributes can only be set when we're 221 // not using placement. 222 p.InstanceId = "" 223 p.Nonce = "" 224 p.HardwareCharacteristics = instance.HardwareCharacteristics{} 225 p.Addrs = nil 226 } 227 228 if p.Series == "" { 229 conf, err := c.api.stateAccessor.ModelConfig() 230 if err != nil { 231 return nil, err 232 } 233 p.Series = config.PreferredSeries(conf) 234 } 235 236 var placementDirective string 237 if p.Placement != nil { 238 env, err := c.api.stateAccessor.Model() 239 if err != nil { 240 return nil, err 241 } 242 // For 1.21 we should support both UUID and name, and with 1.22 243 // just support UUID 244 if p.Placement.Scope != env.Name() && p.Placement.Scope != env.UUID() { 245 return nil, fmt.Errorf("invalid model name %q", p.Placement.Scope) 246 } 247 placementDirective = p.Placement.Directive 248 } 249 250 jobs, err := common.StateJobs(p.Jobs) 251 if err != nil { 252 return nil, err 253 } 254 template := state.MachineTemplate{ 255 Series: p.Series, 256 Constraints: p.Constraints, 257 InstanceId: p.InstanceId, 258 Jobs: jobs, 259 Nonce: p.Nonce, 260 HardwareCharacteristics: p.HardwareCharacteristics, 261 Addresses: params.NetworkAddresses(p.Addrs), 262 Placement: placementDirective, 263 } 264 if p.ContainerType == "" { 265 return c.api.stateAccessor.AddOneMachine(template) 266 } 267 if p.ParentId != "" { 268 return c.api.stateAccessor.AddMachineInsideMachine(template, p.ParentId, p.ContainerType) 269 } 270 return c.api.stateAccessor.AddMachineInsideNewMachine(template, template, p.ContainerType) 271 } 272 273 // ProvisioningScript returns a shell script that, when run, 274 // provisions a machine agent on the machine executing the script. 275 func (c *Client) ProvisioningScript(args params.ProvisioningScriptParams) (params.ProvisioningScriptResult, error) { 276 var result params.ProvisioningScriptResult 277 icfg, err := InstanceConfig(c.api.state(), args.MachineId, args.Nonce, args.DataDir) 278 if err != nil { 279 return result, common.ServerError(errors.Annotate( 280 err, "getting instance config", 281 )) 282 } 283 284 // Until DisablePackageCommands is retired, for backwards 285 // compatibility, we must respect the client's request and 286 // override any model settings the user may have specified. 287 // If the client does specify this setting, it will only ever be 288 // true. False indicates the client doesn't care and we should use 289 // what's specified in the environment config. 290 if args.DisablePackageCommands { 291 icfg.EnableOSRefreshUpdate = false 292 icfg.EnableOSUpgrade = false 293 } else if cfg, err := c.api.stateAccessor.ModelConfig(); err != nil { 294 return result, common.ServerError(errors.Annotate( 295 err, "getting model config", 296 )) 297 } else { 298 icfg.EnableOSUpgrade = cfg.EnableOSUpgrade() 299 icfg.EnableOSRefreshUpdate = cfg.EnableOSRefreshUpdate() 300 } 301 302 result.Script, err = manual.ProvisioningScript(icfg) 303 if err != nil { 304 return result, common.ServerError(errors.Annotate( 305 err, "getting provisioning script", 306 )) 307 } 308 return result, nil 309 } 310 311 // DestroyMachines removes a given set of machines. 312 func (c *Client) DestroyMachines(args params.DestroyMachines) error { 313 if err := c.check.RemoveAllowed(); !args.Force && err != nil { 314 return errors.Trace(err) 315 } 316 317 return common.DestroyMachines(c.api.stateAccessor, args.Force, args.MachineNames...) 318 } 319 320 // CharmInfo returns information about the requested charm. 321 func (c *Client) CharmInfo(args params.CharmInfo) (api.CharmInfo, error) { 322 curl, err := charm.ParseURL(args.CharmURL) 323 if err != nil { 324 return api.CharmInfo{}, err 325 } 326 charm, err := c.api.stateAccessor.Charm(curl) 327 if err != nil { 328 return api.CharmInfo{}, err 329 } 330 info := api.CharmInfo{ 331 Revision: charm.Revision(), 332 URL: curl.String(), 333 Config: charm.Config(), 334 Meta: charm.Meta(), 335 Actions: charm.Actions(), 336 } 337 return info, nil 338 } 339 340 // ModelInfo returns information about the current model (default 341 // series and type). 342 func (c *Client) ModelInfo() (params.ModelInfo, error) { 343 state := c.api.stateAccessor 344 conf, err := state.ModelConfig() 345 if err != nil { 346 return params.ModelInfo{}, err 347 } 348 env, err := state.Model() 349 if err != nil { 350 return params.ModelInfo{}, err 351 } 352 353 info := params.ModelInfo{ 354 DefaultSeries: config.PreferredSeries(conf), 355 ProviderType: conf.Type(), 356 Name: conf.Name(), 357 UUID: env.UUID(), 358 ControllerUUID: env.ControllerUUID(), 359 } 360 return info, nil 361 } 362 363 // ModelUserInfo returns information on all users in the model. 364 func (c *Client) ModelUserInfo() (params.ModelUserInfoResults, error) { 365 var results params.ModelUserInfoResults 366 env, err := c.api.stateAccessor.Model() 367 if err != nil { 368 return results, errors.Trace(err) 369 } 370 users, err := env.Users() 371 if err != nil { 372 return results, errors.Trace(err) 373 } 374 375 for _, user := range users { 376 var result params.ModelUserInfoResult 377 userInfo, err := common.ModelUserInfo(user) 378 if err != nil { 379 result.Error = common.ServerError(err) 380 } else { 381 result.Result = &userInfo 382 } 383 results.Results = append(results.Results, result) 384 } 385 return results, nil 386 } 387 388 // AgentVersion returns the current version that the API server is running. 389 func (c *Client) AgentVersion() (params.AgentVersionResult, error) { 390 return params.AgentVersionResult{Version: jujuversion.Current}, nil 391 } 392 393 // ModelGet implements the server-side part of the 394 // get-model-config CLI command. 395 func (c *Client) ModelGet() (params.ModelConfigResults, error) { 396 result := params.ModelConfigResults{} 397 // Get the existing environment config from the state. 398 config, err := c.api.stateAccessor.ModelConfig() 399 if err != nil { 400 return result, err 401 } 402 result.Config = config.AllAttrs() 403 return result, nil 404 } 405 406 // ModelSet implements the server-side part of the 407 // set-model-config CLI command. 408 func (c *Client) ModelSet(args params.ModelSet) error { 409 if err := c.check.ChangeAllowed(); err != nil { 410 return errors.Trace(err) 411 } 412 // Make sure we don't allow changing agent-version. 413 checkAgentVersion := func(updateAttrs map[string]interface{}, removeAttrs []string, oldConfig *config.Config) error { 414 if v, found := updateAttrs["agent-version"]; found { 415 oldVersion, _ := oldConfig.AgentVersion() 416 if v != oldVersion.String() { 417 return fmt.Errorf("agent-version cannot be changed") 418 } 419 } 420 return nil 421 } 422 // Replace any deprecated attributes with their new values. 423 attrs := config.ProcessDeprecatedAttributes(args.Config) 424 // TODO(waigani) 2014-3-11 #1167616 425 // Add a txn retry loop to ensure that the settings on disk have not 426 // changed underneath us. 427 return c.api.stateAccessor.UpdateModelConfig(attrs, nil, checkAgentVersion) 428 } 429 430 // ModelUnset implements the server-side part of the 431 // set-model-config CLI command. 432 func (c *Client) ModelUnset(args params.ModelUnset) error { 433 if err := c.check.ChangeAllowed(); err != nil { 434 return errors.Trace(err) 435 } 436 // TODO(waigani) 2014-3-11 #1167616 437 // Add a txn retry loop to ensure that the settings on disk have not 438 // changed underneath us. 439 return c.api.stateAccessor.UpdateModelConfig(nil, args.Keys, nil) 440 } 441 442 // SetModelAgentVersion sets the model agent version. 443 func (c *Client) SetModelAgentVersion(args params.SetModelAgentVersion) error { 444 if err := c.check.ChangeAllowed(); err != nil { 445 return errors.Trace(err) 446 } 447 // Before changing the agent version to trigger an upgrade or downgrade, 448 // we'll do a very basic check to ensure the 449 cfg, err := c.api.stateAccessor.ModelConfig() 450 if err != nil { 451 return errors.Trace(err) 452 } 453 env, err := getEnvironment(cfg) 454 if err != nil { 455 return errors.Trace(err) 456 } 457 if err := environs.CheckProviderAPI(env); err != nil { 458 return err 459 } 460 return c.api.stateAccessor.SetModelAgentVersion(args.Version) 461 } 462 463 var getEnvironment = func(cfg *config.Config) (environs.Environ, error) { 464 env, err := environs.New(cfg) 465 if err != nil { 466 return nil, err 467 } 468 return env, nil 469 } 470 471 // AbortCurrentUpgrade aborts and archives the current upgrade 472 // synchronisation record, if any. 473 func (c *Client) AbortCurrentUpgrade() error { 474 if err := c.check.ChangeAllowed(); err != nil { 475 return errors.Trace(err) 476 } 477 return c.api.stateAccessor.AbortCurrentUpgrade() 478 } 479 480 // FindTools returns a List containing all tools matching the given parameters. 481 func (c *Client) FindTools(args params.FindToolsParams) (params.FindToolsResult, error) { 482 return c.api.toolsFinder.FindTools(args) 483 } 484 485 func (c *Client) AddCharm(args params.AddCharm) error { 486 return service.AddCharmWithAuthorization(c.api.state(), params.AddCharmWithAuthorization{ 487 URL: args.URL, 488 Channel: args.Channel, 489 }) 490 } 491 492 // AddCharmWithAuthorization adds the given charm URL (which must include revision) to 493 // the model, if it does not exist yet. Local charms are not 494 // supported, only charm store URLs. See also AddLocalCharm(). 495 // 496 // The authorization macaroon, args.CharmStoreMacaroon, may be 497 // omitted, in which case this call is equivalent to AddCharm. 498 func (c *Client) AddCharmWithAuthorization(args params.AddCharmWithAuthorization) error { 499 return service.AddCharmWithAuthorization(c.api.state(), args) 500 } 501 502 // ResolveCharm resolves the best available charm URLs with series, for charm 503 // locations without a series specified. 504 func (c *Client) ResolveCharms(args params.ResolveCharms) (params.ResolveCharmResults, error) { 505 return service.ResolveCharms(c.api.state(), args) 506 } 507 508 // RetryProvisioning marks a provisioning error as transient on the machines. 509 func (c *Client) RetryProvisioning(p params.Entities) (params.ErrorResults, error) { 510 if err := c.check.ChangeAllowed(); err != nil { 511 return params.ErrorResults{}, errors.Trace(err) 512 } 513 entityStatus := make([]params.EntityStatusArgs, len(p.Entities)) 514 for i, entity := range p.Entities { 515 entityStatus[i] = params.EntityStatusArgs{Tag: entity.Tag, Data: map[string]interface{}{"transient": true}} 516 } 517 return c.api.statusSetter.UpdateStatus(params.SetStatus{ 518 Entities: entityStatus, 519 }) 520 } 521 522 // APIHostPorts returns the API host/port addresses stored in state. 523 func (c *Client) APIHostPorts() (result params.APIHostPortsResult, err error) { 524 var servers [][]network.HostPort 525 if servers, err = c.api.stateAccessor.APIHostPorts(); err != nil { 526 return params.APIHostPortsResult{}, err 527 } 528 result.Servers = params.FromNetworkHostsPorts(servers) 529 return result, nil 530 } 531 532 // DestroyModel will try to destroy the current model. 533 // If there is a block on destruction, this method will return an error. 534 func (c *Client) DestroyModel() (err error) { 535 if err := c.check.DestroyAllowed(); err != nil { 536 return errors.Trace(err) 537 } 538 539 modelTag := c.api.stateAccessor.ModelTag() 540 return errors.Trace(common.DestroyModel(c.api.state(), modelTag)) 541 }