github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/undertaker/undertaker.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package undertaker
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names"
     9  
    10  	"github.com/juju/juju/apiserver/common"
    11  	"github.com/juju/juju/apiserver/params"
    12  	"github.com/juju/juju/state"
    13  	"github.com/juju/juju/state/watcher"
    14  )
    15  
    16  func init() {
    17  	common.RegisterStandardFacade("Undertaker", 1, NewUndertakerAPI)
    18  }
    19  
    20  // UndertakerAPI implements the API used by the machine undertaker worker.
    21  type UndertakerAPI struct {
    22  	st        State
    23  	resources *common.Resources
    24  	*common.StatusSetter
    25  }
    26  
    27  // NewUndertakerAPI creates a new instance of the undertaker API.
    28  func NewUndertakerAPI(st *state.State, resources *common.Resources, authorizer common.Authorizer) (*UndertakerAPI, error) {
    29  	return newUndertakerAPI(&stateShim{st}, resources, authorizer)
    30  }
    31  
    32  func newUndertakerAPI(st State, resources *common.Resources, authorizer common.Authorizer) (*UndertakerAPI, error) {
    33  	if !authorizer.AuthMachineAgent() || !authorizer.AuthModelManager() {
    34  		return nil, common.ErrPerm
    35  	}
    36  	model, err := st.Model()
    37  	if err != nil {
    38  		return nil, errors.Trace(err)
    39  	}
    40  	getCanModifyModel := func() (common.AuthFunc, error) {
    41  		return func(tag names.Tag) bool {
    42  			if st.IsController() {
    43  				return true
    44  			}
    45  			// Only the agent's model can be modified.
    46  			modelTag, ok := tag.(names.ModelTag)
    47  			if !ok {
    48  				return false
    49  			}
    50  			return modelTag.Id() == model.UUID()
    51  		}, nil
    52  	}
    53  	return &UndertakerAPI{
    54  		st:           st,
    55  		resources:    resources,
    56  		StatusSetter: common.NewStatusSetter(st, getCanModifyModel),
    57  	}, nil
    58  }
    59  
    60  // ModelInfo returns information on the model needed by the undertaker worker.
    61  func (u *UndertakerAPI) ModelInfo() (params.UndertakerModelInfoResult, error) {
    62  	result := params.UndertakerModelInfoResult{}
    63  	env, err := u.st.Model()
    64  
    65  	if err != nil {
    66  		return result, errors.Trace(err)
    67  	}
    68  
    69  	result.Result = params.UndertakerModelInfo{
    70  		UUID:       env.UUID(),
    71  		GlobalName: env.Owner().String() + "/" + env.Name(),
    72  		Name:       env.Name(),
    73  		IsSystem:   u.st.IsController(),
    74  		Life:       params.Life(env.Life().String()),
    75  	}
    76  
    77  	return result, nil
    78  }
    79  
    80  // ProcessDyingModel checks if a dying environment has any machines or services.
    81  // If there are none, the environment's life is changed from dying to dead.
    82  func (u *UndertakerAPI) ProcessDyingModel() error {
    83  	return u.st.ProcessDyingModel()
    84  }
    85  
    86  // RemoveModel removes any records of this model from Juju.
    87  func (u *UndertakerAPI) RemoveModel() error {
    88  	err := u.st.RemoveAllModelDocs()
    89  	if err != nil {
    90  		// TODO(waigani) Return a human friendly error for now. The proper fix
    91  		// is to run a buildTxn within state.RemoveAllModelDocs, so we
    92  		// can return better errors than "transaction aborted".
    93  		return errors.New("an error occurred, unable to remove model")
    94  	}
    95  	return nil
    96  }
    97  
    98  func (u *UndertakerAPI) environResourceWatcher() params.NotifyWatchResult {
    99  	var nothing params.NotifyWatchResult
   100  	machines, err := u.st.AllMachines()
   101  	if err != nil {
   102  		nothing.Error = common.ServerError(err)
   103  		return nothing
   104  	}
   105  	services, err := u.st.AllServices()
   106  	if err != nil {
   107  		nothing.Error = common.ServerError(err)
   108  		return nothing
   109  	}
   110  	var watchers []state.NotifyWatcher
   111  	for _, machine := range machines {
   112  		watchers = append(watchers, machine.Watch())
   113  	}
   114  	for _, service := range services {
   115  		watchers = append(watchers, service.Watch())
   116  	}
   117  
   118  	watch := common.NewMultiNotifyWatcher(watchers...)
   119  
   120  	if _, ok := <-watch.Changes(); ok {
   121  		return params.NotifyWatchResult{
   122  			NotifyWatcherId: u.resources.Register(watch),
   123  		}
   124  	}
   125  	nothing.Error = common.ServerError(watcher.EnsureErr(watch))
   126  	return nothing
   127  }
   128  
   129  // WatchModelResources creates watchers for changes to the lifecycle of an
   130  // model's machines and services.
   131  func (u *UndertakerAPI) WatchModelResources() params.NotifyWatchResults {
   132  	return params.NotifyWatchResults{
   133  		Results: []params.NotifyWatchResult{
   134  			u.environResourceWatcher(),
   135  		},
   136  	}
   137  }
   138  
   139  // ModelConfig returns the model's configuration.
   140  func (u *UndertakerAPI) ModelConfig() (params.ModelConfigResult, error) {
   141  	result := params.ModelConfigResult{}
   142  
   143  	config, err := u.st.ModelConfig()
   144  	if err != nil {
   145  		return result, err
   146  	}
   147  	allAttrs := config.AllAttrs()
   148  	result.Config = allAttrs
   149  	return result, nil
   150  }