-
Notifications
You must be signed in to change notification settings - Fork 220
/
machine_set.go
111 lines (101 loc) · 2.74 KB
/
machine_set.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
package machine
import (
"context"
"errors"
"fmt"
"sync"
"time"
"github.com/superfly/flyctl/api"
"github.com/superfly/flyctl/flaps"
"github.com/superfly/flyctl/iostreams"
"github.com/superfly/flyctl/terminal"
)
type MachineSet interface {
AcquireLeases(context.Context, time.Duration) error
ReleaseLeases(context.Context) error
IsEmpty() bool
GetMachines() []LeasableMachine
}
type machineSet struct {
machines []LeasableMachine
}
func NewMachineSet(flapsClient *flaps.Client, io *iostreams.IOStreams, machines []*api.Machine) MachineSet {
leaseMachines := make([]LeasableMachine, 0)
for _, m := range machines {
leaseMachines = append(leaseMachines, NewLeasableMachine(flapsClient, io, m))
}
return &machineSet{
machines: leaseMachines,
}
}
func (ms *machineSet) IsEmpty() bool {
return len(ms.machines) == 0
}
func (ms *machineSet) GetMachines() []LeasableMachine {
return ms.machines
}
func (ms *machineSet) AcquireLeases(ctx context.Context, duration time.Duration) error {
results := make(chan error, len(ms.machines))
var wg sync.WaitGroup
for _, m := range ms.machines {
wg.Add(1)
go func(m LeasableMachine) {
defer wg.Done()
results <- m.AcquireLease(ctx, duration)
}(m)
}
go func() {
wg.Wait()
close(results)
}()
hadError := false
for err := range results {
if err != nil {
hadError = true
terminal.Warnf("failed to acquire lease: %v\n", err)
}
}
if hadError {
if err := ms.ReleaseLeases(ctx); err != nil {
terminal.Warnf("error releasing machine leases: %v\n", err)
}
return fmt.Errorf("error acquiring leases on all machines")
}
return nil
}
func (ms *machineSet) ReleaseLeases(ctx context.Context) error {
// when context is canceled, take 500ms to attempt to release the leases
contextWasAlreadyCanceled := errors.Is(ctx.Err(), context.Canceled)
if contextWasAlreadyCanceled {
var cancel context.CancelFunc
cancelTimeout := 500 * time.Millisecond
ctx, cancel = context.WithTimeout(context.TODO(), cancelTimeout)
terminal.Infof("detected canceled context and allowing %s to release machine leases\n", cancelTimeout)
defer cancel()
}
results := make(chan error, len(ms.machines))
var wg sync.WaitGroup
for _, m := range ms.machines {
wg.Add(1)
go func(m LeasableMachine) {
defer wg.Done()
results <- m.ReleaseLease(ctx)
}(m)
}
go func() {
wg.Wait()
close(results)
}()
hadError := false
for err := range results {
contextTimedOutOrCanceled := errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled)
if err != nil && (!contextWasAlreadyCanceled || !contextTimedOutOrCanceled) {
hadError = true
terminal.Warnf("failed to release lease: %v\n", err)
}
}
if hadError {
return fmt.Errorf("error releasing leases on machines")
}
return nil
}