github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/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  	"time"
     8  
     9  	"github.com/juju/collections/set"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/loggo"
    12  	"gopkg.in/juju/charm.v6"
    13  	"gopkg.in/juju/names.v2"
    14  
    15  	"github.com/juju/juju/apiserver/common"
    16  	"github.com/juju/juju/apiserver/common/networkingcommon"
    17  	"github.com/juju/juju/apiserver/common/storagecommon"
    18  	"github.com/juju/juju/apiserver/facade"
    19  	"github.com/juju/juju/apiserver/params"
    20  	"github.com/juju/juju/container"
    21  	"github.com/juju/juju/core/constraints"
    22  	"github.com/juju/juju/core/instance"
    23  	"github.com/juju/juju/core/lxdprofile"
    24  	"github.com/juju/juju/core/status"
    25  	"github.com/juju/juju/environs"
    26  	"github.com/juju/juju/environs/config"
    27  	"github.com/juju/juju/environs/context"
    28  	"github.com/juju/juju/network"
    29  	"github.com/juju/juju/network/containerizer"
    30  	"github.com/juju/juju/state"
    31  	"github.com/juju/juju/state/stateenvirons"
    32  	"github.com/juju/juju/state/watcher"
    33  	"github.com/juju/juju/storage"
    34  	"github.com/juju/juju/storage/poolmanager"
    35  )
    36  
    37  var logger = loggo.GetLogger("juju.apiserver.provisioner")
    38  
    39  // ProvisionerAPI provides access to the Provisioner API facade.
    40  type ProvisionerAPI struct {
    41  	*common.ControllerConfigAPI
    42  	*common.Remover
    43  	*common.StatusSetter
    44  	*common.StatusGetter
    45  	*common.DeadEnsurer
    46  	*common.PasswordChanger
    47  	*common.LifeGetter
    48  	*common.StateAddresser
    49  	*common.APIAddresser
    50  	*common.ModelWatcher
    51  	*common.ModelMachinesWatcher
    52  	*common.InstanceIdGetter
    53  	*common.ToolsFinder
    54  	*common.ToolsGetter
    55  	*networkingcommon.NetworkConfigAPI
    56  
    57  	st                      *state.State
    58  	m                       *state.Model
    59  	resources               facade.Resources
    60  	authorizer              facade.Authorizer
    61  	storageProviderRegistry storage.ProviderRegistry
    62  	storagePoolManager      poolmanager.PoolManager
    63  	configGetter            environs.EnvironConfigGetter
    64  	getAuthFunc             common.GetAuthFunc
    65  	getCanModify            common.GetAuthFunc
    66  	providerCallContext     context.ProviderCallContext
    67  }
    68  
    69  // NewProvisionerAPI creates a new server-side ProvisionerAPI facade.
    70  func NewProvisionerAPI(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ProvisionerAPI, error) {
    71  	if !authorizer.AuthMachineAgent() && !authorizer.AuthController() {
    72  		return nil, common.ErrPerm
    73  	}
    74  	getAuthFunc := func() (common.AuthFunc, error) {
    75  		isModelManager := authorizer.AuthController()
    76  		isMachineAgent := authorizer.AuthMachineAgent()
    77  		authEntityTag := authorizer.GetAuthTag()
    78  
    79  		return func(tag names.Tag) bool {
    80  			if isMachineAgent && tag == authEntityTag {
    81  				// A machine agent can always access its own machine.
    82  				return true
    83  			}
    84  			switch tag := tag.(type) {
    85  			case names.MachineTag:
    86  				parentId := state.ParentId(tag.Id())
    87  				if parentId == "" {
    88  					// All top-level machines are accessible by the controller.
    89  					return isModelManager
    90  				}
    91  				// All containers with the authenticated machine as a
    92  				// parent are accessible by it.
    93  				// TODO(dfc) sometimes authEntity tag is nil, which is fine because nil is
    94  				// only equal to nil, but it suggests someone is passing an authorizer
    95  				// with a nil tag.
    96  				return isMachineAgent && names.NewMachineTag(parentId) == authEntityTag
    97  			default:
    98  				return false
    99  			}
   100  		}, nil
   101  	}
   102  	getCanModify := func() (common.AuthFunc, error) {
   103  		return authorizer.AuthOwner, nil
   104  	}
   105  	getAuthOwner := func() (common.AuthFunc, error) {
   106  		return authorizer.AuthOwner, nil
   107  	}
   108  	model, err := st.Model()
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	configGetter := stateenvirons.EnvironConfigGetter{st, model}
   113  	env, err := environs.GetEnviron(configGetter, environs.New)
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  	urlGetter := common.NewToolsURLGetter(model.UUID(), st)
   118  	storageProviderRegistry := stateenvirons.NewStorageProviderRegistry(env)
   119  
   120  	callCtx := state.CallContext(st)
   121  	return &ProvisionerAPI{
   122  		Remover:                 common.NewRemover(st, false, getAuthFunc),
   123  		StatusSetter:            common.NewStatusSetter(st, getAuthFunc),
   124  		StatusGetter:            common.NewStatusGetter(st, getAuthFunc),
   125  		DeadEnsurer:             common.NewDeadEnsurer(st, getAuthFunc),
   126  		PasswordChanger:         common.NewPasswordChanger(st, getAuthFunc),
   127  		LifeGetter:              common.NewLifeGetter(st, getAuthFunc),
   128  		StateAddresser:          common.NewStateAddresser(st),
   129  		APIAddresser:            common.NewAPIAddresser(st, resources),
   130  		ModelWatcher:            common.NewModelWatcher(model, resources, authorizer),
   131  		ModelMachinesWatcher:    common.NewModelMachinesWatcher(st, resources, authorizer),
   132  		ControllerConfigAPI:     common.NewStateControllerConfig(st),
   133  		InstanceIdGetter:        common.NewInstanceIdGetter(st, getAuthFunc),
   134  		ToolsFinder:             common.NewToolsFinder(configGetter, st, urlGetter),
   135  		ToolsGetter:             common.NewToolsGetter(st, configGetter, st, urlGetter, getAuthOwner),
   136  		NetworkConfigAPI:        networkingcommon.NewNetworkConfigAPI(st, callCtx, getCanModify),
   137  		st:                      st,
   138  		m:                       model,
   139  		resources:               resources,
   140  		authorizer:              authorizer,
   141  		configGetter:            configGetter,
   142  		storageProviderRegistry: storageProviderRegistry,
   143  		storagePoolManager:      poolmanager.New(state.NewStateSettings(st), storageProviderRegistry),
   144  		getAuthFunc:             getAuthFunc,
   145  		getCanModify:            getCanModify,
   146  		providerCallContext:     callCtx,
   147  	}, nil
   148  }
   149  
   150  // ProvisionerAPIV4 provides v4 (and v3 for some reason) of the provisioner facade.
   151  type ProvisionerAPIV4 struct {
   152  	*ProvisionerAPIV5
   153  }
   154  
   155  // ProvisionerAPIV5 provides v5 of the provisioner facade.
   156  type ProvisionerAPIV5 struct {
   157  	*ProvisionerAPIV6
   158  }
   159  
   160  // ProvisionerAPIV6 provides v6 of the provisioner facade.
   161  type ProvisionerAPIV6 struct {
   162  	*ProvisionerAPIV7
   163  }
   164  
   165  // ProvisionerAPIV7 provides v7 of the provisioner facade.
   166  type ProvisionerAPIV7 struct {
   167  	*ProvisionerAPI
   168  }
   169  
   170  // NewProvisionerAPIV4 creates a new server-side version 4 Provisioner API facade.
   171  func NewProvisionerAPIV4(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ProvisionerAPIV4, error) {
   172  	provisionerAPI, err := NewProvisionerAPIV5(st, resources, authorizer)
   173  	if err != nil {
   174  		return nil, errors.Trace(err)
   175  	}
   176  	return &ProvisionerAPIV4{provisionerAPI}, nil
   177  }
   178  
   179  // NewProvisionerAPIV5 creates a new server-side Provisioner API facade.
   180  func NewProvisionerAPIV5(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ProvisionerAPIV5, error) {
   181  	provisionerAPI, err := NewProvisionerAPIV6(st, resources, authorizer)
   182  	if err != nil {
   183  		return nil, errors.Trace(err)
   184  	}
   185  	return &ProvisionerAPIV5{provisionerAPI}, nil
   186  }
   187  
   188  // NewProvisionerAPIV6 creates a new server-side Provisioner API facade.
   189  func NewProvisionerAPIV6(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ProvisionerAPIV6, error) {
   190  	provisionerAPI, err := NewProvisionerAPIV7(st, resources, authorizer)
   191  	if err != nil {
   192  		return nil, errors.Trace(err)
   193  	}
   194  	return &ProvisionerAPIV6{provisionerAPI}, nil
   195  }
   196  
   197  // NewProvisionerAPIV7 creates a new server-side Provisioner API facade.
   198  func NewProvisionerAPIV7(st *state.State, resources facade.Resources, authorizer facade.Authorizer) (*ProvisionerAPIV7, error) {
   199  	provisionerAPI, err := NewProvisionerAPI(st, resources, authorizer)
   200  	if err != nil {
   201  		return nil, errors.Trace(err)
   202  	}
   203  	return &ProvisionerAPIV7{provisionerAPI}, nil
   204  }
   205  
   206  func (p *ProvisionerAPI) getMachine(canAccess common.AuthFunc, tag names.MachineTag) (*state.Machine, error) {
   207  	if !canAccess(tag) {
   208  		return nil, common.ErrPerm
   209  	}
   210  	entity, err := p.st.FindEntity(tag)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	// The authorization function guarantees that the tag represents a
   215  	// machine.
   216  	return entity.(*state.Machine), nil
   217  }
   218  
   219  func (p *ProvisionerAPI) watchOneMachineContainers(arg params.WatchContainer) (params.StringsWatchResult, error) {
   220  	nothing := params.StringsWatchResult{}
   221  	canAccess, err := p.getAuthFunc()
   222  	if err != nil {
   223  		return nothing, common.ErrPerm
   224  	}
   225  	tag, err := names.ParseMachineTag(arg.MachineTag)
   226  	if err != nil {
   227  		return nothing, common.ErrPerm
   228  	}
   229  	if !canAccess(tag) {
   230  		return nothing, common.ErrPerm
   231  	}
   232  	machine, err := p.st.Machine(tag.Id())
   233  	if err != nil {
   234  		return nothing, err
   235  	}
   236  	var watch state.StringsWatcher
   237  	if arg.ContainerType != "" {
   238  		watch = machine.WatchContainers(instance.ContainerType(arg.ContainerType))
   239  	} else {
   240  		watch = machine.WatchAllContainers()
   241  	}
   242  	// Consume the initial event and forward it to the result.
   243  	if changes, ok := <-watch.Changes(); ok {
   244  		return params.StringsWatchResult{
   245  			StringsWatcherId: p.resources.Register(watch),
   246  			Changes:          changes,
   247  		}, nil
   248  	}
   249  	return nothing, watcher.EnsureErr(watch)
   250  }
   251  
   252  // WatchContainers starts a StringsWatcher to watch containers deployed to
   253  // any machine passed in args.
   254  func (p *ProvisionerAPI) WatchContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
   255  	result := params.StringsWatchResults{
   256  		Results: make([]params.StringsWatchResult, len(args.Params)),
   257  	}
   258  	for i, arg := range args.Params {
   259  		watcherResult, err := p.watchOneMachineContainers(arg)
   260  		result.Results[i] = watcherResult
   261  		result.Results[i].Error = common.ServerError(err)
   262  	}
   263  	return result, nil
   264  }
   265  
   266  // WatchAllContainers starts a StringsWatcher to watch all containers deployed to
   267  // any machine passed in args.
   268  func (p *ProvisionerAPI) WatchAllContainers(args params.WatchContainers) (params.StringsWatchResults, error) {
   269  	return p.WatchContainers(args)
   270  }
   271  
   272  func (p *ProvisionerAPI) watchOneMachineContainersCharmProfiles(arg params.WatchContainer) (params.StringsWatchResult, error) {
   273  	nothing := params.StringsWatchResult{}
   274  	canAccess, err := p.getAuthFunc()
   275  	if err != nil {
   276  		return nothing, common.ErrPerm
   277  	}
   278  	tag, err := names.ParseMachineTag(arg.MachineTag)
   279  	if err != nil {
   280  		return nothing, common.ErrPerm
   281  	}
   282  	if !canAccess(tag) {
   283  		return nothing, common.ErrPerm
   284  	}
   285  	machine, err := p.st.Machine(tag.Id())
   286  	if err != nil {
   287  		return nothing, err
   288  	}
   289  	var watch state.StringsWatcher
   290  	if arg.ContainerType != "" {
   291  		watch, err = machine.WatchContainersCharmProfiles(instance.ContainerType(arg.ContainerType))
   292  		if err != nil {
   293  			return nothing, common.ErrPerm
   294  		}
   295  	} else {
   296  		return nothing, errors.BadRequestf("ContainerType not specified")
   297  	}
   298  	// Consume the initial event and forward it to the result.
   299  	if changes, ok := <-watch.Changes(); ok {
   300  		return params.StringsWatchResult{
   301  			StringsWatcherId: p.resources.Register(watch),
   302  			Changes:          changes,
   303  		}, nil
   304  	}
   305  	return nothing, watcher.EnsureErr(watch)
   306  }
   307  
   308  // WatchContainersCharmProfiles starts a StringsWatcher to  notifies when
   309  // the provisioner should update the charm profiles used by a container on
   310  // the given machine.
   311  func (p *ProvisionerAPI) WatchContainersCharmProfiles(args params.WatchContainers) (params.StringsWatchResults, error) {
   312  	result := params.StringsWatchResults{
   313  		Results: make([]params.StringsWatchResult, len(args.Params)),
   314  	}
   315  	for i, arg := range args.Params {
   316  		watcherResult, err := p.watchOneMachineContainersCharmProfiles(arg)
   317  		result.Results[i] = watcherResult
   318  		result.Results[i].Error = common.ServerError(err)
   319  	}
   320  	return result, nil
   321  }
   322  
   323  // SetSupportedContainers updates the list of containers supported by the machines passed in args.
   324  func (p *ProvisionerAPI) SetSupportedContainers(args params.MachineContainersParams) (params.ErrorResults, error) {
   325  	result := params.ErrorResults{
   326  		Results: make([]params.ErrorResult, len(args.Params)),
   327  	}
   328  
   329  	canAccess, err := p.getAuthFunc()
   330  	if err != nil {
   331  		return result, err
   332  	}
   333  	for i, arg := range args.Params {
   334  		tag, err := names.ParseMachineTag(arg.MachineTag)
   335  		if err != nil {
   336  			logger.Warningf("SetSupportedContainers called with %q which is not a valid machine tag: %v", arg.MachineTag, err)
   337  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   338  			continue
   339  		}
   340  		machine, err := p.getMachine(canAccess, tag)
   341  		if err != nil {
   342  			result.Results[i].Error = common.ServerError(err)
   343  			continue
   344  		}
   345  		if len(arg.ContainerTypes) == 0 {
   346  			err = machine.SupportsNoContainers()
   347  		} else {
   348  			err = machine.SetSupportedContainers(arg.ContainerTypes)
   349  		}
   350  		if err != nil {
   351  			result.Results[i].Error = common.ServerError(err)
   352  		}
   353  	}
   354  	return result, nil
   355  }
   356  
   357  // ContainerManagerConfig returns information from the model config that is
   358  // needed for configuring the container manager.
   359  func (p *ProvisionerAPI) ContainerManagerConfig(args params.ContainerManagerConfigParams) (params.ContainerManagerConfig, error) {
   360  	var result params.ContainerManagerConfig
   361  	cfg := make(map[string]string)
   362  	cfg[container.ConfigModelUUID] = p.st.ModelUUID()
   363  
   364  	switch args.Type {
   365  	case instance.LXD:
   366  		// TODO(jam): DefaultMTU needs to be handled here
   367  	}
   368  
   369  	mConfig, err := p.m.ModelConfig()
   370  	if err != nil {
   371  		return result, err
   372  	}
   373  	if url, set := mConfig.ContainerImageMetadataURL(); set {
   374  		cfg[config.ContainerImageMetadataURLKey] = url
   375  	}
   376  	cfg[config.ContainerImageStreamKey] = mConfig.ContainerImageStream()
   377  
   378  	result.ManagerConfig = cfg
   379  	return result, nil
   380  }
   381  
   382  // ContainerConfig returns information from the model config that is
   383  // needed for container cloud-init.
   384  func (p *ProvisionerAPI) ContainerConfig() (params.ContainerConfig, error) {
   385  	result := params.ContainerConfig{}
   386  	config, err := p.m.ModelConfig()
   387  	if err != nil {
   388  		return result, err
   389  	}
   390  
   391  	result.UpdateBehavior = &params.UpdateBehavior{
   392  		config.EnableOSRefreshUpdate(),
   393  		config.EnableOSUpgrade(),
   394  	}
   395  	result.ProviderType = config.Type()
   396  	result.AuthorizedKeys = config.AuthorizedKeys()
   397  	result.SSLHostnameVerification = config.SSLHostnameVerification()
   398  	result.LegacyProxy = config.LegacyProxySettings()
   399  	result.JujuProxy = config.JujuProxySettings()
   400  	result.AptProxy = config.AptProxySettings()
   401  	result.AptMirror = config.AptMirror()
   402  	result.CloudInitUserData = config.CloudInitUserData()
   403  	result.ContainerInheritProperties = config.ContainerInheritProperies()
   404  	return result, nil
   405  }
   406  
   407  // ContainerConfig returns information from the model config that is
   408  // needed for container cloud-init.
   409  func (p *ProvisionerAPIV5) ContainerConfig() (params.ContainerConfigV5, error) {
   410  	var empty params.ContainerConfigV5
   411  	cfg, err := p.ProvisionerAPI.ContainerConfig()
   412  	if err != nil {
   413  		return empty, err
   414  	}
   415  
   416  	return params.ContainerConfigV5{
   417  		ProviderType:               cfg.ProviderType,
   418  		AuthorizedKeys:             cfg.AuthorizedKeys,
   419  		SSLHostnameVerification:    cfg.SSLHostnameVerification,
   420  		Proxy:                      cfg.LegacyProxy,
   421  		AptProxy:                   cfg.AptProxy,
   422  		AptMirror:                  cfg.AptMirror,
   423  		CloudInitUserData:          cfg.CloudInitUserData,
   424  		ContainerInheritProperties: cfg.ContainerInheritProperties,
   425  		UpdateBehavior:             cfg.UpdateBehavior,
   426  	}, nil
   427  }
   428  
   429  // MachinesWithTransientErrors returns status data for machines with provisioning
   430  // errors which are transient.
   431  func (p *ProvisionerAPI) MachinesWithTransientErrors() (params.StatusResults, error) {
   432  	var results params.StatusResults
   433  	canAccessFunc, err := p.getAuthFunc()
   434  	if err != nil {
   435  		return results, err
   436  	}
   437  	// TODO (wallyworld) - add state.State API for more efficient machines query
   438  	machines, err := p.st.AllMachines()
   439  	if err != nil {
   440  		return results, err
   441  	}
   442  	for _, machine := range machines {
   443  		if !canAccessFunc(machine.Tag()) {
   444  			continue
   445  		}
   446  		if _, provisionedErr := machine.InstanceId(); provisionedErr == nil {
   447  			// Machine may have been provisioned but machiner hasn't set the
   448  			// status to Started yet.
   449  			continue
   450  		}
   451  		var result params.StatusResult
   452  		statusInfo, err := machine.InstanceStatus()
   453  		if err != nil {
   454  			continue
   455  		}
   456  		result.Status = statusInfo.Status.String()
   457  		result.Info = statusInfo.Message
   458  		result.Data = statusInfo.Data
   459  		if statusInfo.Status != status.Error && statusInfo.Status != status.ProvisioningError {
   460  			continue
   461  		}
   462  		// Transient errors are marked as such in the status data.
   463  		if transient, ok := result.Data["transient"].(bool); !ok || !transient {
   464  			continue
   465  		}
   466  		result.Id = machine.Id()
   467  		result.Life = params.Life(machine.Life().String())
   468  		results.Results = append(results.Results, result)
   469  	}
   470  	return results, nil
   471  }
   472  
   473  // Series returns the deployed series for each given machine entity.
   474  func (p *ProvisionerAPI) Series(args params.Entities) (params.StringResults, error) {
   475  	result := params.StringResults{
   476  		Results: make([]params.StringResult, len(args.Entities)),
   477  	}
   478  	canAccess, err := p.getAuthFunc()
   479  	if err != nil {
   480  		return result, err
   481  	}
   482  	for i, entity := range args.Entities {
   483  		tag, err := names.ParseMachineTag(entity.Tag)
   484  		if err != nil {
   485  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   486  			continue
   487  		}
   488  		machine, err := p.getMachine(canAccess, tag)
   489  		if err == nil {
   490  			result.Results[i].Result = machine.Series()
   491  		}
   492  		result.Results[i].Error = common.ServerError(err)
   493  	}
   494  	return result, nil
   495  }
   496  
   497  // AvailabilityZone returns a provider-specific availability zone for each given machine entity
   498  func (p *ProvisionerAPI) AvailabilityZone(args params.Entities) (params.StringResults, error) {
   499  	result := params.StringResults{
   500  		Results: make([]params.StringResult, len(args.Entities)),
   501  	}
   502  	canAccess, err := p.getAuthFunc()
   503  	if err != nil {
   504  		return result, err
   505  	}
   506  	for i, entity := range args.Entities {
   507  		tag, err := names.ParseMachineTag(entity.Tag)
   508  		if err != nil {
   509  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   510  			continue
   511  		}
   512  		machine, err := p.getMachine(canAccess, tag)
   513  		if err == nil {
   514  			hc, err := machine.HardwareCharacteristics()
   515  			if err == nil {
   516  				if hc.AvailabilityZone != nil {
   517  					result.Results[i].Result = *hc.AvailabilityZone
   518  				} else {
   519  					result.Results[i].Result = ""
   520  				}
   521  			} else {
   522  				result.Results[i].Error = common.ServerError(err)
   523  			}
   524  		}
   525  	}
   526  	return result, nil
   527  }
   528  
   529  // KeepInstance returns the keep-instance value for each given machine entity.
   530  func (p *ProvisionerAPI) KeepInstance(args params.Entities) (params.BoolResults, error) {
   531  	result := params.BoolResults{
   532  
   533  		Results: make([]params.BoolResult, len(args.Entities)),
   534  	}
   535  	canAccess, err := p.getAuthFunc()
   536  	if err != nil {
   537  		return result, err
   538  	}
   539  	for i, entity := range args.Entities {
   540  		tag, err := names.ParseMachineTag(entity.Tag)
   541  		if err != nil {
   542  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   543  			continue
   544  		}
   545  		machine, err := p.getMachine(canAccess, tag)
   546  		if err == nil {
   547  			keep, err := machine.KeepInstance()
   548  			result.Results[i].Result = keep
   549  			result.Results[i].Error = common.ServerError(err)
   550  		}
   551  		result.Results[i].Error = common.ServerError(err)
   552  	}
   553  	return result, nil
   554  }
   555  
   556  // DistributionGroup returns, for each given machine entity,
   557  // a slice of instance.Ids that belong to the same distribution
   558  // group as that machine. This information may be used to
   559  // distribute instances for high availability.
   560  func (p *ProvisionerAPI) DistributionGroup(args params.Entities) (params.DistributionGroupResults, error) {
   561  	result := params.DistributionGroupResults{
   562  		Results: make([]params.DistributionGroupResult, len(args.Entities)),
   563  	}
   564  	canAccess, err := p.getAuthFunc()
   565  	if err != nil {
   566  		return result, err
   567  	}
   568  	for i, entity := range args.Entities {
   569  		tag, err := names.ParseMachineTag(entity.Tag)
   570  		if err != nil {
   571  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   572  			continue
   573  		}
   574  		machine, err := p.getMachine(canAccess, tag)
   575  		if err == nil {
   576  			// If the machine is a controller, return
   577  			// controller instances. Otherwise, return
   578  			// instances with services in common with the machine
   579  			// being provisioned.
   580  			if machine.IsManager() {
   581  				result.Results[i].Result, err = controllerInstances(p.st)
   582  			} else {
   583  				result.Results[i].Result, err = commonServiceInstances(p.st, machine)
   584  			}
   585  		}
   586  		result.Results[i].Error = common.ServerError(err)
   587  	}
   588  	return result, nil
   589  }
   590  
   591  // controllerInstances returns all environ manager instances.
   592  func controllerInstances(st *state.State) ([]instance.Id, error) {
   593  	info, err := st.ControllerInfo()
   594  	if err != nil {
   595  		return nil, err
   596  	}
   597  	instances := make([]instance.Id, 0, len(info.MachineIds))
   598  	for _, id := range info.MachineIds {
   599  		machine, err := st.Machine(id)
   600  		if err != nil {
   601  			return nil, err
   602  		}
   603  		instanceId, err := machine.InstanceId()
   604  		if err == nil {
   605  			instances = append(instances, instanceId)
   606  		} else if !errors.IsNotProvisioned(err) {
   607  			return nil, err
   608  		}
   609  	}
   610  	return instances, nil
   611  }
   612  
   613  // commonServiceInstances returns instances with
   614  // services in common with the specified machine.
   615  func commonServiceInstances(st *state.State, m *state.Machine) ([]instance.Id, error) {
   616  	units, err := m.Units()
   617  	if err != nil {
   618  		return nil, err
   619  	}
   620  	instanceIdSet := make(set.Strings)
   621  	for _, unit := range units {
   622  		if !unit.IsPrincipal() {
   623  			continue
   624  		}
   625  		instanceIds, err := state.ApplicationInstances(st, unit.ApplicationName())
   626  		if err != nil {
   627  			return nil, err
   628  		}
   629  		for _, instanceId := range instanceIds {
   630  			instanceIdSet.Add(string(instanceId))
   631  		}
   632  	}
   633  	instanceIds := make([]instance.Id, instanceIdSet.Size())
   634  	// Sort values to simplify testing.
   635  	for i, instanceId := range instanceIdSet.SortedValues() {
   636  		instanceIds[i] = instance.Id(instanceId)
   637  	}
   638  	return instanceIds, nil
   639  }
   640  
   641  // DistributionGroupByMachineId isn't on the v4 API.
   642  func (p *ProvisionerAPIV4) DistributionGroupByMachineId(_, _ struct{}) {}
   643  
   644  // DistributionGroupByMachineId returns, for each given machine entity,
   645  // a slice of machine.Ids that belong to the same distribution
   646  // group as that machine. This information may be used to
   647  // distribute instances for high availability.
   648  func (p *ProvisionerAPI) DistributionGroupByMachineId(args params.Entities) (params.StringsResults, error) {
   649  	result := params.StringsResults{
   650  		Results: make([]params.StringsResult, len(args.Entities)),
   651  	}
   652  	canAccess, err := p.getAuthFunc()
   653  	if err != nil {
   654  		return params.StringsResults{}, err
   655  	}
   656  	for i, entity := range args.Entities {
   657  		tag, err := names.ParseMachineTag(entity.Tag)
   658  		if err != nil {
   659  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   660  			continue
   661  		}
   662  		machine, err := p.getMachine(canAccess, tag)
   663  		if err == nil {
   664  			// If the machine is a controller, return
   665  			// controller instances. Otherwise, return
   666  			// instances with services in common with the machine
   667  			// being provisioned.
   668  			if machine.IsManager() {
   669  				result.Results[i].Result, err = controllerMachineIds(p.st, machine)
   670  			} else {
   671  				result.Results[i].Result, err = commonApplicationMachineId(p.st, machine)
   672  			}
   673  		}
   674  		result.Results[i].Error = common.ServerError(err)
   675  	}
   676  	return result, nil
   677  }
   678  
   679  // controllerMachineIds returns a slice of all other environ manager machine.Ids.
   680  func controllerMachineIds(st *state.State, m *state.Machine) ([]string, error) {
   681  	info, err := st.ControllerInfo()
   682  	if err != nil {
   683  		return nil, err
   684  	}
   685  	result := set.NewStrings(info.MachineIds...)
   686  	result.Remove(m.Id())
   687  	return result.SortedValues(), nil
   688  }
   689  
   690  // commonApplicationMachineId returns a slice of machine.Ids with
   691  // applications in common with the specified machine.
   692  func commonApplicationMachineId(st *state.State, m *state.Machine) ([]string, error) {
   693  	applications := m.Principals()
   694  	var union set.Strings
   695  	for _, app := range applications {
   696  		machines, err := state.ApplicationMachines(st, app)
   697  		if err != nil {
   698  			return nil, err
   699  		}
   700  		union = union.Union(set.NewStrings(machines...))
   701  	}
   702  	union.Remove(m.Id())
   703  	return union.SortedValues(), nil
   704  }
   705  
   706  // Constraints returns the constraints for each given machine entity.
   707  func (p *ProvisionerAPI) Constraints(args params.Entities) (params.ConstraintsResults, error) {
   708  	result := params.ConstraintsResults{
   709  		Results: make([]params.ConstraintsResult, len(args.Entities)),
   710  	}
   711  	canAccess, err := p.getAuthFunc()
   712  	if err != nil {
   713  		return result, err
   714  	}
   715  	for i, entity := range args.Entities {
   716  		tag, err := names.ParseMachineTag(entity.Tag)
   717  		if err != nil {
   718  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   719  			continue
   720  		}
   721  		machine, err := p.getMachine(canAccess, tag)
   722  		if err == nil {
   723  			var cons constraints.Value
   724  			cons, err = machine.Constraints()
   725  			if err == nil {
   726  				result.Results[i].Constraints = cons
   727  			}
   728  		}
   729  		result.Results[i].Error = common.ServerError(err)
   730  	}
   731  	return result, nil
   732  }
   733  
   734  // SetInstanceInfo sets the provider specific machine id, nonce,
   735  // metadata and network info for each given machine. Once set, the
   736  // instance id cannot be changed.
   737  func (p *ProvisionerAPI) SetInstanceInfo(args params.InstancesInfo) (params.ErrorResults, error) {
   738  	result := params.ErrorResults{
   739  		Results: make([]params.ErrorResult, len(args.Machines)),
   740  	}
   741  	canAccess, err := p.getAuthFunc()
   742  	if err != nil {
   743  		return result, err
   744  	}
   745  	setInstanceInfo := func(arg params.InstanceInfo) error {
   746  		tag, err := names.ParseMachineTag(arg.Tag)
   747  		if err != nil {
   748  			return common.ErrPerm
   749  		}
   750  		machine, err := p.getMachine(canAccess, tag)
   751  		if err != nil {
   752  			return err
   753  		}
   754  		volumes, err := storagecommon.VolumesToState(arg.Volumes)
   755  		if err != nil {
   756  			return err
   757  		}
   758  		volumeAttachments, err := storagecommon.VolumeAttachmentInfosToState(arg.VolumeAttachments)
   759  		if err != nil {
   760  			return err
   761  		}
   762  
   763  		devicesArgs, devicesAddrs := networkingcommon.NetworkConfigsToStateArgs(arg.NetworkConfig)
   764  
   765  		err = machine.SetInstanceInfo(
   766  			arg.InstanceId, arg.DisplayName, arg.Nonce, arg.Characteristics,
   767  			devicesArgs, devicesAddrs,
   768  			volumes, volumeAttachments, arg.CharmProfiles,
   769  		)
   770  		if err != nil {
   771  			return errors.Annotatef(err, "cannot record provisioning info for %q", arg.InstanceId)
   772  		}
   773  		return nil
   774  	}
   775  	for i, arg := range args.Machines {
   776  		err := setInstanceInfo(arg)
   777  		result.Results[i].Error = common.ServerError(err)
   778  	}
   779  	return result, nil
   780  }
   781  
   782  // WatchMachineErrorRetry returns a NotifyWatcher that notifies when
   783  // the provisioner should retry provisioning machines with transient errors.
   784  func (p *ProvisionerAPI) WatchMachineErrorRetry() (params.NotifyWatchResult, error) {
   785  	result := params.NotifyWatchResult{}
   786  	if !p.authorizer.AuthController() {
   787  		return result, common.ErrPerm
   788  	}
   789  	watch := newWatchMachineErrorRetry()
   790  	// Consume any initial event and forward it to the result.
   791  	if _, ok := <-watch.Changes(); ok {
   792  		result.NotifyWatcherId = p.resources.Register(watch)
   793  	} else {
   794  		return result, watcher.EnsureErr(watch)
   795  	}
   796  	return result, nil
   797  }
   798  
   799  // WatchModelMachinesCharmProfiles returns a StringsWatcher that notifies when
   800  // the provisioner should update the charm profiles used by a machine.
   801  func (p *ProvisionerAPI) WatchModelMachinesCharmProfiles() (params.StringsWatchResult, error) {
   802  	result := params.StringsWatchResult{}
   803  	if !p.authorizer.AuthController() {
   804  		return result, common.ErrPerm
   805  	}
   806  	watch, err := p.st.WatchModelMachinesCharmProfiles()
   807  	if err != nil {
   808  		return result, common.ErrPerm
   809  	}
   810  	// Consume any initial event and forward it to the result.
   811  	if changes, ok := <-watch.Changes(); ok {
   812  		result.StringsWatcherId = p.resources.Register(watch)
   813  		result.Changes = changes
   814  	} else {
   815  		return result, watcher.EnsureErr(watch)
   816  	}
   817  	return result, nil
   818  }
   819  
   820  // ReleaseContainerAddresses finds addresses allocated to a container and marks
   821  // them as Dead, to be released and removed. It accepts container tags as
   822  // arguments.
   823  func (p *ProvisionerAPI) ReleaseContainerAddresses(args params.Entities) (params.ErrorResults, error) {
   824  	result := params.ErrorResults{
   825  		Results: make([]params.ErrorResult, len(args.Entities)),
   826  	}
   827  
   828  	canAccess, err := p.getAuthFunc()
   829  	if err != nil {
   830  		logger.Errorf("failed to get an authorisation function: %v", err)
   831  		return result, errors.Trace(err)
   832  	}
   833  	// Loop over the passed container tags.
   834  	for i, entity := range args.Entities {
   835  		tag, err := names.ParseMachineTag(entity.Tag)
   836  		if err != nil {
   837  			logger.Warningf("failed to parse machine tag %q: %v", entity.Tag, err)
   838  			result.Results[i].Error = common.ServerError(common.ErrPerm)
   839  			continue
   840  		}
   841  
   842  		// The auth function (canAccess) checks that the machine is a
   843  		// top level machine (we filter those out next) or that the
   844  		// machine has the host as a parent.
   845  		guest, err := p.getMachine(canAccess, tag)
   846  		if err != nil {
   847  			logger.Warningf("failed to get machine %q: %v", tag, err)
   848  			result.Results[i].Error = common.ServerError(err)
   849  			continue
   850  		} else if !guest.IsContainer() {
   851  			err = errors.Errorf("cannot mark addresses for removal for %q: not a container", tag)
   852  			result.Results[i].Error = common.ServerError(err)
   853  			continue
   854  		}
   855  
   856  		// TODO(dimitern): Release those via the provider once we have
   857  		// Environ.ReleaseContainerAddresses. See LP bug http://pad.lv/1585878
   858  		err = guest.RemoveAllAddresses()
   859  		if err != nil {
   860  			logger.Warningf("failed to remove container %q addresses: %v", tag, err)
   861  			result.Results[i].Error = common.ServerError(err)
   862  			continue
   863  		}
   864  	}
   865  
   866  	return result, nil
   867  }
   868  
   869  // PrepareContainerInterfaceInfo allocates an address and returns information to
   870  // configure networking for a container. It accepts container tags as arguments.
   871  func (p *ProvisionerAPI) PrepareContainerInterfaceInfo(args params.Entities) (
   872  	params.MachineNetworkConfigResults,
   873  	error,
   874  ) {
   875  	return p.prepareOrGetContainerInterfaceInfo(args, false)
   876  }
   877  
   878  // GetContainerInterfaceInfo returns information to configure networking for a
   879  // container. It accepts container tags as arguments.
   880  func (p *ProvisionerAPI) GetContainerInterfaceInfo(args params.Entities) (
   881  	params.MachineNetworkConfigResults,
   882  	error,
   883  ) {
   884  	return p.prepareOrGetContainerInterfaceInfo(args, true)
   885  }
   886  
   887  // Machine is an indirection for use in container provisioning.
   888  //go:generate mockgen -package mocks -destination mocks/machine_mock.go github.com/juju/juju/apiserver/facades/agent/provisioner Machine
   889  //go:generate mockgen -package mocks -destination mocks/containerizer_mock.go github.com/juju/juju/network/containerizer LinkLayerDevice,Unit,Application,Charm
   890  type Machine interface {
   891  	containerizer.Container
   892  	InstanceId() (instance.Id, error)
   893  	IsManual() (bool, error)
   894  	MachineTag() names.MachineTag
   895  	Units() ([]containerizer.Unit, error)
   896  }
   897  
   898  // perContainerHandler is the interface we need to trigger processing on
   899  // every container passed in as a list of things to process.
   900  type perContainerHandler interface {
   901  	// ProcessOneContainer is called once we've assured ourselves that all of
   902  	// the access permissions are correct and the container is ready to be
   903  	// processed.
   904  	// env is the Environment you are working, idx is the index for this
   905  	// container (used for deciding where to store results), host is the
   906  	// machine that is hosting the container.
   907  	// Any errors that are returned from ProcessOneContainer will be turned
   908  	// into ServerError and handed to SetError
   909  	ProcessOneContainer(env environs.Environ, callContext context.ProviderCallContext, idx int, host, guest Machine) error
   910  	// SetError will be called whenever there is a problem with the a given
   911  	// request. Generally this just does result.Results[i].Error = error
   912  	// but the Result type is opaque so we can't do it ourselves.
   913  	SetError(resultIndex int, err error)
   914  	// ConfigType indicates the type of config the handler is getting for
   915  	// for error messaging.
   916  	ConfigType() string
   917  }
   918  
   919  func (p *ProvisionerAPI) processEachContainer(args params.Entities, handler perContainerHandler) error {
   920  	env, hostMachine, canAccess, err := p.prepareContainerAccessEnvironment()
   921  	if err != nil {
   922  		// Overall error
   923  		return errors.Trace(err)
   924  	}
   925  	_, err = hostMachine.InstanceId()
   926  	if errors.IsNotProvisioned(err) {
   927  		err = errors.NotProvisionedf("cannot prepare container %s config: host machine %q", handler.ConfigType(), hostMachine)
   928  		return err
   929  	} else if err != nil {
   930  		return errors.Trace(err)
   931  	}
   932  
   933  	for i, entity := range args.Entities {
   934  		machineTag, err := names.ParseMachineTag(entity.Tag)
   935  		if err != nil {
   936  			handler.SetError(i, err)
   937  			continue
   938  		}
   939  		// The auth function (canAccess) checks that the machine is a
   940  		// top level machine (we filter those out next) or that the
   941  		// machine has the host as a parent.
   942  		guest, err := p.getMachine(canAccess, machineTag)
   943  		if err != nil {
   944  			handler.SetError(i, err)
   945  			continue
   946  		} else if !guest.IsContainer() {
   947  			err = errors.Errorf("cannot prepare %s config for %q: not a container", handler.ConfigType(), machineTag)
   948  			handler.SetError(i, err)
   949  			continue
   950  		}
   951  
   952  		if err := handler.ProcessOneContainer(
   953  			env, p.providerCallContext, i,
   954  			&containerizer.MachineShim{Machine: hostMachine},
   955  			&containerizer.MachineShim{Machine: guest},
   956  		); err != nil {
   957  			handler.SetError(i, err)
   958  			continue
   959  		}
   960  	}
   961  	return nil
   962  }
   963  
   964  type prepareOrGetContext struct {
   965  	result   params.MachineNetworkConfigResults
   966  	maintain bool
   967  }
   968  
   969  // Implements perContainerHandler.SetError
   970  func (ctx *prepareOrGetContext) SetError(idx int, err error) {
   971  	ctx.result.Results[idx].Error = common.ServerError(err)
   972  }
   973  
   974  // Implements perContainerHandler.ConfigType
   975  func (ctx *prepareOrGetContext) ConfigType() string {
   976  	return "network"
   977  }
   978  
   979  // Implements perContainerHandler.ProcessOneContainer
   980  func (ctx *prepareOrGetContext) ProcessOneContainer(
   981  	env environs.Environ, callContext context.ProviderCallContext, idx int, host, guest Machine,
   982  ) error {
   983  	instanceId, err := guest.InstanceId()
   984  	if ctx.maintain {
   985  		if err == nil {
   986  			// Since we want to configure and create NICs on the
   987  			// container before it starts, it must also be not
   988  			// provisioned yet.
   989  			return errors.Errorf("container %q already provisioned as %q", guest.Id(), instanceId)
   990  		}
   991  	}
   992  	// The only error we allow is NotProvisioned
   993  	if err != nil && !errors.IsNotProvisioned(err) {
   994  		return errors.Trace(err)
   995  	}
   996  
   997  	bridgePolicy := containerizer.BridgePolicy{
   998  		NetBondReconfigureDelay:   env.Config().NetBondReconfigureDelay(),
   999  		ContainerNetworkingMethod: env.Config().ContainerNetworkingMethod(),
  1000  	}
  1001  
  1002  	// TODO(jam): 2017-01-31 PopulateContainerLinkLayerDevices should really
  1003  	// just be returning the ones we'd like to exist, and then we turn those
  1004  	// into things we'd like to tell the Host machine to create, and then *it*
  1005  	// reports back what actually exists when its done.
  1006  	if err := bridgePolicy.PopulateContainerLinkLayerDevices(host, guest); err != nil {
  1007  		return errors.Trace(err)
  1008  	}
  1009  
  1010  	containerDevices, err := guest.AllLinkLayerDevices()
  1011  	if err != nil {
  1012  		return errors.Trace(err)
  1013  	}
  1014  
  1015  	// We do not ask the provider to allocate addresses for manually provisioned
  1016  	// machines as we do not expect such machines to be recognised (LP:1796106).
  1017  	askProviderForAddress := false
  1018  	hostIsManual, err := host.IsManual()
  1019  	if err != nil {
  1020  		return errors.Trace(err)
  1021  	}
  1022  	if !hostIsManual {
  1023  		askProviderForAddress = environs.SupportsContainerAddresses(callContext, env)
  1024  	}
  1025  
  1026  	preparedInfo := make([]network.InterfaceInfo, len(containerDevices))
  1027  	for j, device := range containerDevices {
  1028  		parentDevice, err := device.ParentDevice()
  1029  		if err != nil || parentDevice == nil {
  1030  			return errors.Errorf(
  1031  				"cannot get parent %q of container device %q: %v",
  1032  				device.ParentName(), device.Name(), err,
  1033  			)
  1034  		}
  1035  		parentAddrs, err := parentDevice.Addresses()
  1036  		if err != nil {
  1037  			return errors.Trace(err)
  1038  		}
  1039  
  1040  		info := network.InterfaceInfo{
  1041  			InterfaceName:       device.Name(),
  1042  			MACAddress:          device.MACAddress(),
  1043  			ConfigType:          network.ConfigManual,
  1044  			InterfaceType:       network.InterfaceType(device.Type()),
  1045  			NoAutoStart:         !device.IsAutoStart(),
  1046  			Disabled:            !device.IsUp(),
  1047  			MTU:                 int(device.MTU()),
  1048  			ParentInterfaceName: parentDevice.Name(),
  1049  		}
  1050  
  1051  		if len(parentAddrs) > 0 {
  1052  			logger.Debugf("host machine device %q has addresses %v", parentDevice.Name(), parentAddrs)
  1053  			firstAddress := parentAddrs[0]
  1054  			if askProviderForAddress {
  1055  				parentDeviceSubnet, err := firstAddress.Subnet()
  1056  				if err != nil {
  1057  					return errors.Annotatef(err,
  1058  						"cannot get subnet %q used by address %q of host machine device %q",
  1059  						firstAddress.SubnetCIDR(), firstAddress.Value(), parentDevice.Name(),
  1060  					)
  1061  				}
  1062  				info.ConfigType = network.ConfigStatic
  1063  				info.CIDR = parentDeviceSubnet.CIDR()
  1064  				info.ProviderSubnetId = parentDeviceSubnet.ProviderId()
  1065  				info.VLANTag = parentDeviceSubnet.VLANTag()
  1066  				info.IsDefaultGateway = firstAddress.IsDefaultGateway()
  1067  			} else {
  1068  				info.ConfigType = network.ConfigDHCP
  1069  				info.CIDR = firstAddress.SubnetCIDR()
  1070  				info.ProviderSubnetId = ""
  1071  				info.VLANTag = 0
  1072  			}
  1073  		} else {
  1074  			logger.Infof("host machine device %q has no addresses %v", parentDevice.Name(), parentAddrs)
  1075  			// TODO(jam): 2017-02-15, have a concrete test for this case, as it
  1076  			// seems to be the common case in the wild.
  1077  			info.ConfigType = network.ConfigDHCP
  1078  			info.ProviderSubnetId = ""
  1079  			info.VLANTag = 0
  1080  		}
  1081  
  1082  		logger.Tracef("prepared info for container interface %q: %+v", info.InterfaceName, info)
  1083  		preparedInfo[j] = info
  1084  	}
  1085  
  1086  	hostInstanceId, err := host.InstanceId()
  1087  	if err != nil {
  1088  		// this should have already been checked in the processEachContainer helper
  1089  		return errors.Trace(err)
  1090  	}
  1091  
  1092  	allocatedInfo := preparedInfo
  1093  	if askProviderForAddress {
  1094  		// supportContainerAddresses already checks that we can cast to an environ.Networking
  1095  		networking := env.(environs.Networking)
  1096  		allocatedInfo, err = networking.AllocateContainerAddresses(
  1097  			callContext, hostInstanceId, guest.MachineTag(), preparedInfo)
  1098  		if err != nil {
  1099  			return errors.Trace(err)
  1100  		}
  1101  		logger.Debugf("got allocated info from provider: %+v", allocatedInfo)
  1102  	} else {
  1103  		logger.Debugf("using dhcp allocated addresses")
  1104  	}
  1105  
  1106  	allocatedConfig := networkingcommon.NetworkConfigFromInterfaceInfo(allocatedInfo)
  1107  	logger.Debugf("allocated network config: %+v", allocatedConfig)
  1108  	ctx.result.Results[idx].Config = allocatedConfig
  1109  	return nil
  1110  }
  1111  
  1112  func (p *ProvisionerAPI) prepareOrGetContainerInterfaceInfo(args params.Entities, maintain bool) (params.MachineNetworkConfigResults, error) {
  1113  	ctx := &prepareOrGetContext{
  1114  		result: params.MachineNetworkConfigResults{
  1115  			Results: make([]params.MachineNetworkConfigResult, len(args.Entities)),
  1116  		},
  1117  		maintain: maintain,
  1118  	}
  1119  
  1120  	if err := p.processEachContainer(args, ctx); err != nil {
  1121  		return ctx.result, errors.Trace(err)
  1122  	}
  1123  	return ctx.result, nil
  1124  }
  1125  
  1126  // prepareContainerAccessEnvironment retrieves the environment, host machine, and access
  1127  // for working with containers.
  1128  func (p *ProvisionerAPI) prepareContainerAccessEnvironment() (environs.Environ, *state.Machine, common.AuthFunc, error) {
  1129  	env, err := environs.GetEnviron(p.configGetter, environs.New)
  1130  	if err != nil {
  1131  		return nil, nil, nil, errors.Trace(err)
  1132  	}
  1133  	// TODO(jam): 2017-02-01 NetworkingEnvironFromModelConfig used to do this, but it doesn't feel good
  1134  	if env.Config().Type() == "dummy" {
  1135  		return nil, nil, nil, errors.NotSupportedf("dummy provider network config")
  1136  	}
  1137  
  1138  	canAccess, err := p.getAuthFunc()
  1139  	if err != nil {
  1140  		return nil, nil, nil, errors.Annotate(err, "cannot authenticate request")
  1141  	}
  1142  	hostAuthTag := p.authorizer.GetAuthTag()
  1143  	if hostAuthTag == nil {
  1144  		return nil, nil, nil, errors.Errorf("authenticated entity tag is nil")
  1145  	}
  1146  	hostTag, err := names.ParseMachineTag(hostAuthTag.String())
  1147  	if err != nil {
  1148  		return nil, nil, nil, errors.Trace(err)
  1149  	}
  1150  	host, err := p.getMachine(canAccess, hostTag)
  1151  	if err != nil {
  1152  		return nil, nil, nil, errors.Trace(err)
  1153  	}
  1154  	return env, host, canAccess, nil
  1155  }
  1156  
  1157  type hostChangesContext struct {
  1158  	result params.HostNetworkChangeResults
  1159  }
  1160  
  1161  // Implements perContainerHandler.ProcessOneContainer
  1162  func (ctx *hostChangesContext) ProcessOneContainer(
  1163  	env environs.Environ, callContext context.ProviderCallContext, idx int, host, guest Machine,
  1164  ) error {
  1165  	bridgePolicy := containerizer.BridgePolicy{
  1166  		NetBondReconfigureDelay:   env.Config().NetBondReconfigureDelay(),
  1167  		ContainerNetworkingMethod: env.Config().ContainerNetworkingMethod(),
  1168  	}
  1169  	bridges, reconfigureDelay, err := bridgePolicy.FindMissingBridgesForContainer(host, guest)
  1170  	if err != nil {
  1171  		return err
  1172  	}
  1173  
  1174  	ctx.result.Results[idx].ReconfigureDelay = reconfigureDelay
  1175  	for _, bridgeInfo := range bridges {
  1176  		ctx.result.Results[idx].NewBridges = append(
  1177  			ctx.result.Results[idx].NewBridges,
  1178  			params.DeviceBridgeInfo{
  1179  				HostDeviceName: bridgeInfo.DeviceName,
  1180  				BridgeName:     bridgeInfo.BridgeName,
  1181  				MACAddress:     bridgeInfo.MACAddress,
  1182  			})
  1183  	}
  1184  	return nil
  1185  }
  1186  
  1187  // Implements perContainerHandler.SetError
  1188  func (ctx *hostChangesContext) SetError(idx int, err error) {
  1189  	ctx.result.Results[idx].Error = common.ServerError(err)
  1190  }
  1191  
  1192  // Implements perContainerHandler.ConfigType
  1193  func (ctx *hostChangesContext) ConfigType() string {
  1194  	return "network"
  1195  }
  1196  
  1197  // HostChangesForContainers returns the set of changes that need to be done
  1198  // to the host machine to prepare it for the containers to be created.
  1199  // Pass in a list of the containers that you want the changes for.
  1200  func (p *ProvisionerAPI) HostChangesForContainers(args params.Entities) (params.HostNetworkChangeResults, error) {
  1201  	ctx := &hostChangesContext{
  1202  		result: params.HostNetworkChangeResults{
  1203  			Results: make([]params.HostNetworkChange, len(args.Entities)),
  1204  		},
  1205  	}
  1206  	if err := p.processEachContainer(args, ctx); err != nil {
  1207  		return ctx.result, errors.Trace(err)
  1208  	}
  1209  	return ctx.result, nil
  1210  }
  1211  
  1212  type containerProfileContext struct {
  1213  	result    params.ContainerProfileResults
  1214  	modelName string
  1215  }
  1216  
  1217  // Implements perContainerHandler.ProcessOneContainer
  1218  func (ctx *containerProfileContext) ProcessOneContainer(
  1219  	_ environs.Environ, _ context.ProviderCallContext, idx int, _, guest Machine,
  1220  ) error {
  1221  	units, err := guest.Units()
  1222  	if err != nil {
  1223  		ctx.result.Results[idx].Error = common.ServerError(err)
  1224  		return errors.Trace(err)
  1225  	}
  1226  	var resPro []*params.ContainerLXDProfile
  1227  	for _, unit := range units {
  1228  		app, err := unit.Application()
  1229  		if err != nil {
  1230  			ctx.SetError(idx, err)
  1231  			return errors.Trace(err)
  1232  		}
  1233  		ch, _, err := app.Charm()
  1234  		if err != nil {
  1235  			ctx.SetError(idx, err)
  1236  			return errors.Trace(err)
  1237  		}
  1238  		profile := ch.LXDProfile()
  1239  		if profile == nil || (profile != nil && profile.Empty()) {
  1240  			logger.Tracef("no profile to return for %q", unit.Name())
  1241  			continue
  1242  		}
  1243  		resPro = append(resPro, &params.ContainerLXDProfile{
  1244  			Profile: params.CharmLXDProfile{
  1245  				Config:      profile.Config,
  1246  				Description: profile.Description,
  1247  				Devices:     profile.Devices,
  1248  			},
  1249  			Name: lxdprofile.Name(ctx.modelName, app.Name(), ch.Revision()),
  1250  		})
  1251  	}
  1252  
  1253  	ctx.result.Results[idx].LXDProfiles = resPro
  1254  	return nil
  1255  }
  1256  
  1257  // Implements perContainerHandler.SetError
  1258  func (ctx *containerProfileContext) SetError(idx int, err error) {
  1259  	ctx.result.Results[idx].Error = common.ServerError(err)
  1260  }
  1261  
  1262  // Implements perContainerHandler.ConfigType
  1263  func (ctx *containerProfileContext) ConfigType() string {
  1264  	return "LXD profile"
  1265  }
  1266  
  1267  // GetContainerProfileInfo returns information to configure a lxd profile(s) for a
  1268  // container based on the charms deployed to the container. It accepts container
  1269  // tags as arguments. Unlike machineLXDProfileNames which has the environ
  1270  // write the lxd profiles and returns the names of profiles already written.
  1271  func (p *ProvisionerAPI) GetContainerProfileInfo(args params.Entities) (params.ContainerProfileResults, error) {
  1272  	ctx := &containerProfileContext{
  1273  		result: params.ContainerProfileResults{
  1274  			Results: make([]params.ContainerProfileResult, len(args.Entities)),
  1275  		},
  1276  		modelName: p.m.Name(),
  1277  	}
  1278  	if err := p.processEachContainer(args, ctx); err != nil {
  1279  		return ctx.result, errors.Trace(err)
  1280  	}
  1281  	return ctx.result, nil
  1282  }
  1283  
  1284  // InstanceStatus returns the instance status for each given entity.
  1285  // Only machine tags are accepted.
  1286  func (p *ProvisionerAPI) InstanceStatus(args params.Entities) (params.StatusResults, error) {
  1287  	result := params.StatusResults{
  1288  		Results: make([]params.StatusResult, len(args.Entities)),
  1289  	}
  1290  	canAccess, err := p.getAuthFunc()
  1291  	if err != nil {
  1292  		logger.Errorf("failed to get an authorisation function: %v", err)
  1293  		return result, errors.Trace(err)
  1294  	}
  1295  	for i, arg := range args.Entities {
  1296  		mTag, err := names.ParseMachineTag(arg.Tag)
  1297  		if err != nil {
  1298  			logger.Warningf("InstanceStatus called with %q which is not a valid machine tag: %v", arg.Tag, err)
  1299  			result.Results[i].Error = common.ServerError(common.ErrPerm)
  1300  			continue
  1301  		}
  1302  		machine, err := p.getMachine(canAccess, mTag)
  1303  		if err == nil {
  1304  			var statusInfo status.StatusInfo
  1305  			statusInfo, err = machine.InstanceStatus()
  1306  			result.Results[i].Status = statusInfo.Status.String()
  1307  			result.Results[i].Info = statusInfo.Message
  1308  			result.Results[i].Data = statusInfo.Data
  1309  			result.Results[i].Since = statusInfo.Since
  1310  		}
  1311  		result.Results[i].Error = common.ServerError(err)
  1312  	}
  1313  	return result, nil
  1314  }
  1315  
  1316  func (p *ProvisionerAPI) setOneInstanceStatus(canAccess common.AuthFunc, arg params.EntityStatusArgs) error {
  1317  	logger.Debugf("SetInstanceStatus called with: %#v", arg)
  1318  	mTag, err := names.ParseMachineTag(arg.Tag)
  1319  	if err != nil {
  1320  		logger.Warningf("SetInstanceStatus called with %q which is not a valid machine tag: %v", arg.Tag, err)
  1321  		return common.ErrPerm
  1322  	}
  1323  	machine, err := p.getMachine(canAccess, mTag)
  1324  	if err != nil {
  1325  		logger.Debugf("SetInstanceStatus unable to get machine %q", mTag)
  1326  		return err
  1327  	}
  1328  	// TODO(perrito666) 2016-05-02 lp:1558657
  1329  	now := time.Now()
  1330  	s := status.StatusInfo{
  1331  		Status:  status.Status(arg.Status),
  1332  		Message: arg.Info,
  1333  		Data:    arg.Data,
  1334  		Since:   &now,
  1335  	}
  1336  
  1337  	// TODO(jam): 2017-01-29 These two status should be set in a single
  1338  	//	transaction, not in two separate transactions. Otherwise you can see
  1339  	//	one toggle, but not the other.
  1340  	if err = machine.SetInstanceStatus(s); err != nil {
  1341  		logger.Debugf("failed to SetInstanceStatus for %q: %v", mTag, err)
  1342  		return err
  1343  	}
  1344  	if status.Status(arg.Status) == status.ProvisioningError ||
  1345  		status.Status(arg.Status) == status.Error {
  1346  		s.Status = status.Error
  1347  		logger.Debugf("SetInstanceStatus triggering SetStatus for %#v", s)
  1348  		if err = machine.SetStatus(s); err != nil {
  1349  			return err
  1350  		}
  1351  	}
  1352  	return nil
  1353  }
  1354  
  1355  // SetInstanceStatus updates the instance status for each given
  1356  // entity. Only machine tags are accepted.
  1357  func (p *ProvisionerAPI) SetInstanceStatus(args params.SetStatus) (params.ErrorResults, error) {
  1358  	result := params.ErrorResults{
  1359  		Results: make([]params.ErrorResult, len(args.Entities)),
  1360  	}
  1361  	canAccess, err := p.getAuthFunc()
  1362  	if err != nil {
  1363  		logger.Errorf("failed to get an authorisation function: %v", err)
  1364  		return result, errors.Trace(err)
  1365  	}
  1366  	for i, arg := range args.Entities {
  1367  		err = p.setOneInstanceStatus(canAccess, arg)
  1368  		result.Results[i].Error = common.ServerError(err)
  1369  	}
  1370  	return result, nil
  1371  }
  1372  
  1373  // MarkMachinesForRemoval indicates that the specified machines are
  1374  // ready to have any provider-level resources cleaned up and then be
  1375  // removed.
  1376  func (p *ProvisionerAPI) MarkMachinesForRemoval(machines params.Entities) (params.ErrorResults, error) {
  1377  	results := make([]params.ErrorResult, len(machines.Entities))
  1378  	canAccess, err := p.getAuthFunc()
  1379  	if err != nil {
  1380  		logger.Errorf("failed to get an authorisation function: %v", err)
  1381  		return params.ErrorResults{}, errors.Trace(err)
  1382  	}
  1383  	for i, machine := range machines.Entities {
  1384  		results[i].Error = common.ServerError(p.markOneMachineForRemoval(machine.Tag, canAccess))
  1385  	}
  1386  	return params.ErrorResults{Results: results}, nil
  1387  }
  1388  
  1389  func (p *ProvisionerAPI) markOneMachineForRemoval(machineTag string, canAccess common.AuthFunc) error {
  1390  	mTag, err := names.ParseMachineTag(machineTag)
  1391  	if err != nil {
  1392  		return errors.Trace(err)
  1393  	}
  1394  	machine, err := p.getMachine(canAccess, mTag)
  1395  	if err != nil {
  1396  		return errors.Trace(err)
  1397  	}
  1398  	return machine.MarkForRemoval()
  1399  }
  1400  
  1401  func (p *ProvisionerAPI) SetHostMachineNetworkConfig(args params.SetMachineNetworkConfig) error {
  1402  	return p.SetObservedNetworkConfig(args)
  1403  }
  1404  
  1405  // CACert returns the certificate used to validate the state connection.
  1406  func (a *ProvisionerAPI) CACert() (params.BytesResult, error) {
  1407  	cfg, err := a.st.ControllerConfig()
  1408  	if err != nil {
  1409  		return params.BytesResult{}, errors.Trace(err)
  1410  	}
  1411  	caCert, _ := cfg.CACert()
  1412  	return params.BytesResult{Result: []byte(caCert)}, nil
  1413  }
  1414  
  1415  // CharmProfileChangeInfo retrieves the info necessary to change a charm
  1416  // profile used by a machine.
  1417  func (p *ProvisionerAPI) CharmProfileChangeInfo(machines params.Entities) (params.ProfileChangeResults, error) {
  1418  	results := make([]params.ProfileChangeResult, len(machines.Entities))
  1419  	canAccess, err := p.getAuthFunc()
  1420  	if err != nil {
  1421  		logger.Errorf("failed to get an authorisation function: %v", err)
  1422  		return params.ProfileChangeResults{}, errors.Trace(err)
  1423  	}
  1424  	for i, machine := range machines.Entities {
  1425  		mTag, err := names.ParseMachineTag(machine.Tag)
  1426  		if err != nil {
  1427  			results[i].Error = common.ServerError(err)
  1428  			continue
  1429  		}
  1430  		machine, err := p.getMachine(canAccess, mTag)
  1431  		if err != nil {
  1432  			results[i].Error = common.ServerError(err)
  1433  			continue
  1434  		}
  1435  		result, err := machineChangeProfileChangeInfo(&profileMachineShim{Machine: machine}, &profileBackendShim{p.st})
  1436  		if err != nil {
  1437  			results[i].Error = common.ServerError(err)
  1438  			continue
  1439  		}
  1440  		results[i] = result
  1441  	}
  1442  	return params.ProfileChangeResults{Results: results}, nil
  1443  }
  1444  
  1445  func machineChangeProfileChangeInfo(machine ProfileMachine, st ProfileBackend) (params.ProfileChangeResult, error) {
  1446  	nothing := params.ProfileChangeResult{}
  1447  
  1448  	appName, err := machine.UpgradeCharmProfileApplication()
  1449  	if err != nil {
  1450  		return nothing, errors.Trace(err)
  1451  	}
  1452  	if appName == "" {
  1453  		return nothing, errors.Trace(errors.New("no appname for profile charm upgrade"))
  1454  	}
  1455  	profileNames, err := machine.CharmProfiles()
  1456  	if err != nil {
  1457  		return nothing, errors.Trace(err)
  1458  	}
  1459  	// If oldProfileName ends up as an empty string,
  1460  	// the charm has added an lxd-profile where it didn't have one before.
  1461  	oldProfileName, err := lxdprofile.MatchProfileNameByAppName(profileNames, appName)
  1462  	if err != nil {
  1463  		return nothing, errors.Trace(err)
  1464  	}
  1465  
  1466  	url, err := machine.UpgradeCharmProfileCharmURL()
  1467  	if err != nil {
  1468  		return nothing, errors.Trace(err)
  1469  	}
  1470  	switch {
  1471  	case url == "" && oldProfileName != "":
  1472  		// Remove the old profile from the machine,
  1473  		// the unit is being removed.
  1474  		return params.ProfileChangeResult{
  1475  			OldProfileName: oldProfileName,
  1476  		}, nil
  1477  	case url == "":
  1478  		return nothing, errors.Trace(errors.New("no url for profile charm upgrade, no profile to remove"))
  1479  	}
  1480  
  1481  	chURL, err := charm.ParseURL(url)
  1482  	if err != nil {
  1483  		return nothing, errors.Trace(err)
  1484  	}
  1485  
  1486  	ch, err := st.Charm(chURL)
  1487  	if err != nil {
  1488  		return nothing, errors.Trace(err)
  1489  	}
  1490  	var profile *params.CharmLXDProfile
  1491  	chProfile := ch.LXDProfile()
  1492  	if chProfile != nil && !chProfile.Empty() {
  1493  		p := params.CharmLXDProfile(*chProfile)
  1494  		profile = &p
  1495  	}
  1496  	chRev := ch.Revision()
  1497  
  1498  	var newProfileName string
  1499  	switch {
  1500  	case oldProfileName == "":
  1501  		newProfileName = lxdprofile.Name(machine.ModelName(), appName, chRev)
  1502  		logger.Tracef("new profile %s is being added to machine-%s", machine.Id(), newProfileName)
  1503  	case profile == nil:
  1504  		logger.Tracef("profile %s is being removed from machine-%s, no replacement", machine.Id(), oldProfileName)
  1505  		newProfileName = ""
  1506  	default:
  1507  		newProfileName, err = lxdprofile.ProfileReplaceRevision(oldProfileName, chRev)
  1508  		if err != nil {
  1509  			return nothing, errors.Trace(err)
  1510  		}
  1511  		logger.Tracef("profile %s is being replaced with %s on machine-%s", oldProfileName, newProfileName, machine.Id())
  1512  	}
  1513  
  1514  	meta := ch.Meta()
  1515  
  1516  	return params.ProfileChangeResult{
  1517  		OldProfileName: oldProfileName,
  1518  		NewProfileName: newProfileName,
  1519  		Profile:        profile,
  1520  		Subordinate:    meta.Subordinate,
  1521  	}, nil
  1522  }
  1523  
  1524  // SetCharmProfiles records the given slice of charm profile names.
  1525  func (p *ProvisionerAPI) SetCharmProfiles(args params.SetProfileArgs) (params.ErrorResults, error) {
  1526  	results := make([]params.ErrorResult, len(args.Args))
  1527  	canAccess, err := p.getAuthFunc()
  1528  	if err != nil {
  1529  		logger.Errorf("failed to get an authorisation function: %v", err)
  1530  		return params.ErrorResults{}, errors.Trace(err)
  1531  	}
  1532  	for i, a := range args.Args {
  1533  		results[i].Error = common.ServerError(p.setOneMachineCharmProfiles(a.Entity.Tag, a.Profiles, canAccess))
  1534  	}
  1535  	return params.ErrorResults{Results: results}, nil
  1536  }
  1537  
  1538  func (p *ProvisionerAPI) setOneMachineCharmProfiles(machineTag string, profiles []string, canAccess common.AuthFunc) error {
  1539  	mTag, err := names.ParseMachineTag(machineTag)
  1540  	if err != nil {
  1541  		return errors.Trace(err)
  1542  	}
  1543  	machine, err := p.getMachine(canAccess, mTag)
  1544  	if err != nil {
  1545  		return errors.Trace(err)
  1546  	}
  1547  	return machine.SetCharmProfiles(profiles)
  1548  }
  1549  
  1550  // SetUpgradeCharmProfileComplete recorded that the result of updating
  1551  // the machine's charm profile(s)
  1552  func (p *ProvisionerAPI) SetUpgradeCharmProfileComplete(args params.SetProfileUpgradeCompleteArgs) (params.ErrorResults, error) {
  1553  	results := make([]params.ErrorResult, len(args.Args))
  1554  	canAccess, err := p.getAuthFunc()
  1555  	if err != nil {
  1556  		logger.Errorf("failed to get an authorisation function: %v", err)
  1557  		return params.ErrorResults{}, errors.Trace(err)
  1558  	}
  1559  	for i, a := range args.Args {
  1560  		results[i].Error = common.ServerError(p.oneUpgradeCharmProfileComplete(a.Entity.Tag, a.Message, canAccess))
  1561  	}
  1562  	return params.ErrorResults{Results: results}, nil
  1563  }
  1564  
  1565  func (p *ProvisionerAPI) oneUpgradeCharmProfileComplete(machineTag string, msg string, canAccess common.AuthFunc) error {
  1566  	mTag, err := names.ParseMachineTag(machineTag)
  1567  	if err != nil {
  1568  		return errors.Trace(err)
  1569  	}
  1570  	machine, err := p.getMachine(canAccess, mTag)
  1571  	if err != nil {
  1572  		return errors.Trace(err)
  1573  	}
  1574  	return machine.SetUpgradeCharmProfileComplete(msg)
  1575  }
  1576  
  1577  // RemoveUpgradeCharmProfileData completely removes the instance charm profile
  1578  // data for a machine, even if the machine is dead.
  1579  func (p *ProvisionerAPI) RemoveUpgradeCharmProfileData(args params.Entities) (params.ErrorResults, error) {
  1580  	results := make([]params.ErrorResult, len(args.Entities))
  1581  	canAccess, err := p.getAuthFunc()
  1582  	if err != nil {
  1583  		logger.Errorf("failed to get an authorisation function: %v", err)
  1584  		return params.ErrorResults{}, errors.Trace(err)
  1585  	}
  1586  	for i, a := range args.Entities {
  1587  		results[i].Error = common.ServerError(p.oneRemoveUpgradeCharmProfileData(a.Tag, canAccess))
  1588  	}
  1589  	return params.ErrorResults{Results: results}, nil
  1590  }
  1591  
  1592  func (p *ProvisionerAPI) oneRemoveUpgradeCharmProfileData(machineTag string, canAccess common.AuthFunc) error {
  1593  	mTag, err := names.ParseMachineTag(machineTag)
  1594  	if err != nil {
  1595  		return errors.Trace(err)
  1596  	}
  1597  	machine, err := p.getMachine(canAccess, mTag)
  1598  	if err != nil {
  1599  		return errors.Trace(err)
  1600  	}
  1601  	return machine.RemoveUpgradeCharmProfileData()
  1602  }