package daemon import ( "context" "crypto/rand" "encoding/hex" "errors" "fmt" "io/fs" "os" "path/filepath" "time" "dev.mediocregopher.com/mediocre-go-lib.git/mlog" ) // until keeps trying fn until it returns nil, returning true. If the context is // canceled then until returns false. func until( ctx context.Context, logger *mlog.Logger, descr string, fn func(context.Context) error, ) bool { for { logger.Info(ctx, descr) err := fn(ctx) if err == nil { return true } else if ctxErr := ctx.Err(); ctxErr != nil { return false } logger.Warn(ctx, descr+" failed, retrying in one second", err) time.Sleep(1 * time.Second) } } func randStr(l int) string { b := make([]byte, l) if _, err := rand.Read(b); err != nil { panic(err) } return hex.EncodeToString(b) } // mkDir is like os.Mkdir but it returns better error messages. If the directory // already exists then nil is returned. func mkDir(path string) error { { parentPath := filepath.Dir(path) parentInfo, err := os.Stat(parentPath) if err != nil { return fmt.Errorf("checking fs node of parent %q: %w", parentPath, err) } else if !parentInfo.IsDir() { return fmt.Errorf("%q is not a directory", parentPath) } } info, err := os.Stat(path) if errors.Is(err, fs.ErrNotExist) { // fine } else if err != nil { return fmt.Errorf("checking fs node: %w", err) } else if !info.IsDir() { return fmt.Errorf("exists but is not a directory") } else { return nil } if err := os.Mkdir(path, 0700); err != nil { return fmt.Errorf("creating directory: %w", err) } return nil }