launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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  	errgo "launchpad.net/errgo/errors"
     8  	"launchpad.net/juju-core/constraints"
     9  	"launchpad.net/juju-core/errors"
    10  	"launchpad.net/juju-core/instance"
    11  	"launchpad.net/juju-core/names"
    12  	"launchpad.net/juju-core/state"
    13  	"launchpad.net/juju-core/state/api/params"
    14  	"launchpad.net/juju-core/state/apiserver/common"
    15  	"launchpad.net/juju-core/state/watcher"
    16  )
    17  
    18  var mask = errgo.Mask
    19  
    20  // ProvisionerAPI provides access to the Provisioner API facade.
    21  type ProvisionerAPI struct {
    22  	*common.Remover
    23  	*common.StatusSetter
    24  	*common.DeadEnsurer
    25  	*common.PasswordChanger
    26  	*common.LifeGetter
    27  	*common.StateAddresser
    28  	*common.APIAddresser
    29  	*common.ToolsGetter
    30  	*common.EnvironWatcher
    31  	*common.EnvironMachinesWatcher
    32  	*common.InstanceIdGetter
    33  
    34  	st          *state.State
    35  	resources   *common.Resources
    36  	authorizer  common.Authorizer
    37  	getAuthFunc common.GetAuthFunc
    38  }
    39  
    40  // NewProvisionerAPI creates a new server-side ProvisionerAPI facade.
    41  func NewProvisionerAPI(
    42  	st *state.State,
    43  	resources *common.Resources,
    44  	authorizer common.Authorizer,
    45  ) (*ProvisionerAPI, error) {
    46  	if !authorizer.AuthMachineAgent() && !authorizer.AuthEnvironManager() {
    47  		return nil, common.ErrPerm
    48  	}
    49  	getAuthFunc := func() (common.AuthFunc, error) {
    50  		isEnvironManager := authorizer.AuthEnvironManager()
    51  		isMachineAgent := authorizer.AuthMachineAgent()
    52  		authEntityTag := authorizer.GetAuthTag()
    53  
    54  		return func(tag string) bool {
    55  			if isMachineAgent && tag == authEntityTag {
    56  				// A machine agent can always access its own machine.
    57  				return true
    58  			}
    59  			_, id, err := names.ParseTag(tag, names.MachineTagKind)
    60  			if err != nil {
    61  				return false
    62  			}
    63  			parentId := state.ParentId(id)
    64  			if parentId == "" {
    65  				// All top-level machines are accessible by the
    66  				// environment manager.
    67  				return isEnvironManager
    68  			}
    69  			// All containers with the authenticated machine as a
    70  			// parent are accessible by it.
    71  			return isMachineAgent && names.MachineTag(parentId) == authEntityTag
    72  		}, nil
    73  	}
    74  	// Both provisioner types can watch the environment.
    75  	getCanWatch := common.AuthAlways(true)
    76  	// Only the environment provisioner can read secrets.
    77  	getCanReadSecrets := common.AuthAlways(authorizer.AuthEnvironManager())
    78  	return &ProvisionerAPI{
    79  		Remover:                common.NewRemover(st, false, getAuthFunc),
    80  		StatusSetter:           common.NewStatusSetter(st, getAuthFunc),
    81  		DeadEnsurer:            common.NewDeadEnsurer(st, getAuthFunc),
    82  		PasswordChanger:        common.NewPasswordChanger(st, getAuthFunc),
    83  		LifeGetter:             common.NewLifeGetter(st, getAuthFunc),
    84  		StateAddresser:         common.NewStateAddresser(st),
    85  		APIAddresser:           common.NewAPIAddresser(st),
    86  		ToolsGetter:            common.NewToolsGetter(st, getAuthFunc),
    87  		EnvironWatcher:         common.NewEnvironWatcher(st, resources, getCanWatch, getCanReadSecrets),
    88  		EnvironMachinesWatcher: common.NewEnvironMachinesWatcher(st, resources, getCanReadSecrets),
    89  		InstanceIdGetter:       common.NewInstanceIdGetter(st, getAuthFunc),
    90  		st:                     st,
    91  		resources:              resources,
    92  		authorizer:             authorizer,
    93  		getAuthFunc:            getAuthFunc,
    94  	}, nil
    95  }
    96  
    97  func (p *ProvisionerAPI) getMachine(canAccess common.AuthFunc, tag string) (*state.Machine, error) {
    98  	if !canAccess(tag) {
    99  		return nil, common.ErrPerm
   100  	}
   101  	entity, err := p.st.FindEntity(tag)
   102  	if err != nil {
   103  		return nil, mask(err, errors.IsNotFoundError)
   104  	}
   105  
   106  	// The authorization function guarantees that the tag represents a
   107  	// machine.
   108  	return entity.(*state.Machine), nil
   109  }
   110  
   111  func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) {
   112  	nothing := params.StringsWatchResult{}
   113  	canAccess, err := p.getAuthFunc()
   114  	if err != nil {
   115  		return nothing, mask(err)
   116  	}
   117  	if !canAccess(arg.MachineTag) {
   118  		return nothing, common.ErrPerm
   119  	}
   120  	_, id, err := names.ParseTag(arg.MachineTag, names.MachineTagKind)
   121  	if err != nil {
   122  		return nothing, mask(err)
   123  	}
   124  	machine, err := p.st.Machine(id)
   125  	if err != nil {
   126  		return nothing, mask(err, errors.IsNotFoundError)
   127  	}
   128  	var watch state.StringsWatcher
   129  	if arg.ContainerType != "" {
   130  		watch = machine.WatchContainers(instance.ContainerType(arg.ContainerType))
   131  	} else {
   132  		watch = machine.WatchAllContainers()
   133  	}
   134  	// Consume the initial event and forward it to the result.
   135  	if changes, ok := <-watch.Changes(); ok {
   136  		return params.StringsWatchResult{
   137  			StringsWatcherId: p.resources.Register(watch),
   138  			Changes:          changes,
   139  		}, nil
   140  	}
   141  	return nothing, watcher.MustErr(watch)
   142  }
   143  
   144  // WatchContainers starts a StringsWatcher to watch containers deployed to
   145  // any machine passed in args.
   146  func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
   147  	result := params.StringsWatchResults{
   148  		Results: make([]params.StringsWatchResult, len(args.Params)),
   149  	}
   150  	for i, arg := range args.Params {
   151  		watcherResult, err := p.watchOneMachineContainers(arg)
   152  		result.Results[i] = watcherResult
   153  		result.Results[i].Error = common.ServerError(err)
   154  	}
   155  	return result, nil
   156  }
   157  
   158  // WatchAllContainers starts a StringsWatcher to watch all containers deployed to
   159  // any machine passed in args.
   160  func (p *ProvisionerAPI) WatchAllContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
   161  	return p.WatchContainers(args)
   162  }
   163  
   164  // SetSupportedContainers updates the list of containers supported by the machines passed in args.
   165  func (p *ProvisionerAPI) SetSupportedContainers(
   166  	args params.MachineContainersParams) (params.ErrorResults, error) {
   167  
   168  	result := params.ErrorResults{
   169  		Results: make([]params.ErrorResult, len(args.Params)),
   170  	}
   171  	for i, arg := range args.Params {
   172  		canAccess, err := p.getAuthFunc()
   173  		if err != nil {
   174  			return result, mask(err)
   175  		}
   176  		machine, err := p.getMachine(canAccess, arg.MachineTag)
   177  		if err != nil {
   178  			result.Results[i].Error = common.ServerError(err)
   179  			continue
   180  		}
   181  		if len(arg.ContainerTypes) == 0 {
   182  			err = machine.SupportsNoContainers()
   183  		} else {
   184  			err = machine.SetSupportedContainers(arg.ContainerTypes)
   185  		}
   186  		if err != nil {
   187  			result.Results[i].Error = common.ServerError(err)
   188  		}
   189  	}
   190  	return result, nil
   191  }
   192  
   193  // ContainerConfig returns information from the environment config that are
   194  // needed for container cloud-init.
   195  func (p *ProvisionerAPI) ContainerConfig() (params.ContainerConfig, error) {
   196  	result := params.ContainerConfig{}
   197  	config, err := p.st.EnvironConfig()
   198  	if err != nil {
   199  		return result, mask(err)
   200  	}
   201  	result.ProviderType = config.Type()
   202  	result.AuthorizedKeys = config.AuthorizedKeys()
   203  	result.SSLHostnameVerification = config.SSLHostnameVerification()
   204  	result.SyslogPort = config.SyslogPort()
   205  	result.Proxy = config.ProxySettings()
   206  	result.AptProxy = config.AptProxySettings()
   207  	return result, nil
   208  }
   209  
   210  // Status returns the status of each given machine entity.
   211  func (p *ProvisionerAPI) Status(args params.Entities) (params.StatusResults, error) {
   212  	result := params.StatusResults{
   213  		Results: make([]params.StatusResult, len(args.Entities)),
   214  	}
   215  	canAccess, err := p.getAuthFunc()
   216  	if err != nil {
   217  		return result, mask(err)
   218  	}
   219  	for i, entity := range args.Entities {
   220  		machine, err := p.getMachine(canAccess, entity.Tag)
   221  		if err == nil {
   222  			r := &result.Results[i]
   223  			r.Status, r.Info, _, err = machine.Status()
   224  		}
   225  		result.Results[i].Error = common.ServerError(err)
   226  	}
   227  	return result, nil
   228  }
   229  
   230  // Series returns the deployed series for each given machine entity.
   231  func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) {
   232  	result := params.StringResults{
   233  		Results: make([]params.StringResult, len(args.Entities)),
   234  	}
   235  	canAccess, err := p.getAuthFunc()
   236  	if err != nil {
   237  		return result, mask(err)
   238  	}
   239  	for i, entity := range args.Entities {
   240  		machine, err := p.getMachine(canAccess, entity.Tag)
   241  		if err == nil {
   242  			result.Results[i].Result = machine.Series()
   243  		}
   244  		result.Results[i].Error = common.ServerError(err)
   245  	}
   246  	return result, nil
   247  }
   248  
   249  // Constraints returns the constraints for each given machine entity.
   250  func (p *ProvisionerAPI) Constraints(args params.Entities) (params.ConstraintsResults, error) {
   251  	result := params.ConstraintsResults{
   252  		Results: make([]params.ConstraintsResult, len(args.Entities)),
   253  	}
   254  	canAccess, err := p.getAuthFunc()
   255  	if err != nil {
   256  		return result, mask(err)
   257  	}
   258  	for i, entity := range args.Entities {
   259  		machine, err := p.getMachine(canAccess, entity.Tag)
   260  		if err == nil {
   261  			var cons constraints.Value
   262  			cons, err = machine.Constraints()
   263  			if err == nil {
   264  				result.Results[i].Constraints = cons
   265  			}
   266  		}
   267  		result.Results[i].Error = common.ServerError(err)
   268  	}
   269  	return result, nil
   270  }
   271  
   272  // SetProvisioned sets the provider specific machine id, nonce and
   273  // metadata for each given machine. Once set, the instance id cannot
   274  // be changed.
   275  func (p *ProvisionerAPI) SetProvisioned(args params.SetProvisioned) (params.ErrorResults, error) {
   276  	result := params.ErrorResults{
   277  		Results: make([]params.ErrorResult, len(args.Machines)),
   278  	}
   279  	canAccess, err := p.getAuthFunc()
   280  	if err != nil {
   281  		return result, mask(err)
   282  	}
   283  	for i, arg := range args.Machines {
   284  		machine, err := p.getMachine(canAccess, arg.Tag)
   285  		if err == nil {
   286  			err = machine.SetProvisioned(arg.InstanceId, arg.Nonce, arg.Characteristics)
   287  		}
   288  		result.Results[i].Error = common.ServerError(err)
   289  	}
   290  	return result, nil
   291  }