xetys/hetzner-kube

View on GitHub
pkg/progress_coordinator.go

Summary

Maintainability
A
0 mins
Test Coverage
package pkg

import (
    "fmt"
    "os"
    "sync"

    "github.com/go-kit/kit/log/term"
    "github.com/gosuri/uiprogress"
    "github.com/gosuri/uiprogress/util/strutil"
)

// CompletedEvent indicate the process completed
const CompletedEvent = "complete!"

// UIProgressCoordinator coortinate display of progress in UI
type UIProgressCoordinator struct {
    group      sync.WaitGroup
    progresses map[string]*Progress
}

// RenderProgressBars indicate if we need to display progress in UI
var RenderProgressBars bool

// NewProgressCoordinator create a new progress coordinator UI
func NewProgressCoordinator() *UIProgressCoordinator {
    if isUIEnabled() {
        uiprogress.Start()
    }
    pc := new(UIProgressCoordinator)
    pc.progresses = make(map[string]*Progress)

    return pc
}

func isUIEnabled() bool {
    if RenderProgressBars {
        return term.IsTerminal(os.Stdout)
    }
    return false
}

func shortLeftPadRight(s string, padWidth int) string {
    if len(s) > padWidth {
        l := len(s)
        return "..." + s[(l-(padWidth-2)):(l-1)]
    }
    return strutil.PadRight(s, padWidth, ' ')
}

// StartProgress start the progress UI
func (c *UIProgressCoordinator) StartProgress(name string, steps int) {
    progress := &Progress{
        Bar:     uiprogress.AddBar(steps),
        State:   "starting",
        channel: make(chan string),
        Name:    name,
    }
    progress.Bar.Width = 16
    progress.Bar.PrependFunc(func(b *uiprogress.Bar) string {
        percent := strutil.PadLeft(fmt.Sprintf("%.01f%%", b.CompletedPercent()), 6, ' ')
        return fmt.Sprintf("%s : %s  %s",
            shortLeftPadRight(name, 20),
            shortLeftPadRight(progress.State, 32),
            percent,
        )
    })
    c.progresses[name] = progress
    c.group.Add(1)
    go func(progress *Progress) {
        for {
            event := <-progress.channel
            if !isUIEnabled() {
                fmt.Printf("%s: %s (%d)", progress.Name, event, progress.Bar.Current()+1)
                fmt.Println()
            }
            if event == CompletedEvent {
                progress.Bar.Set(progress.Bar.Total)
                progress.SetText(event)
                break
            }

            progress.SetText(event)
            if done := progress.Bar.Incr(); !done {
                break
            }
        }
        c.group.Done()
    }(progress)
}

// AddEvent add an new event in the progress UI
func (c *UIProgressCoordinator) AddEvent(progressName string, eventName string) {
    if progress, isPresent := c.progresses[progressName]; isPresent {
        progress.channel <- eventName
    }
}

// CompleteProgress sends an completed event
func (c *UIProgressCoordinator) CompleteProgress(nodeName string) {
    if progress, isPresent := c.progresses[nodeName]; isPresent {
        progress.channel <- CompletedEvent
    }
}

// Wait temporary stop the progress UI
func (c *UIProgressCoordinator) Wait() {
    c.group.Wait()
    if isUIEnabled() {
        uiprogress.Stop()
    }
}