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 }