in src/go/cmd/vmscaler/vmscaler/vmscaler.go [284:417]
func (v *VMScaler) createPlan(firstRun bool) (*Plan, error) {
log.Debug.Printf("[VMScaler.createPlan")
defer log.Debug.Printf("VMScaler.createPlan]")
var vmssMap map[string]compute.VirtualMachineScaleSet
vmssMap = make(map[string]compute.VirtualMachineScaleSet)
var vmssAtCapacity []string
var vmssToSeal []string
var vmssToIncrease []string
var newVMSSNames []string
var vmssToDelete []string
currentCapacity := int64(0)
increasedCapacity := int64(0)
// read all VMSS resources in the resource group
vmssList, err := v.vmssClient.ListVMSS(v.ResourceGroup)
if err != nil {
return nil, err
}
// get the current capacity of all VMSS
for _, element := range vmssList {
currentCapacity += *element.Sku.Capacity
}
// on first run, we set date on all unsealed VMSS. This ensures
// no pre-mature sealing
if firstRun {
for _, element := range vmssList {
if _, ok := element.Tags[SEALED_TAG_KEY]; ok {
log.Debug.Printf("vmss '%s' is sealed", *element.Name)
continue
}
vmssMap[*element.Name] = element
vmssAtCapacity = append(vmssAtCapacity, *element.Name)
}
plan := &Plan{
VmssMap: vmssMap,
CurrentCapacity: currentCapacity,
IncreasedCapacity: increasedCapacity,
VmssAtCapacity: vmssAtCapacity,
VMSSToSeal: vmssToSeal,
VMSSToIncrease: vmssToIncrease,
VMSSToDelete: vmssToDelete,
NewVMSSNames: newVMSSNames,
}
plan.Print(v)
return plan, nil
}
for _, element := range vmssList {
vmssMap[*element.Name] = element
if !v.vmssOpManager.IsComplete(*element.Name) {
log.Info.Printf("skipping vmss '%s', operation still pending", *element.Name)
continue
}
// delete 0 capacity VMSS
if *element.Sku.Capacity == 0 {
log.Info.Printf("need to delete %s", *element.Name)
vmssToDelete = append(vmssToDelete, *element.Name)
continue
}
// skip operations on sealed VMSS
if _, ok := element.Tags[SEALED_TAG_KEY]; ok {
log.Debug.Printf("vmss '%s' is sealed", *element.Name)
continue
}
// check VMSS at capacity
if *element.Sku.Capacity >= v.VMsPerVMSS {
vmssAtCapacity = append(vmssAtCapacity, *element.Name)
continue
}
// check if VMSS needs to be sealed
if val, ok := element.Tags[LAST_TIME_AT_CAPACITY_TAG_KEY]; ok {
var lastTimeAtCapacity time.Time
if err := lastTimeAtCapacity.UnmarshalText([]byte(*val)); err != nil {
log.Error.Printf("vmss '%s' has a bad tag", *element.Name)
} else if time.Since(lastTimeAtCapacity) > timeToSeal {
vmssToSeal = append(vmssToSeal, *element.Name)
continue
}
}
// check if VMSS can be increased
if *element.VirtualMachineScaleSetProperties.ProvisioningState == string(computesvc.ProvisioningStateSucceeded) {
if (currentCapacity + increasedCapacity) < v.TotalNodes {
vmssToIncrease = append(vmssToIncrease, *element.Name)
increasedCapacity += (v.VMsPerVMSS - *element.Sku.Capacity)
} else {
// we have enough nodes, but we should update the timestamp, so we do not seal
vmssAtCapacity = append(vmssAtCapacity, *element.Name)
}
continue
}
}
remainingVMs := v.TotalNodes - (currentCapacity + increasedCapacity)
if remainingVMs > 0 {
// creating the new VMSS
newVmssCount := (remainingVMs / v.VMsPerVMSS)
if (remainingVMs % v.VMsPerVMSS) > 0 {
newVmssCount++
}
for i := 0; int64(len(newVMSSNames)) < newVmssCount; i++ {
vmssName := fmt.Sprintf("%s%d", VMSS_PREFIX, i)
if _, ok := vmssMap[vmssName]; !ok {
newVMSSNames = append(newVMSSNames, vmssName)
}
}
}
plan := &Plan{
VmssMap: vmssMap,
CurrentCapacity: currentCapacity,
IncreasedCapacity: increasedCapacity,
VmssAtCapacity: vmssAtCapacity,
VMSSToSeal: vmssToSeal,
VMSSToIncrease: vmssToIncrease,
VMSSToDelete: vmssToDelete,
NewVMSSNames: newVMSSNames,
}
plan.Print(v)
return plan, nil
}