Initial QSfera import
This commit is contained in:
+120
@@ -0,0 +1,120 @@
|
||||
package wait
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/containerd/errdefs"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Strategy = (*FileStrategy)(nil)
|
||||
_ StrategyTimeout = (*FileStrategy)(nil)
|
||||
)
|
||||
|
||||
// FileStrategy waits for a file to exist in the container.
|
||||
type FileStrategy struct {
|
||||
timeout *time.Duration
|
||||
file string
|
||||
pollInterval time.Duration
|
||||
matcher func(io.Reader) error
|
||||
}
|
||||
|
||||
// NewFileStrategy constructs an FileStrategy strategy.
|
||||
func NewFileStrategy(file string) *FileStrategy {
|
||||
return &FileStrategy{
|
||||
file: file,
|
||||
pollInterval: defaultPollInterval(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithStartupTimeout can be used to change the default startup timeout
|
||||
func (ws *FileStrategy) WithStartupTimeout(startupTimeout time.Duration) *FileStrategy {
|
||||
ws.timeout = &startupTimeout
|
||||
return ws
|
||||
}
|
||||
|
||||
// WithPollInterval can be used to override the default polling interval of 100 milliseconds
|
||||
func (ws *FileStrategy) WithPollInterval(pollInterval time.Duration) *FileStrategy {
|
||||
ws.pollInterval = pollInterval
|
||||
return ws
|
||||
}
|
||||
|
||||
// WithMatcher can be used to consume the file content.
|
||||
// The matcher can return an errdefs.ErrNotFound to indicate that the file is not ready.
|
||||
// Any other error will be considered a failure.
|
||||
// Default: nil, will only wait for the file to exist.
|
||||
func (ws *FileStrategy) WithMatcher(matcher func(io.Reader) error) *FileStrategy {
|
||||
ws.matcher = matcher
|
||||
return ws
|
||||
}
|
||||
|
||||
// ForFile is a convenience method to assign FileStrategy
|
||||
func ForFile(file string) *FileStrategy {
|
||||
return NewFileStrategy(file)
|
||||
}
|
||||
|
||||
// Timeout returns the timeout for the strategy
|
||||
func (ws *FileStrategy) Timeout() *time.Duration {
|
||||
return ws.timeout
|
||||
}
|
||||
|
||||
// String returns a human-readable description of the wait strategy.
|
||||
func (ws *FileStrategy) String() string {
|
||||
if ws.matcher != nil {
|
||||
return fmt.Sprintf("file %q to exist and match condition", ws.file)
|
||||
}
|
||||
return fmt.Sprintf("file %q to exist", ws.file)
|
||||
}
|
||||
|
||||
// WaitUntilReady waits until the file exists in the container and copies it to the target.
|
||||
func (ws *FileStrategy) WaitUntilReady(ctx context.Context, target StrategyTarget) error {
|
||||
timeout := defaultStartupTimeout()
|
||||
if ws.timeout != nil {
|
||||
timeout = *ws.timeout
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
defer cancel()
|
||||
|
||||
timer := time.NewTicker(ws.pollInterval)
|
||||
defer timer.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case <-timer.C:
|
||||
if err := ws.matchFile(ctx, target); err != nil {
|
||||
if errdefs.IsNotFound(err) {
|
||||
// Not found, continue polling.
|
||||
continue
|
||||
}
|
||||
|
||||
return fmt.Errorf("copy from container: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// matchFile tries to copy the file from the container and match it.
|
||||
func (ws *FileStrategy) matchFile(ctx context.Context, target StrategyTarget) error {
|
||||
rc, err := target.CopyFileFromContainer(ctx, ws.file)
|
||||
if err != nil {
|
||||
return fmt.Errorf("copy from container: %w", err)
|
||||
}
|
||||
defer rc.Close()
|
||||
|
||||
if ws.matcher == nil {
|
||||
// No matcher, just check if the file exists.
|
||||
return nil
|
||||
}
|
||||
|
||||
if err = ws.matcher(rc); err != nil {
|
||||
return fmt.Errorf("matcher: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user