-
Notifications
You must be signed in to change notification settings - Fork 224
/
deploy_first.go
128 lines (108 loc) · 4.05 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
package deploy
import (
"context"
"fmt"
"github.com/samber/lo"
"github.com/superfly/flyctl/api"
"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 remmediate 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")
}
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 []api.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
}
for _, m := range groupConfig.Mounts {
if v := existentVolumes[m.Source]; v > 0 {
existentVolumes[m.Source]--
continue
}
fmt.Fprintf(md.io.Out, "Creating 1GB volume '%s' for process group '%s'. Use 'fly vol extend' to increase its size\n", m.Source, groupName)
input := api.CreateVolumeInput{
AppID: md.app.ID,
Name: m.Source,
Region: groupConfig.PrimaryRegion,
SizeGb: 1,
Encrypted: true,
}
vol, err := md.apiClient.CreateVolume(ctx, input)
if err != nil {
return fmt.Errorf("failed creating volume: %w", err)
}
md.volumes[m.Source] = append(md.volumes[m.Source], *vol)
}
}
return nil
}