github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/apiserver/provisioner/provisioner.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/names"
    10  	"github.com/juju/utils/set"
    11  
    12  	"github.com/juju/juju/constraints"
    13  	"github.com/juju/juju/container"
    14  	"github.com/juju/juju/instance"
    15  	"github.com/juju/juju/state"
    16  	"github.com/juju/juju/state/api/params"
    17  	"github.com/juju/juju/state/apiserver/common"
    18  	"github.com/juju/juju/state/watcher"
    19  )
    20  
    21  // ProvisionerAPI provides access to the Provisioner API facade.
    22  type ProvisionerAPI struct {
    23  	*common.Remover
    24  	*common.StatusSetter
    25  	*common.DeadEnsurer
    26  	*common.PasswordChanger
    27  	*common.LifeGetter
    28  	*common.StateAddresser
    29  	*common.APIAddresser
    30  	*common.ToolsGetter
    31  	*common.EnvironWatcher
    32  	*common.EnvironMachinesWatcher
    33  	*common.InstanceIdGetter
    34  
    35  	st                  *state.State
    36  	resources           *common.Resources
    37  	authorizer          common.Authorizer
    38  	getAuthFunc         common.GetAuthFunc
    39  	getCanWatchMachines common.GetAuthFunc
    40  }
    41  
    42  // NewProvisionerAPI creates a new server-side ProvisionerAPI facade.
    43  func NewProvisionerAPI(
    44  	st *state.State,
    45  	resources *common.Resources,
    46  	authorizer common.Authorizer,
    47  ) (*ProvisionerAPI, error) {
    48  	if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() {
    49  		return nil, common.ErrPerm
    50  	}
    51  	getAuthFunc := func() (common.AuthFunc, error) {
    52  		isEnvironManager := authorizer.AuthEnvironManager()
    53  		isMachineAgent := authorizer.AuthMachineAgent()
    54  		authEntityTag := authorizer.GetAuthTag()
    55  
    56  		return func(tag string) bool {
    57  			if isMachineAgent && tag == authEntityTag {
    58  				// A machine agent can always access its own machine.
    59  				return true
    60  			}
    61  			_, id, err := names.ParseTag(tag, names.MachineTagKind)
    62  			if err != nil {
    63  				return false
    64  			}
    65  			parentId := state.ParentId(id)
    66  			if parentId == "" {
    67  				// All top-level machines are accessible by the
    68  				// environment manager.
    69  				return isEnvironManager
    70  			}
    71  			// All containers with the authenticated machine as a
    72  			// parent are accessible by it.
    73  			return isMachineAgent && names.MachineTag(parentId) == authEntityTag
    74  		}, nil
    75  	}
    76  	// Both provisioner types can watch the environment.
    77  	getCanWatch := common.AuthAlways(true)
    78  	// Only the environment provisioner can read secrets.
    79  	getCanReadSecrets := common.AuthAlways(authorizer.AuthEnvironManager())
    80  	return &ProvisionerAPI{
    81  		Remover:                common.NewRemover(st, false, getAuthFunc),
    82  		StatusSetter:           common.NewStatusSetter(st, getAuthFunc),
    83  		DeadEnsurer:            common.NewDeadEnsurer(st, getAuthFunc),
    84  		PasswordChanger:        common.NewPasswordChanger(st, getAuthFunc),
    85  		LifeGetter:             common.NewLifeGetter(st, getAuthFunc),
    86  		StateAddresser:         common.NewStateAddresser(st),
    87  		APIAddresser:           common.NewAPIAddresser(st, resources),
    88  		ToolsGetter:            common.NewToolsGetter(st, getAuthFunc),
    89  		EnvironWatcher:         common.NewEnvironWatcher(st, resources, getCanWatch, getCanReadSecrets),
    90  		EnvironMachinesWatcher: common.NewEnvironMachinesWatcher(st, resources, getCanReadSecrets),
    91  		InstanceIdGetter:       common.NewInstanceIdGetter(st, getAuthFunc),
    92  		st:                     st,
    93  		resources:              resources,
    94  		authorizer:             authorizer,
    95  		getAuthFunc:            getAuthFunc,
    96  		getCanWatchMachines:    getCanReadSecrets,
    97  	}, nil
    98  }
    99  
   100  func (p *ProvisionerAPI) getMachine(canAccess common.AuthFunc, tag string) (*state.Machine, error) {
   101  	if !canAccess(tag) {
   102  		return nil, common.ErrPerm
   103  	}
   104  	entity, err := p.st.FindEntity(tag)
   105  	if err != nil {
   106  		return nil, err
   107  	}
   108  	// The authorization function guarantees that the tag represents a
   109  	// machine.
   110  	return entity.(*state.Machine), nil
   111  }
   112  
   113  func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) {
   114  	nothing := params.StringsWatchResult{}
   115  	canAccess, err := p.getAuthFunc()
   116  	if err != nil {
   117  		return nothing, err
   118  	}
   119  	if !canAccess(arg.MachineTag) {
   120  		return nothing, common.ErrPerm
   121  	}
   122  	_, id, err := names.ParseTag(arg.MachineTag, names.MachineTagKind)
   123  	if err != nil {
   124  		return nothing, err
   125  	}
   126  	machine, err := p.st.Machine(id)
   127  	if err != nil {
   128  		return nothing, err
   129  	}
   130  	var watch state.StringsWatcher
   131  	if arg.ContainerType != "" {
   132  		watch = machine.WatchContainers(instance.ContainerType(arg.ContainerType))
   133  	} else {
   134  		watch = machine.WatchAllContainers()
   135  	}
   136  	// Consume the initial event and forward it to the result.
   137  	if changes, ok := <-watch.Changes(); ok {
   138  		return params.StringsWatchResult{
   139  			StringsWatcherId: p.resources.Register(watch),
   140  			Changes:          changes,
   141  		}, nil
   142  	}
   143  	return nothing, watcher.MustErr(watch)
   144  }
   145  
   146  // WatchContainers starts a StringsWatcher to watch containers deployed to
   147  // any machine passed in args.
   148  func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
   149  	result := params.StringsWatchResults{
   150  		Results: make([]params.StringsWatchResult, len(args.Params)),
   151  	}
   152  	for i, arg := range args.Params {
   153  		watcherResult, err := p.watchOneMachineContainers(arg)
   154  		result.Results[i] = watcherResult
   155  		result.Results[i].Error = common.ServerError(err)
   156  	}
   157  	return result, nil
   158  }
   159  
   160  // WatchAllContainers starts a StringsWatcher to watch all containers deployed to
   161  // any machine passed in args.
   162  func (p *ProvisionerAPI) WatchAllContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
   163  	return p.WatchContainers(args)
   164  }
   165  
   166  // SetSupportedContainers updates the list of containers supported by the machines passed in args.
   167  func (p *ProvisionerAPI) SetSupportedContainers(
   168  	args params.MachineContainersParams) (params.ErrorResults, error) {
   169  
   170  	result := params.ErrorResults{
   171  		Results: make([]params.ErrorResult, len(args.Params)),
   172  	}
   173  	for i, arg := range args.Params {
   174  		canAccess, err := p.getAuthFunc()
   175  		if err != nil {
   176  			return result, err
   177  		}
   178  		machine, err := p.getMachine(canAccess, arg.MachineTag)
   179  		if err != nil {
   180  			result.Results[i].Error = common.ServerError(err)
   181  			continue
   182  		}
   183  		if len(arg.ContainerTypes) == 0 {
   184  			err = machine.SupportsNoContainers()
   185  		} else {
   186  			err = machine.SetSupportedContainers(arg.ContainerTypes)
   187  		}
   188  		if err != nil {
   189  			result.Results[i].Error = common.ServerError(err)
   190  		}
   191  	}
   192  	return result, nil
   193  }
   194  
   195  // ContainerManagerConfig returns information from the environment config that is
   196  // needed for configuring the container manager.
   197  func (p *ProvisionerAPI) ContainerManagerConfig(args params.ContainerManagerConfigParams) (params.ContainerManagerConfig, error) {
   198  	var result params.ContainerManagerConfig
   199  	config, err := p.st.EnvironConfig()
   200  	if err != nil {
   201  		return result, err
   202  	}
   203  	cfg := make(map[string]string)
   204  	cfg[container.ConfigName] = "juju"
   205  	switch args.Type {
   206  	case instance.LXC:
   207  		if useLxcClone, ok := config.LXCUseClone(); ok {
   208  			cfg["use-clone"] = fmt.Sprint(useLxcClone)
   209  		}
   210  		if useLxcCloneAufs, ok := config.LXCUseCloneAUFS(); ok {
   211  			cfg["use-aufs"] = fmt.Sprint(useLxcCloneAufs)
   212  		}
   213  	}
   214  	result.ManagerConfig = cfg
   215  	return result, nil
   216  }
   217  
   218  // ContainerConfig returns information from the environment config that is
   219  // needed for container cloud-init.
   220  func (p *ProvisionerAPI) ContainerConfig() (params.ContainerConfig, error) {
   221  	result := params.ContainerConfig{}
   222  	config, err := p.st.EnvironConfig()
   223  	if err != nil {
   224  		return result, err
   225  	}
   226  	result.ProviderType = config.Type()
   227  	result.AuthorizedKeys = config.AuthorizedKeys()
   228  	result.SSLHostnameVerification = config.SSLHostnameVerification()
   229  	result.Proxy = config.ProxySettings()
   230  	result.AptProxy = config.AptProxySettings()
   231  	return result, nil
   232  }
   233  
   234  // Status returns the status of each given machine entity.
   235  func (p *ProvisionerAPI) Status(args params.Entities) (params.StatusResults, error) {
   236  	result := params.StatusResults{
   237  		Results: make([]params.StatusResult, len(args.Entities)),
   238  	}
   239  	canAccess, err := p.getAuthFunc()
   240  	if err != nil {
   241  		return result, err
   242  	}
   243  	for i, entity := range args.Entities {
   244  		machine, err := p.getMachine(canAccess, entity.Tag)
   245  		if err == nil {
   246  			r := &result.Results[i]
   247  			r.Status, r.Info, r.Data, err = machine.Status()
   248  		}
   249  		result.Results[i].Error = common.ServerError(err)
   250  	}
   251  	return result, nil
   252  }
   253  
   254  // MachinesWithTransientErrors returns status data for machines with provisioning
   255  // errors which are transient.
   256  func (p *ProvisionerAPI) MachinesWithTransientErrors() (params.StatusResults, error) {
   257  	results := params.StatusResults{}
   258  	canAccessFunc, err := p.getAuthFunc()
   259  	if err != nil {
   260  		return results, err
   261  	}
   262  	// TODO (wallyworld) - add state.State API for more efficient machines query
   263  	machines, err := p.st.AllMachines()
   264  	if err != nil {
   265  		return results, err
   266  	}
   267  	for _, machine := range machines {
   268  		if !canAccessFunc(machine.Tag()) {
   269  			continue
   270  		}
   271  		if _, provisionedErr := machine.InstanceId(); provisionedErr == nil {
   272  			// Machine may have been provisioned but machiner hasn't set the
   273  			// status to Started yet.
   274  			continue
   275  		}
   276  		result := params.StatusResult{}
   277  		if result.Status, result.Info, result.Data, err = machine.Status(); err != nil {
   278  			continue
   279  		}
   280  		if result.Status != params.StatusError {
   281  			continue
   282  		}
   283  		// Transient errors are marked as such in the status data.
   284  		if transient, ok := result.Data["transient"].(bool); !ok || !transient {
   285  			continue
   286  		}
   287  		result.Id = machine.Id()
   288  		result.Life = params.Life(machine.Life().String())
   289  		results.Results = append(results.Results, result)
   290  	}
   291  	return results, nil
   292  }
   293  
   294  // Series returns the deployed series for each given machine entity.
   295  func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) {
   296  	result := params.StringResults{
   297  		Results: make([]params.StringResult, len(args.Entities)),
   298  	}
   299  	canAccess, err := p.getAuthFunc()
   300  	if err != nil {
   301  		return result, err
   302  	}
   303  	for i, entity := range args.Entities {
   304  		machine, err := p.getMachine(canAccess, entity.Tag)
   305  		if err == nil {
   306  			result.Results[i].Result = machine.Series()
   307  		}
   308  		result.Results[i].Error = common.ServerError(err)
   309  	}
   310  	return result, nil
   311  }
   312  
   313  // ProvisioningInfo returns the provisioning information for each given machine entity.
   314  func (p *ProvisionerAPI) ProvisioningInfo(args params.Entities) (params.ProvisioningInfoResults, error) {
   315  	result := params.ProvisioningInfoResults{
   316  		Results: make([]params.ProvisioningInfoResult, len(args.Entities)),
   317  	}
   318  	canAccess, err := p.getAuthFunc()
   319  	if err != nil {
   320  		return result, err
   321  	}
   322  	for i, entity := range args.Entities {
   323  		machine, err := p.getMachine(canAccess, entity.Tag)
   324  		if err == nil {
   325  			result.Results[i].Result, err = getProvisioningInfo(machine)
   326  		}
   327  		result.Results[i].Error = common.ServerError(err)
   328  	}
   329  	return result, nil
   330  }
   331  
   332  func getProvisioningInfo(m *state.Machine) (*params.ProvisioningInfo, error) {
   333  	cons, err := m.Constraints()
   334  	if err != nil {
   335  		return nil, err
   336  	}
   337  	// TODO(dimitern) For now, since network names and
   338  	// provider ids are the same, we return what we got
   339  	// from state. In the future, when networks can be
   340  	// added before provisioning, we should convert both
   341  	// slices from juju network names to provider-specific
   342  	// ids before returning them.
   343  	networks, err := m.RequestedNetworks()
   344  	if err != nil {
   345  		return nil, err
   346  	}
   347  	return &params.ProvisioningInfo{
   348  		Constraints: cons,
   349  		Series:      m.Series(),
   350  		Placement:   m.Placement(),
   351  		Networks:    networks,
   352  	}, nil
   353  }
   354  
   355  // DistributionGroup returns, for each given machine entity,
   356  // a slice of instance.Ids that belong to the same distribution
   357  // group as that machine. This information may be used to
   358  // distribute instances for high availability.
   359  func (p *ProvisionerAPI) DistributionGroup(args params.Entities) (params.DistributionGroupResults, error) {
   360  	result := params.DistributionGroupResults{
   361  		Results: make([]params.DistributionGroupResult, len(args.Entities)),
   362  	}
   363  	canAccess, err := p.getAuthFunc()
   364  	if err != nil {
   365  		return result, err
   366  	}
   367  	for i, entity := range args.Entities {
   368  		machine, err := p.getMachine(canAccess, entity.Tag)
   369  		if err == nil {
   370  			// If the machine is an environment manager, return
   371  			// environment manager instances. Otherwise, return
   372  			// instances with services in common with the machine
   373  			// being provisioned.
   374  			if machine.IsManager() {
   375  				result.Results[i].Result, err = environManagerInstances(p.st)
   376  			} else {
   377  				result.Results[i].Result, err = commonServiceInstances(p.st, machine)
   378  			}
   379  		}
   380  		result.Results[i].Error = common.ServerError(err)
   381  	}
   382  	return result, nil
   383  }
   384  
   385  // environManagerInstances returns all environ manager instances.
   386  func environManagerInstances(st *state.State) ([]instance.Id, error) {
   387  	info, err := st.StateServerInfo()
   388  	if err != nil {
   389  		return nil, err
   390  	}
   391  	instances := make([]instance.Id, 0, len(info.MachineIds))
   392  	for _, id := range info.MachineIds {
   393  		machine, err := st.Machine(id)
   394  		if err != nil {
   395  			return nil, err
   396  		}
   397  		instanceId, err := machine.InstanceId()
   398  		if err == nil {
   399  			instances = append(instances, instanceId)
   400  		} else if !state.IsNotProvisionedError(err) {
   401  			return nil, err
   402  		}
   403  	}
   404  	return instances, nil
   405  }
   406  
   407  // commonServiceInstances returns instances with
   408  // services in common with the specified machine.
   409  func commonServiceInstances(st *state.State, m *state.Machine) ([]instance.Id, error) {
   410  	units, err := m.Units()
   411  	if err != nil {
   412  		return nil, err
   413  	}
   414  	var instanceIdSet set.Strings
   415  	for _, unit := range units {
   416  		if !unit.IsPrincipal() {
   417  			continue
   418  		}
   419  		instanceIds, err := state.ServiceInstances(st, unit.ServiceName())
   420  		if err != nil {
   421  			return nil, err
   422  		}
   423  		for _, instanceId := range instanceIds {
   424  			instanceIdSet.Add(string(instanceId))
   425  		}
   426  	}
   427  	instanceIds := make([]instance.Id, instanceIdSet.Size())
   428  	// Sort values to simplify testing.
   429  	for i, instanceId := range instanceIdSet.SortedValues() {
   430  		instanceIds[i] = instance.Id(instanceId)
   431  	}
   432  	return instanceIds, nil
   433  }
   434  
   435  // Constraints returns the constraints for each given machine entity.
   436  func (p *ProvisionerAPI) Constraints(args params.Entities) (params.ConstraintsResults, error) {
   437  	result := params.ConstraintsResults{
   438  		Results: make([]params.ConstraintsResult, len(args.Entities)),
   439  	}
   440  	canAccess, err := p.getAuthFunc()
   441  	if err != nil {
   442  		return result, err
   443  	}
   444  	for i, entity := range args.Entities {
   445  		machine, err := p.getMachine(canAccess, entity.Tag)
   446  		if err == nil {
   447  			var cons constraints.Value
   448  			cons, err = machine.Constraints()
   449  			if err == nil {
   450  				result.Results[i].Constraints = cons
   451  			}
   452  		}
   453  		result.Results[i].Error = common.ServerError(err)
   454  	}
   455  	return result, nil
   456  }
   457  
   458  func networkParamsToStateParams(networks []params.Network, ifaces []params.NetworkInterface) (
   459  	[]state.NetworkInfo, []state.NetworkInterfaceInfo, error,
   460  ) {
   461  	stateNetworks := make([]state.NetworkInfo, len(networks))
   462  	for i, network := range networks {
   463  		_, networkName, err := names.ParseTag(network.Tag, names.NetworkTagKind)
   464  		if err != nil {
   465  			return nil, nil, err
   466  		}
   467  		stateNetworks[i] = state.NetworkInfo{
   468  			Name:       networkName,
   469  			ProviderId: network.ProviderId,
   470  			CIDR:       network.CIDR,
   471  			VLANTag:    network.VLANTag,
   472  		}
   473  	}
   474  	stateInterfaces := make([]state.NetworkInterfaceInfo, len(ifaces))
   475  	for i, iface := range ifaces {
   476  		_, networkName, err := names.ParseTag(iface.NetworkTag, names.NetworkTagKind)
   477  		if err != nil {
   478  			return nil, nil, err
   479  		}
   480  		stateInterfaces[i] = state.NetworkInterfaceInfo{
   481  			MACAddress:    iface.MACAddress,
   482  			NetworkName:   networkName,
   483  			InterfaceName: iface.InterfaceName,
   484  			IsVirtual:     iface.IsVirtual,
   485  		}
   486  	}
   487  	return stateNetworks, stateInterfaces, nil
   488  }
   489  
   490  // RequestedNetworks returns the requested networks for each given
   491  // machine entity. Each entry in both lists is returned with its
   492  // provider specific id.
   493  func (p *ProvisionerAPI) RequestedNetworks(args params.Entities) (params.RequestedNetworksResults, error) {
   494  	result := params.RequestedNetworksResults{
   495  		Results: make([]params.RequestedNetworkResult, len(args.Entities)),
   496  	}
   497  	canAccess, err := p.getAuthFunc()
   498  	if err != nil {
   499  		return result, err
   500  	}
   501  	for i, entity := range args.Entities {
   502  		machine, err := p.getMachine(canAccess, entity.Tag)
   503  		if err == nil {
   504  			var networks []string
   505  			networks, err = machine.RequestedNetworks()
   506  			if err == nil {
   507  				// TODO(dimitern) For now, since network names and
   508  				// provider ids are the same, we return what we got
   509  				// from state. In the future, when networks can be
   510  				// added before provisioning, we should convert both
   511  				// slices from juju network names to provider-specific
   512  				// ids before returning them.
   513  				result.Results[i].Networks = networks
   514  			}
   515  		}
   516  		result.Results[i].Error = common.ServerError(err)
   517  	}
   518  	return result, nil
   519  }
   520  
   521  // SetProvisioned sets the provider specific instance id, nonce and
   522  // metadata for each given machine. Once set, the instance id cannot
   523  // be changed.
   524  //
   525  // TODO(dimitern) This is not used anymore (as of 1.19.0) and is
   526  // retained only for backwards-compatibility. It should be removed as
   527  // deprecated. SetInstanceInfo is used instead.
   528  func (p *ProvisionerAPI) SetProvisioned(args params.SetProvisioned) (params.ErrorResults, error) {
   529  	result := params.ErrorResults{
   530  		Results: make([]params.ErrorResult, len(args.Machines)),
   531  	}
   532  	canAccess, err := p.getAuthFunc()
   533  	if err != nil {
   534  		return result, err
   535  	}
   536  	for i, arg := range args.Machines {
   537  		machine, err := p.getMachine(canAccess, arg.Tag)
   538  		if err == nil {
   539  			err = machine.SetProvisioned(arg.InstanceId, arg.Nonce, arg.Characteristics)
   540  		}
   541  		result.Results[i].Error = common.ServerError(err)
   542  	}
   543  	return result, nil
   544  }
   545  
   546  // SetInstanceInfo sets the provider specific machine id, nonce,
   547  // metadata and network info for each given machine. Once set, the
   548  // instance id cannot be changed.
   549  func (p *ProvisionerAPI) SetInstanceInfo(args params.InstancesInfo) (params.ErrorResults, error) {
   550  	result := params.ErrorResults{
   551  		Results: make([]params.ErrorResult, len(args.Machines)),
   552  	}
   553  	canAccess, err := p.getAuthFunc()
   554  	if err != nil {
   555  		return result, err
   556  	}
   557  	for i, arg := range args.Machines {
   558  		machine, err := p.getMachine(canAccess, arg.Tag)
   559  		if err == nil {
   560  			var networks []state.NetworkInfo
   561  			var interfaces []state.NetworkInterfaceInfo
   562  			networks, interfaces, err = networkParamsToStateParams(arg.Networks, arg.Interfaces)
   563  			if err == nil {
   564  				err = machine.SetInstanceInfo(
   565  					arg.InstanceId, arg.Nonce, arg.Characteristics,
   566  					networks, interfaces)
   567  			}
   568  			if err != nil {
   569  				// Give the user more context about the error.
   570  				err = fmt.Errorf("aborted instance %q: %v", arg.InstanceId, err)
   571  			}
   572  		}
   573  		result.Results[i].Error = common.ServerError(err)
   574  	}
   575  	return result, nil
   576  }
   577  
   578  // WatchMachineErrorRetry returns a NotifyWatcher that notifies when
   579  // the provisioner should retry provisioning machines with transient errors.
   580  func (p *ProvisionerAPI) WatchMachineErrorRetry() (params.NotifyWatchResult, error) {
   581  	result := params.NotifyWatchResult{}
   582  	canWatch, err := p.getCanWatchMachines()
   583  	if err != nil {
   584  		return params.NotifyWatchResult{}, err
   585  	}
   586  	if !canWatch("") {
   587  		return result, common.ErrPerm
   588  	}
   589  	watch := newWatchMachineErrorRetry()
   590  	// Consume any initial event and forward it to the result.
   591  	if _, ok := <-watch.Changes(); ok {
   592  		result.NotifyWatcherId = p.resources.Register(watch)
   593  	} else {
   594  		return result, watcher.MustErr(watch)
   595  	}
   596  	return result, nil
   597  }