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  }