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  }