-
Notifications
You must be signed in to change notification settings - Fork 218
/
deploy_first.go
160 lines (138 loc) · 4.94 KB
/
deploy_first.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package deploy
import (
"context"
"fmt"
"github.com/docker/go-units"
"github.com/samber/lo"
fly "github.com/superfly/fly-go"
"github.com/superfly/flyctl/helpers"
"github.com/superfly/flyctl/internal/prompt"
)
func (md *machineDeployment) provisionFirstDeploy(ctx context.Context, allocPublicIPs bool) error {
if !md.isFirstDeploy || md.restartOnly {
return nil
}
if err := md.provisionIpsOnFirstDeploy(ctx, allocPublicIPs); err != nil {
fmt.Fprintf(md.io.ErrOut, "Failed to provision IP addresses. Use `fly ips` commands to remediate it. ERROR: %s", err)
}
if err := md.provisionVolumesOnFirstDeploy(ctx); err != nil {
return fmt.Errorf("failed to provision seed volumes: %w", err)
}
return nil
}
func (md *machineDeployment) provisionIpsOnFirstDeploy(ctx context.Context, allocPublicIPs bool) error {
// Provision only if the app hasn't been deployed and have services defined
if !md.isFirstDeploy || len(md.appConfig.AllServices()) == 0 || !allocPublicIPs {
return nil
}
// Do not touch IPs if there are already allocated
ipAddrs, err := md.apiClient.GetIPAddresses(ctx, md.app.Name)
if err != nil {
return fmt.Errorf("error detecting ip addresses allocated to %s app: %w", md.app.Name, err)
}
if len(ipAddrs) > 0 {
return nil
}
switch md.appConfig.HasNonHttpAndHttpsStandardServices() {
case true:
hasUdpService := md.appConfig.HasUdpService()
ipStuffStr := "a dedicated ipv4 address"
if !hasUdpService {
ipStuffStr = "dedicated ipv4 and ipv6 addresses"
}
confirmDedicatedIp, err := prompt.Confirmf(ctx, "Would you like to allocate %s now?", ipStuffStr)
if confirmDedicatedIp && err == nil {
v4Dedicated, err := md.apiClient.AllocateIPAddress(ctx, md.app.Name, "v4", "", nil, "")
if err != nil {
return err
}
fmt.Fprintf(md.io.Out, "Allocated dedicated ipv4: %s\n", v4Dedicated.Address)
if !hasUdpService {
v6Dedicated, err := md.apiClient.AllocateIPAddress(ctx, md.app.Name, "v6", "", nil, "")
if err != nil {
return err
}
fmt.Fprintf(md.io.Out, "Allocated dedicated ipv6: %s\n", v6Dedicated.Address)
}
}
case false:
fmt.Fprintf(md.io.Out, "Provisioning ips for %s\n", md.colorize.Bold(md.app.Name))
v6Addr, err := md.apiClient.AllocateIPAddress(ctx, md.app.Name, "v6", "", nil, "")
if err != nil {
return fmt.Errorf("error allocating ipv6 after detecting first deploy and presence of services: %w", err)
}
fmt.Fprintf(md.io.Out, " Dedicated ipv6: %s\n", v6Addr.Address)
v4Shared, err := md.apiClient.AllocateSharedIPAddress(ctx, md.app.Name)
if err != nil {
return fmt.Errorf("error allocating shared ipv4 after detecting first deploy and presence of services: %w", err)
}
fmt.Fprintf(md.io.Out, " Shared ipv4: %s\n", v4Shared)
fmt.Fprintf(md.io.Out, " Add a dedicated ipv4 with: fly ips allocate-v4\n")
}
fmt.Fprintln(md.io.Out)
return nil
}
func (md *machineDeployment) provisionVolumesOnFirstDeploy(ctx context.Context) error {
// Provision only if the app hasn't been deployed and have mounts defined
if !md.isFirstDeploy || len(md.appConfig.Mounts) == 0 {
return nil
}
// md.setVolumes already queried for existent unattached volumes, do not create more
existentVolumes := lo.MapValues(md.volumes, func(vs []fly.Volume, _ string) int {
return len(vs)
})
// The logic here is to provision one volume per process group that needs it only on the primary region
for _, groupName := range md.appConfig.ProcessNames() {
groupConfig, err := md.appConfig.Flatten(groupName)
if err != nil {
return err
}
mConfig, err := md.appConfig.ToMachineConfig(groupName, nil)
if err != nil {
return err
}
guest := md.machineGuest
if mConfig.Guest != nil {
guest = mConfig.Guest
}
for _, m := range groupConfig.Mounts {
if v := existentVolumes[m.Source]; v > 0 {
existentVolumes[m.Source]--
continue
}
var initialSize int
switch {
case m.InitialSize != "":
// Ignore the error because invalid values are caught at config validation time
initialSize, _ = helpers.ParseSize(m.InitialSize, units.FromHumanSize, units.GB)
case md.volumeInitialSize > 0:
initialSize = md.volumeInitialSize
case guest != nil && guest.GPUKind != "":
initialSize = DefaultGPUVolumeInitialSizeGB
default:
initialSize = DefaultVolumeInitialSizeGB
}
fmt.Fprintf(
md.io.Out,
"Creating a %d GB volume named '%s' for process group '%s'. "+
"Use 'fly vol extend' to increase its size\n",
initialSize, m.Source, groupName,
)
input := fly.CreateVolumeRequest{
Name: m.Source,
Region: groupConfig.PrimaryRegion,
SizeGb: fly.Pointer(initialSize),
Encrypted: fly.Pointer(true),
ComputeRequirements: guest,
ComputeImage: md.img,
SnapshotRetention: m.SnapshotRetention,
}
vol, err := md.flapsClient.CreateVolume(ctx, input)
if err != nil {
return err
}
md.volumes[m.Source] = append(md.volumes[m.Source], *vol)
}
}
return nil
}