// createUnitNameFilter creates a predicate that return true if a given // unit name belongs to the configured job and task-group selection. func (d *Deployment) createUnitNamePredicate() func(string) bool { if d.groupSelection.IncludeAll() { // Select everything in the job return func(unitName string) bool { if !d.scalingGroupSelection.IncludeAll() { if !jobs.IsUnitForScalingGroup(unitName, d.job.Name, uint(d.scalingGroupSelection)) { return false } } return jobs.IsUnitForJob(unitName, d.job.Name) } } // Select everything in one of the groups return func(unitName string) bool { if !d.scalingGroupSelection.IncludeAll() { if !jobs.IsUnitForScalingGroup(unitName, d.job.Name, uint(d.scalingGroupSelection)) { return false } } for _, g := range d.groupSelection { if jobs.IsUnitForTaskGroup(unitName, d.job.Name, g) { return true } } return false } }
// Run creates all applicable unit files and deploys them onto the configured cluster. func (d *Deployment) Run() error { // Fetch all current units s, err := d.orchestrator.Scheduler(d.cluster) if err != nil { return maskAny(err) } allUnits, err := s.List() if err != nil { return maskAny(err) } // Prepare UI ui := newStateUI(d.verbose) defer ui.Close() // Find out which current units belong to the configured job remainingLoadedJobUnitNames := selectUnitNames(allUnits, d.createUnitNamePredicate()) // Create scaling group units if err := d.generateScalingGroups(); err != nil { return maskAny(err) } // Ask for confirmation maxScale := d.scalingGroups[len(d.scalingGroups)-1].scalingGroup // Go over every scale step := 1 totalModifications := 0 waitBeforeNextStep := false for sgIndex, sg := range d.scalingGroups { // Select the loaded units that belong to this scaling group correctScalingGroupPredicate := func(unitName string) bool { return jobs.IsUnitForScalingGroup(unitName, d.job.Name, sg.scalingGroup) } loadedScalingGroupUnitNames := selectUnitNames(remainingLoadedJobUnitNames, correctScalingGroupPredicate) // Update remainingLoadedJobUnitNames remainingLoadedJobUnitNames = selectUnitNames(remainingLoadedJobUnitNames, notPredicate(containsPredicate(loadedScalingGroupUnitNames))) // Select the loaded unit name that have become obsolete sgUnitNames := sg.unitNames() obsoleteUnitNames := selectUnitNames(loadedScalingGroupUnitNames, notPredicate(containsPredicate(sgUnitNames))) notObsoleteUnitNames := selectUnitNames(loadedScalingGroupUnitNames, containsPredicate(sgUnitNames)) // Select the unit names that are modified and need an update isModifiedPredicate := d.isModifiedPredicate(sg, s, ui) modifiedUnitNames := selectUnitNames(notObsoleteUnitNames, isModifiedPredicate) isFailedPredicate := d.isFailedPredicate(sg, s, ui) failedUnitNames := selectUnitNames(notObsoleteUnitNames, isFailedPredicate) unitNamesToDestroy := append(append(obsoleteUnitNames, modifiedUnitNames...), failedUnitNames...) newUnitNames := selectUnitNames(sgUnitNames, notPredicate(containsPredicate(loadedScalingGroupUnitNames))) // Are there any changes? anyModifications := (len(loadedScalingGroupUnitNames) != len(sg.units)) || (len(unitNamesToDestroy) > 0) // Wait a bit before proceeding if waitBeforeNextStep && anyModifications { InterruptibleSleep(ui.MessageSink, d.SliceDelay, fmt.Sprintf("Waiting %s before continuing with scaling group %d of %d...", "%s", (sgIndex+1), maxScale)) ui.Clear() } // Confirm modifications if anyModifications && !d.force { curScale := sg.scalingGroup changes := []string{"# Unit | Action"} changes = append(changes, formatChanges("# ", obsoleteUnitNames, "Remove (is obsolete) !!!")...) changes = append(changes, formatChanges("# ", modifiedUnitNames, "Update")...) changes = append(changes, formatChanges("# ", failedUnitNames, "Failed state")...) changes = append(changes, formatChanges("# ", newUnitNames, "Create")...) sort.Strings(changes[1:]) formattedChanges := strings.Replace(columnize.SimpleFormat(changes), "#", " ", -1) ui.HeaderSink <- fmt.Sprintf("Step %d: Update scaling group %d of %d on '%s'.\n%s\n", step, curScale, maxScale, d.cluster.Stack, formattedChanges) if !d.autoContinue { if err := ui.Confirm("Are you sure you want to continue?"); err != nil { return maskAny(err) } } } // Destroy the obsolete & modified units if len(unitNamesToDestroy) > 0 { if err := d.destroyUnits(s, unitNamesToDestroy, ui); err != nil { return maskAny(err) } InterruptibleSleep(ui.MessageSink, d.DestroyDelay, "Waiting for %s...") } // Now launch everything unitsToLaunch := sg.selectByNames(modifiedUnitNames, failedUnitNames, newUnitNames) if unitsToLaunch.Len() > 0 { if err := launchUnits(s, unitsToLaunch, ui); err != nil { return maskAny(err) } } // Update counters if anyModifications { waitBeforeNextStep = true totalModifications++ } step++ ui.Clear() } // Destroy remaining units if len(remainingLoadedJobUnitNames) > 0 { changes := []string{"# Unit | Action"} changes = append(changes, formatChanges("# ", remainingLoadedJobUnitNames, "Remove (is obsolete) !!!")...) sort.Strings(changes[1:]) formattedChanges := strings.Replace(columnize.SimpleFormat(changes), "#", " ", -1) ui.HeaderSink <- fmt.Sprintf("Step %d: Cleanup of obsolete units on '%s'.\n%s\n", step, d.cluster.Stack, formattedChanges) if err := ui.Confirm("Are you sure you want to continue?"); err != nil { return maskAny(err) } if err := d.destroyUnits(s, remainingLoadedJobUnitNames, ui); err != nil { return maskAny(err) } totalModifications++ } // Notify in case we did nothing if totalModifications == 0 { ui.MessageSink <- "No modifications needed." } else { ui.MessageSink <- "Done." } return nil }