2021-06-14 14:48:39 +02:00
|
|
|
// Package don is a little helper if you need to check for the readiness of something.
|
|
|
|
// This could be a command to run (like ssh) or a `db.Ping()` for check of the readiness
|
|
|
|
// of a database container.
|
2021-06-15 10:48:12 +02:00
|
|
|
//
|
2021-09-01 13:43:50 +02:00
|
|
|
// (image/readme) ./README.gif
|
|
|
|
//
|
2021-06-15 10:48:12 +02:00
|
|
|
// Use as commandline tool
|
|
|
|
//
|
2021-06-22 12:33:41 +02:00
|
|
|
// Download the tool from the (download page) https://github.com/xsteadfastx/don/releases or
|
|
|
|
// install via brew:
|
2021-06-15 10:48:12 +02:00
|
|
|
//
|
2021-06-22 12:33:41 +02:00
|
|
|
// brew tap xsteadfastx/tap https://github.com/xsteadfastx/homebrew-tap
|
|
|
|
// brew install don
|
|
|
|
// don -t 15m -r 15s -c "ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 root@container"
|
2021-06-15 10:48:12 +02:00
|
|
|
//
|
|
|
|
// This example checks every 15 seconds if the ssh container is ready. It would timeout with an
|
|
|
|
// error after 15 minutes.
|
|
|
|
//
|
|
|
|
// Use as a library
|
|
|
|
//
|
|
|
|
// If you want to use don as a library, it just takes a `func() bool` in `don.Ready()`
|
|
|
|
// have a function that runs the readiness check and returns `true` or `false` if its
|
|
|
|
// ready or not. The second argument is the overall timeout and
|
|
|
|
// the third argument is the check interval. Import it like this:
|
|
|
|
//
|
|
|
|
// import go.xsfx.dev/don
|
|
|
|
//
|
|
|
|
// Doing the readiness check like this:
|
|
|
|
//
|
|
|
|
// if err := don.Ready(
|
|
|
|
// func() bool {
|
|
|
|
// db, err := sql.Open("oracle", dbConn)
|
|
|
|
// if err != nil {
|
|
|
|
// log.Warn().Err(err).Str("dbConn", dbConn).Msg("could not open connection")
|
|
|
|
//
|
|
|
|
// return false
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// if err := db.Ping(); err != nil {
|
|
|
|
// log.Warn().Err(err).Str("dbConn", dbConn).Msg("could not ping")
|
|
|
|
//
|
|
|
|
// return false
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// return true
|
|
|
|
// },
|
2021-09-01 13:43:50 +02:00
|
|
|
// 10*time.Minute, // When to timeout completly.
|
|
|
|
// 30*time.Second, // Whats the time between retries.
|
|
|
|
// false, // If you want a progressbar.
|
2021-06-15 10:48:12 +02:00
|
|
|
// ); err != nil {
|
|
|
|
// log.Error().Err(err).Msg("received error")
|
|
|
|
// teardown(pool, resource, tmpState.Name())
|
|
|
|
// os.Exit(1)
|
|
|
|
// }
|
|
|
|
//
|
2021-06-14 14:48:39 +02:00
|
|
|
package don
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2021-06-14 20:07:16 +02:00
|
|
|
"os"
|
2021-06-14 14:48:39 +02:00
|
|
|
"os/exec"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/rs/zerolog/log"
|
2021-09-01 13:08:04 +02:00
|
|
|
"github.com/schollz/progressbar/v3"
|
2021-06-14 14:48:39 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
var errTimeout = errors.New("timeout")
|
|
|
|
|
2021-06-15 11:01:32 +02:00
|
|
|
// Cmd returns a `func() bool` for working with `don.Ready()`. It executes a command and
|
2021-06-14 14:48:39 +02:00
|
|
|
// returns a true if everything looks fine or a false if there was some kind of error.
|
|
|
|
func Cmd(c string) func() bool {
|
|
|
|
return func() bool {
|
|
|
|
cmd := exec.Command("sh", "-c", c)
|
2021-06-14 20:07:16 +02:00
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
|
2021-06-14 14:48:39 +02:00
|
|
|
if err := cmd.Run(); err != nil {
|
|
|
|
log.Warn().Err(err).Msg("cmd has error")
|
|
|
|
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-15 10:48:12 +02:00
|
|
|
// Ready takes a function that executes something and returns a bool to indicate if
|
2021-06-14 14:48:39 +02:00
|
|
|
// something is ready or not. It returns an error if it timeouts.
|
2021-09-01 13:08:04 +02:00
|
|
|
func Ready(f func() bool, timeout time.Duration, retry time.Duration, bar bool) error {
|
2021-06-14 14:48:39 +02:00
|
|
|
chReady := make(chan struct{})
|
|
|
|
|
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
if f() {
|
|
|
|
chReady <- struct{}{}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2021-09-01 13:08:04 +02:00
|
|
|
if bar {
|
|
|
|
d := int64(retry / time.Second)
|
|
|
|
bar := progressbar.Default(d)
|
|
|
|
|
|
|
|
for i := int64(0); i < d; i++ {
|
2021-09-01 13:15:49 +02:00
|
|
|
if err := bar.Add(1); err != nil {
|
|
|
|
log.Error().Err(err).Msg("could not add to bar")
|
|
|
|
}
|
|
|
|
|
2021-09-01 13:08:04 +02:00
|
|
|
time.Sleep(time.Second)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
<-time.After(retry)
|
|
|
|
log.Info().Msg("retrying")
|
|
|
|
}
|
2021-06-14 14:48:39 +02:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
select {
|
|
|
|
case <-chReady:
|
|
|
|
return nil
|
|
|
|
|
|
|
|
case <-time.After(timeout):
|
|
|
|
return errTimeout
|
|
|
|
}
|
|
|
|
}
|