-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
118 lines (107 loc) · 2.92 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
113
114
115
116
117
118
package main
import (
"errors"
"flag"
"fmt"
"log"
"net"
"net/netip"
"os"
"strings"
"time"
"github.com/jpillora/backoff"
)
var (
minDelay *time.Duration
maxDelay *time.Duration
factor *float64
jitter *bool
ips map[netip.Addr]bool
ifName *string
)
func addIP(ipStr string) error {
ip, err := netip.ParseAddr(ipStr)
if err != nil {
return fmt.Errorf("invalid address %v: %w", ipStr, err)
}
if ips == nil {
ips = map[netip.Addr]bool{}
}
ips[ip] = true
return nil
}
func main() {
minDelay = flag.Duration("min", 100*time.Millisecond, "minimum backoff duration")
maxDelay = flag.Duration("max", 30*time.Second, "maximum backoff duration")
factor = flag.Float64("factor", 1.5, "multiplication factor for each attempt")
jitter = flag.Bool("jitter", false, "randomize backoff steps")
flag.Func("ip", "IP addresses required for the interfaces to have.\nCan be passed multiple times.\nIf not passed, any address will be accepted.", addIP)
ifName = flag.String("interface", "", "interface to wait for. Required.")
flag.Parse()
log.SetOutput(os.Stderr)
log.SetPrefix(fmt.Sprintf("wait-for-interfaces: "))
log.SetFlags(0)
if ifName == nil || *ifName == "" {
log.Fatal("-interface needs to be passed")
}
log.SetPrefix(fmt.Sprintf("wait-for-interfaces(%v): ", *ifName))
b := backoff.Backoff{
Factor: *factor,
Jitter: *jitter,
Min: *minDelay,
Max: *maxDelay,
}
outer:
for {
interfaces, err := net.Interfaces()
if err != nil {
log.Printf("Could not retrieve interfaces: %v", err)
time.Sleep(b.Duration())
continue outer
}
if err := ensureInterface(*ifName, interfaces); err != nil {
log.Printf("Interface %s is not yet up: %v", *ifName, err)
time.Sleep(b.Duration())
continue outer
}
log.Printf("Interface %s is up.", *ifName)
return
}
}
var (
errNotUp = errors.New("interface is not yet in states UP & RUNNING")
errNoAddr = errors.New("interface has no wanted address yet")
errNoIf = errors.New("Interface doesn't exist (yet)")
)
func ensureInterface(name string, interfaces []net.Interface) error {
for _, iface := range interfaces {
if iface.Name != name {
continue
}
if iface.Flags&(net.FlagUp|net.FlagRunning) == 0 {
return fmt.Errorf("%v with flags %x: %w", name, iface.Flags, errNotUp)
}
addrs, err := iface.Addrs()
if err != nil {
return fmt.Errorf("could not retrieve addresses for interface %v: %w", name, err)
}
hasAddr := 0
needAddr := len(ips)
for _, addr := range addrs {
if ips != nil {
ip := netip.MustParseAddr(strings.Split(addr.String(), "/")[0])
if !ips[ip] {
log.Printf("Interface %v has address %v but we're not looking for that", name, addr.String())
continue
}
}
log.Printf("Interface %v has address %v", name, addr.String())
hasAddr++
}
if hasAddr == 0 || hasAddr < needAddr {
return fmt.Errorf("%v: %w", name, errNoAddr)
}
return nil
}
return fmt.Errorf("%v: %w", name, errNoIf)
}