-
Notifications
You must be signed in to change notification settings - Fork 44
/
Copy pathmain.go
112 lines (97 loc) · 3.06 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package main
import (
"context"
"fmt"
"log/slog"
"net/url"
"os"
"os/signal"
"strconv"
"syscall"
"time"
"github.com/cenkalti/backoff"
"github.com/lmittmann/tint"
"github.com/mattn/go-isatty"
"github.com/tinkerbell/actions/image2disk/image"
)
const (
defaultRetryDuration = 10
defaultProgressInterval = 3
)
func main() {
ctx, done := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGHUP, syscall.SIGTERM)
defer done()
disk := os.Getenv("DEST_DISK")
img := os.Getenv("IMG_URL")
compressedEnv := os.Getenv("COMPRESSED")
retryEnabled := os.Getenv("RETRY_ENABLED")
retryDuration := os.Getenv("RETRY_DURATION_MINUTES")
progressInterval := os.Getenv("PROGRESS_INTERVAL_SECONDS")
textLogging := os.Getenv("TEXT_LOGGING")
log := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{AddSource: true}))
if tlog, _ := strconv.ParseBool(textLogging); tlog {
w := os.Stderr
log = slog.New(tint.NewHandler(w, &tint.Options{
NoColor: !isatty.IsTerminal(w.Fd()),
}))
}
log.Info("IMAGE2DISK - Cloud image streamer")
if img == "" {
log.Error("IMG_URL is required", "image", img)
os.Exit(1)
}
if disk == "" {
log.Error("DEST_DISK is required", "disk", disk)
os.Exit(1)
}
u, err := url.Parse(img)
if err != nil {
log.Error("error parsing image URL (IMG_URL)", "err", err, "image", img)
os.Exit(1)
}
// We can ignore the error and default compressed to false.
cmp, _ := strconv.ParseBool(compressedEnv)
re, _ := strconv.ParseBool(retryEnabled)
pi, err := strconv.Atoi(progressInterval)
if err != nil {
pi = defaultProgressInterval
}
// convert progress interval to duration in seconds
interval := time.Duration(pi) * time.Second
operation := func() error {
if err := image.Write(ctx, log, u.String(), disk, cmp, interval); err != nil {
return fmt.Errorf("error writing image to disk: %w", err)
}
return nil
}
if re {
log.Info("retrying of image2disk is enabled")
boff := backoff.NewExponentialBackOff()
rd, err := strconv.Atoi(retryDuration)
if err != nil {
rd = defaultRetryDuration
if retryDuration == "" {
log.Info(fmt.Sprintf("no retry duration specified, using %v minutes for retry duration", rd))
} else {
log.Info(fmt.Sprintf("error converting retry duration to integer, using %v minutes for retry duration", rd), "err", err)
}
}
boff.MaxElapsedTime = time.Duration(rd) * time.Minute
bctx := backoff.WithContext(boff, ctx)
retryNotifier := func(err error, duration time.Duration) {
log.Error("retrying image2disk", "err", err, "duration", duration)
}
// try to write the image to disk with exponential backoff for 10 minutes
if err := backoff.RetryNotify(operation, bctx, retryNotifier); err != nil {
log.Error("error writing image to disk", "err", err, "image", img, "disk", disk)
os.Exit(1)
}
} else {
// try to write the image to disk without retry
if err := operation(); err != nil {
log.Error("error writing image to disk", "err", err, "image", img, "disk", disk)
os.Exit(1)
}
}
log.Info("Successfully wrote image to disk", "image", img, "disk", disk)
}