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