github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/cmd/juju/controller/killstatus.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package controller 5 6 import ( 7 "fmt" 8 "time" 9 10 "github.com/juju/clock" 11 "github.com/juju/cmd" 12 "github.com/juju/errors" 13 "gopkg.in/juju/names.v2" 14 15 "github.com/juju/juju/apiserver/params" 16 ) 17 18 type ctrData struct { 19 UUID string 20 HostedModelCount int 21 HostedMachineCount int 22 ApplicationCount int 23 TotalVolumeCount int 24 TotalFilesystemCount int 25 26 // Model contains controller model data 27 Model modelData 28 } 29 30 type modelData struct { 31 UUID string 32 Owner string 33 Name string 34 Life string 35 36 HostedMachineCount int 37 ApplicationCount int 38 VolumeCount int 39 FilesystemCount int 40 PersistentVolumeCount int 41 PersistentFilesystemCount int 42 } 43 44 type environmentStatus struct { 45 controller ctrData 46 // models contains only the hosted models. controller.Model 47 // contains data specific to the controller model. 48 models []modelData 49 } 50 51 // newTimedStatusUpdater returns a function which waits a given period of time 52 // before querying the apiserver for updated data. 53 func newTimedStatusUpdater(ctx *cmd.Context, api destroyControllerAPI, controllerModelUUID string, clock clock.Clock) func(time.Duration) environmentStatus { 54 return func(wait time.Duration) environmentStatus { 55 if wait > 0 { 56 <-clock.After(wait) 57 } 58 59 // If we hit an error, status.HostedModelCount will be 0, the polling 60 // loop will stop and we'll go directly to destroying the model. 61 ctrStatus, modelsStatus, err := newData(api, controllerModelUUID) 62 if err != nil { 63 ctx.Infof("Unable to get the controller summary from the API: %s.", err) 64 } 65 66 return environmentStatus{ 67 controller: ctrStatus, 68 models: modelsStatus, 69 } 70 } 71 } 72 73 func newData(api destroyControllerAPI, controllerModelUUID string) (ctrData, []modelData, error) { 74 models, err := api.AllModels() 75 if err != nil { 76 return ctrData{}, nil, errors.Trace(err) 77 } 78 if len(models) == 0 { 79 return ctrData{}, nil, errors.New("no models found") 80 } 81 82 modelTags := make([]names.ModelTag, len(models)) 83 modelName := make(map[string]string) 84 for i, model := range models { 85 modelTags[i] = names.NewModelTag(model.UUID) 86 modelName[model.UUID] = model.Name 87 } 88 89 status, err := api.ModelStatus(modelTags...) 90 if err != nil { 91 return ctrData{}, nil, errors.Trace(err) 92 } 93 94 var hostedMachinesCount int 95 var applicationsCount int 96 var volumeCount int 97 var filesystemCount int 98 var modelsData []modelData 99 var aliveModelCount int 100 var ctrModelData modelData 101 for _, model := range status { 102 if model.Error != nil { 103 if errors.IsNotFound(model.Error) { 104 // This most likely occurred because a model was 105 // destroyed half-way through the call. 106 // Since we filter out models with life.Dead below, it's safe 107 // to assume that we want to filter these models here too. 108 continue 109 } 110 return ctrData{}, nil, errors.Trace(model.Error) 111 } 112 var persistentVolumeCount int 113 var persistentFilesystemCount int 114 for _, v := range model.Volumes { 115 if v.Detachable { 116 persistentVolumeCount++ 117 } 118 } 119 for _, f := range model.Filesystems { 120 if f.Detachable { 121 persistentFilesystemCount++ 122 } 123 } 124 modelData := modelData{ 125 model.UUID, 126 model.Owner, 127 modelName[model.UUID], 128 model.Life, 129 model.HostedMachineCount, 130 model.ApplicationCount, 131 len(model.Volumes), 132 len(model.Filesystems), 133 persistentVolumeCount, 134 persistentFilesystemCount, 135 } 136 if model.UUID == controllerModelUUID { 137 ctrModelData = modelData 138 } else { 139 if model.Life == string(params.Dead) { 140 // Filter out dead, non-controller models. 141 continue 142 } 143 modelsData = append(modelsData, modelData) 144 aliveModelCount++ 145 } 146 hostedMachinesCount += model.HostedMachineCount 147 applicationsCount += model.ApplicationCount 148 volumeCount += modelData.VolumeCount 149 filesystemCount += modelData.FilesystemCount 150 } 151 152 ctrFinalStatus := ctrData{ 153 controllerModelUUID, 154 aliveModelCount, 155 hostedMachinesCount, 156 applicationsCount, 157 volumeCount, 158 filesystemCount, 159 ctrModelData, 160 } 161 162 return ctrFinalStatus, modelsData, nil 163 } 164 165 func hasUnreclaimedResources(env environmentStatus) bool { 166 return hasUnDeadModels(env.models) || 167 env.controller.HostedMachineCount > 0 168 } 169 170 func hasUnDeadModels(models []modelData) bool { 171 for _, model := range models { 172 if model.Life != string(params.Dead) { 173 return true 174 } 175 } 176 return false 177 } 178 179 func hasAliveModels(models []modelData) bool { 180 for _, model := range models { 181 if model.Life == string(params.Alive) { 182 return true 183 } 184 } 185 return false 186 } 187 188 func s(n int) string { 189 if n > 1 { 190 return "s" 191 } 192 return "" 193 } 194 195 func fmtCtrStatus(data ctrData) string { 196 modelNo := data.HostedModelCount 197 out := fmt.Sprintf("Waiting on %d model%s", modelNo, s(modelNo)) 198 199 if machineNo := data.HostedMachineCount; machineNo > 0 { 200 out += fmt.Sprintf(", %d machine%s", machineNo, s(machineNo)) 201 } 202 203 if applicationNo := data.ApplicationCount; applicationNo > 0 { 204 out += fmt.Sprintf(", %d application%s", applicationNo, s(applicationNo)) 205 } 206 207 if n := data.TotalVolumeCount; n > 0 { 208 out += fmt.Sprintf(", %d volume%s", n, s(n)) 209 } 210 211 if n := data.TotalFilesystemCount; n > 0 { 212 out += fmt.Sprintf(", %d filesystem%s", n, s(n)) 213 } 214 215 return out 216 } 217 218 func fmtModelStatus(data modelData) string { 219 out := fmt.Sprintf("\t%s/%s (%s)", data.Owner, data.Name, data.Life) 220 221 if machineNo := data.HostedMachineCount; machineNo > 0 { 222 out += fmt.Sprintf(", %d machine%s", machineNo, s(machineNo)) 223 } 224 225 if applicationNo := data.ApplicationCount; applicationNo > 0 { 226 out += fmt.Sprintf(", %d application%s", applicationNo, s(applicationNo)) 227 } 228 229 if n := data.VolumeCount; n > 0 { 230 out += fmt.Sprintf(", %d volume%s", n, s(n)) 231 } 232 233 if n := data.FilesystemCount; n > 0 { 234 out += fmt.Sprintf(", %d filesystem%s", n, s(n)) 235 } 236 237 return out 238 }