github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/apiserver/systemmanager/systemmanager.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // The systemmanager package defines an API end point for functions dealing 5 // with systems as a whole. Primarily the destruction of systems. 6 package systemmanager 7 8 import ( 9 "sort" 10 11 "github.com/juju/errors" 12 "github.com/juju/loggo" 13 "github.com/juju/names" 14 "github.com/juju/utils/set" 15 16 "github.com/juju/juju/apiserver/common" 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/feature" 19 "github.com/juju/juju/state" 20 ) 21 22 var logger = loggo.GetLogger("juju.apiserver.systemmanager") 23 24 func init() { 25 common.RegisterStandardFacadeForFeature("SystemManager", 1, NewSystemManagerAPI, feature.JES) 26 } 27 28 // SystemManager defines the methods on the systemmanager API end point. 29 type SystemManager interface { 30 AllEnvironments() (params.UserEnvironmentList, error) 31 DestroySystem(args params.DestroySystemArgs) error 32 EnvironmentConfig() (params.EnvironmentConfigResults, error) 33 ListBlockedEnvironments() (params.EnvironmentBlockInfoList, error) 34 RemoveBlocks(args params.RemoveBlocksArgs) error 35 WatchAllEnvs() (params.AllWatcherId, error) 36 } 37 38 // SystemManagerAPI implements the environment manager interface and is 39 // the concrete implementation of the api end point. 40 type SystemManagerAPI struct { 41 state *state.State 42 authorizer common.Authorizer 43 apiUser names.UserTag 44 resources *common.Resources 45 } 46 47 var _ SystemManager = (*SystemManagerAPI)(nil) 48 49 // NewSystemManagerAPI creates a new api server endpoint for managing 50 // environments. 51 func NewSystemManagerAPI( 52 st *state.State, 53 resources *common.Resources, 54 authorizer common.Authorizer, 55 ) (*SystemManagerAPI, error) { 56 if !authorizer.AuthClient() { 57 return nil, errors.Trace(common.ErrPerm) 58 } 59 60 // Since we know this is a user tag (because AuthClient is true), 61 // we just do the type assertion to the UserTag. 62 apiUser, _ := authorizer.GetAuthTag().(names.UserTag) 63 isAdmin, err := st.IsSystemAdministrator(apiUser) 64 if err != nil { 65 return nil, errors.Trace(err) 66 } 67 // The entire end point is only accessible to system administrators. 68 if !isAdmin { 69 return nil, errors.Trace(common.ErrPerm) 70 } 71 72 return &SystemManagerAPI{ 73 state: st, 74 authorizer: authorizer, 75 apiUser: apiUser, 76 resources: resources, 77 }, nil 78 } 79 80 // AllEnvironments allows system administrators to get the list of all the 81 // environments in the system. 82 func (s *SystemManagerAPI) AllEnvironments() (params.UserEnvironmentList, error) { 83 result := params.UserEnvironmentList{} 84 85 // Get all the environments that the authenticated user can see, and 86 // supplement that with the other environments that exist that the user 87 // cannot see. The reason we do this is to get the LastConnection time for 88 // the environments that the user is able to see, so we have consistent 89 // output when listing with or without --all when an admin user. 90 environments, err := s.state.EnvironmentsForUser(s.apiUser) 91 if err != nil { 92 return result, errors.Trace(err) 93 } 94 visibleEnvironments := set.NewStrings() 95 for _, env := range environments { 96 lastConn, err := env.LastConnection() 97 if err != nil && !state.IsNeverConnectedError(err) { 98 return result, errors.Trace(err) 99 } 100 visibleEnvironments.Add(env.UUID()) 101 result.UserEnvironments = append(result.UserEnvironments, params.UserEnvironment{ 102 Environment: params.Environment{ 103 Name: env.Name(), 104 UUID: env.UUID(), 105 OwnerTag: env.Owner().String(), 106 }, 107 LastConnection: &lastConn, 108 }) 109 } 110 111 allEnvs, err := s.state.AllEnvironments() 112 if err != nil { 113 return result, errors.Trace(err) 114 } 115 116 for _, env := range allEnvs { 117 if !visibleEnvironments.Contains(env.UUID()) { 118 result.UserEnvironments = append(result.UserEnvironments, params.UserEnvironment{ 119 Environment: params.Environment{ 120 Name: env.Name(), 121 UUID: env.UUID(), 122 OwnerTag: env.Owner().String(), 123 }, 124 // No LastConnection as this user hasn't. 125 }) 126 } 127 } 128 129 // Sort the resulting sequence by environment name, then owner. 130 sort.Sort(orderedUserEnvironments(result.UserEnvironments)) 131 132 return result, nil 133 } 134 135 // ListBlockedEnvironments returns a list of all environments on the system 136 // which have a block in place. The resulting slice is sorted by environment 137 // name, then owner. Callers must be system administrators to retrieve the 138 // list. 139 func (s *SystemManagerAPI) ListBlockedEnvironments() (params.EnvironmentBlockInfoList, error) { 140 results := params.EnvironmentBlockInfoList{} 141 142 blocks, err := s.state.AllBlocksForSystem() 143 if err != nil { 144 return results, errors.Trace(err) 145 } 146 147 envBlocks := make(map[string][]string) 148 for _, block := range blocks { 149 uuid := block.EnvUUID() 150 types, ok := envBlocks[uuid] 151 if !ok { 152 types = []string{block.Type().String()} 153 } else { 154 types = append(types, block.Type().String()) 155 } 156 envBlocks[uuid] = types 157 } 158 159 for uuid, blocks := range envBlocks { 160 envInfo, err := s.state.GetEnvironment(names.NewEnvironTag(uuid)) 161 if err != nil { 162 logger.Debugf("Unable to get name for environment: %s", uuid) 163 continue 164 } 165 results.Environments = append(results.Environments, params.EnvironmentBlockInfo{ 166 UUID: envInfo.UUID(), 167 Name: envInfo.Name(), 168 OwnerTag: envInfo.Owner().String(), 169 Blocks: blocks, 170 }) 171 } 172 173 // Sort the resulting sequence by environment name, then owner. 174 sort.Sort(orderedBlockInfo(results.Environments)) 175 176 return results, nil 177 } 178 179 // DestroySystem will attempt to destroy the system. If the args specify the 180 // removal of blocks or the destruction of the environments, this method will 181 // attempt to do so. 182 func (s *SystemManagerAPI) DestroySystem(args params.DestroySystemArgs) error { 183 // Get list of all environments in the system. 184 allEnvs, err := s.state.AllEnvironments() 185 if err != nil { 186 return errors.Trace(err) 187 } 188 189 // If there are hosted environments and DestroyEnvironments was not 190 // specified, don't bother trying to destroy the system, as it will fail. 191 if len(allEnvs) > 1 && !args.DestroyEnvironments { 192 return errors.Errorf("state server environment cannot be destroyed before all other environments are destroyed") 193 } 194 195 // If there are blocks, and we aren't being told to ignore them, let the 196 // user know. 197 blocks, err := s.state.AllBlocksForSystem() 198 if err != nil { 199 logger.Debugf("Unable to get blocks for system: %s", err) 200 if !args.IgnoreBlocks { 201 return errors.Trace(err) 202 } 203 } 204 if len(blocks) > 0 { 205 if !args.IgnoreBlocks { 206 return common.ErrOperationBlocked("found blocks in system environments") 207 } 208 209 err := s.state.RemoveAllBlocksForSystem() 210 if err != nil { 211 return errors.Trace(err) 212 } 213 } 214 215 systemEnv, err := s.state.StateServerEnvironment() 216 if err != nil { 217 return errors.Trace(err) 218 } 219 systemTag := systemEnv.EnvironTag() 220 221 if args.DestroyEnvironments { 222 for _, env := range allEnvs { 223 environTag := env.EnvironTag() 224 if environTag != systemTag { 225 if err := common.DestroyEnvironment(s.state, environTag); err != nil { 226 logger.Errorf("unable to destroy environment %q: %s", env.UUID(), err) 227 } 228 } 229 } 230 } 231 232 return errors.Trace(common.DestroyEnvironment(s.state, systemTag)) 233 } 234 235 // EnvironmentConfig returns the environment config for the system 236 // environment. For information on the current environment, use 237 // client.EnvironmentGet 238 func (s *SystemManagerAPI) EnvironmentConfig() (params.EnvironmentConfigResults, error) { 239 result := params.EnvironmentConfigResults{} 240 241 stateServerEnv, err := s.state.StateServerEnvironment() 242 if err != nil { 243 return result, errors.Trace(err) 244 } 245 246 config, err := stateServerEnv.Config() 247 if err != nil { 248 return result, errors.Trace(err) 249 } 250 251 result.Config = config.AllAttrs() 252 return result, nil 253 } 254 255 // RemoveBlocks removes all the blocks in the system. 256 func (s *SystemManagerAPI) RemoveBlocks(args params.RemoveBlocksArgs) error { 257 if !args.All { 258 return errors.New("not supported") 259 } 260 return errors.Trace(s.state.RemoveAllBlocksForSystem()) 261 } 262 263 // WatchAllEnvs starts watching events for all environments in the 264 // system. The returned AllWatcherId should be used with Next on the 265 // AllEnvWatcher endpoint to receive deltas. 266 func (c *SystemManagerAPI) WatchAllEnvs() (params.AllWatcherId, error) { 267 w := c.state.WatchAllEnvs() 268 return params.AllWatcherId{ 269 AllWatcherId: c.resources.Register(w), 270 }, nil 271 } 272 273 type orderedBlockInfo []params.EnvironmentBlockInfo 274 275 func (o orderedBlockInfo) Len() int { 276 return len(o) 277 } 278 279 func (o orderedBlockInfo) Less(i, j int) bool { 280 if o[i].Name < o[j].Name { 281 return true 282 } 283 if o[i].Name > o[j].Name { 284 return false 285 } 286 287 if o[i].OwnerTag < o[j].OwnerTag { 288 return true 289 } 290 if o[i].OwnerTag > o[j].OwnerTag { 291 return false 292 } 293 294 // Unreachable based on the rules of there not being duplicate 295 // environments of the same name for the same owner, but return false 296 // instead of panicing. 297 return false 298 } 299 300 func (o orderedBlockInfo) Swap(i, j int) { 301 o[i], o[j] = o[j], o[i] 302 } 303 304 type orderedUserEnvironments []params.UserEnvironment 305 306 func (o orderedUserEnvironments) Len() int { 307 return len(o) 308 } 309 310 func (o orderedUserEnvironments) Less(i, j int) bool { 311 if o[i].Name < o[j].Name { 312 return true 313 } 314 if o[i].Name > o[j].Name { 315 return false 316 } 317 318 if o[i].OwnerTag < o[j].OwnerTag { 319 return true 320 } 321 if o[i].OwnerTag > o[j].OwnerTag { 322 return false 323 } 324 325 // Unreachable based on the rules of there not being duplicate 326 // environments of the same name for the same owner, but return false 327 // instead of panicing. 328 return false 329 } 330 331 func (o orderedUserEnvironments) Swap(i, j int) { 332 o[i], o[j] = o[j], o[i] 333 }