Skip to content

Commit

Permalink
launch v2: split launchState into new file
Browse files Browse the repository at this point in the history
  • Loading branch information
alichay committed Aug 8, 2023
1 parent 42bb749 commit 7e887cc
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 112 deletions.
112 changes: 0 additions & 112 deletions internal/command/launch/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,9 @@ import (
"context"
"errors"
"fmt"
"strings"

"github.com/AlecAivazis/survey/v2"
"github.com/samber/lo"
"github.com/spf13/cobra"
"github.com/superfly/flyctl/api"
"github.com/superfly/flyctl/client"
"github.com/superfly/flyctl/internal/appconfig"
"github.com/superfly/flyctl/internal/command"
"github.com/superfly/flyctl/internal/command/deploy"
"github.com/superfly/flyctl/internal/command/launch/legacy"
Expand Down Expand Up @@ -138,113 +133,6 @@ func run(ctx context.Context) (err error) {
return nil
}

type launchState struct {
workingDir string
configPath string
plan *launchPlan
planSource *launchPlanSource
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
}

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", state.plan.Postgres.Describe(), 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("%state: %state%state %state(%state)\n", label, labelSpaces, value, valueSpaces, source)
}
return ret, nil
}

// familyToAppType returns a string that describes the app type based on the source info
// For example, "Dockerfile" apps would return "app" but a rails app would return "Rails app"
func familyToAppType(si *scanner.SourceInfo) string {
Expand Down
120 changes: 120 additions & 0 deletions internal/command/launch/state.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
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/scanner"
)

type launchState struct {
workingDir string
configPath string
plan *launchPlan
planSource *launchPlanSource
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
}

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", state.plan.Postgres.Describe(), 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("%state: %state%state %state(%state)\n", label, labelSpaces, value, valueSpaces, source)
}
return ret, nil
}

0 comments on commit 7e887cc

Please sign in to comment.