-
Notifications
You must be signed in to change notification settings - Fork 223
/
state.go
140 lines (118 loc) · 3.32 KB
/
state.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
package launch
import (
"context"
"fmt"
"strings"
"github.com/samber/lo"
"github.com/superfly/flyctl/api"
"github.com/superfly/flyctl/client"
"github.com/superfly/flyctl/internal/appconfig"
"github.com/superfly/flyctl/internal/command/launch/plan"
"github.com/superfly/flyctl/scanner"
)
// Let's *try* to keep this struct backwards-compatible as we change it
type launchPlanSource struct {
appNameSource string
regionSource string
orgSource string
guestSource string
postgresSource string
redisSource string
}
type LaunchManifest struct {
Plan *plan.LaunchPlan
PlanSource *launchPlanSource
}
type launchState struct {
workingDir string
configPath string
LaunchManifest
env map[string]string
appConfig *appconfig.Config
sourceInfo *scanner.SourceInfo
cache map[string]interface{}
}
func cacheGrab[T any](cache map[string]interface{}, key string, cb func() (T, error)) (T, error) {
if val, ok := cache[key]; ok {
return val.(T), nil
}
val, err := cb()
if err != nil {
return val, err
}
cache[key] = val
return val, nil
}
func (state *launchState) Org(ctx context.Context) (*api.Organization, error) {
apiClient := client.FromContext(ctx).API()
return cacheGrab(state.cache, "org,"+state.Plan.OrgSlug, func() (*api.Organization, error) {
return apiClient.GetOrganizationBySlug(ctx, state.Plan.OrgSlug)
})
}
func (state *launchState) Region(ctx context.Context) (api.Region, error) {
apiClient := client.FromContext(ctx).API()
regions, err := cacheGrab(state.cache, "regions", func() ([]api.Region, error) {
regions, _, err := apiClient.PlatformRegions(ctx)
if err != nil {
return nil, err
}
return regions, nil
})
if err != nil {
return api.Region{}, err
}
region, ok := lo.Find(regions, func(r api.Region) bool {
return r.Code == state.Plan.RegionCode
})
if !ok {
return region, fmt.Errorf("region %state not found", state.Plan.RegionCode)
}
return region, nil
}
// PlanSummary returns a human-readable summary of the launch plan.
// Used to confirm the plan before executing it.
func (state *launchState) PlanSummary(ctx context.Context) (string, error) {
guest := state.Plan.Guest()
org, err := state.Org(ctx)
if err != nil {
return "", err
}
region, err := state.Region(ctx)
if err != nil {
return "", err
}
postgresStr, err := state.Plan.Postgres.Describe(ctx)
if err != nil {
return "", err
}
redisStr, err := state.Plan.Redis.Describe(ctx)
if err != nil {
return "", err
}
rows := [][]string{
{"Organization", org.Name, state.PlanSource.orgSource},
{"Name", state.Plan.AppName, state.PlanSource.appNameSource},
{"Region", region.Name, state.PlanSource.regionSource},
{"App Machines", guest.String(), state.PlanSource.guestSource},
{"Postgres", postgresStr, state.PlanSource.postgresSource},
{"Redis", redisStr, state.PlanSource.redisSource},
}
colLengths := []int{0, 0, 0}
for _, row := range rows {
for i, col := range row {
if len(col) > colLengths[i] {
colLengths[i] = len(col)
}
}
}
ret := ""
for _, row := range rows {
label := row[0]
value := row[1]
source := row[2]
labelSpaces := strings.Repeat(" ", colLengths[0]-len(label))
valueSpaces := strings.Repeat(" ", colLengths[1]-len(value))
ret += fmt.Sprintf("%s: %s%s %s(%s)\n", label, labelSpaces, value, valueSpaces, source)
}
return ret, nil
}