Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions internal/appconfig/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,31 @@ func (cfg *Config) DeployStrategy() string {
}
return cfg.Deploy.Strategy
}

// HasHealthChecks returns true if there is at least one health check defined in
// the configuration. It inspects top-level checks as well as checks defined for
// any service or the http_service.
func (cfg *Config) HasHealthChecks() bool {
if len(cfg.Checks) > 0 || len(cfg.MachineChecks) > 0 {
return true
}

if cfg.HTTPService != nil {
if len(cfg.HTTPService.HTTPChecks) > 0 || len(cfg.HTTPService.MachineChecks) > 0 {
return true
}
}

for _, svc := range cfg.Services {
if len(svc.HTTPChecks) > 0 || len(svc.TCPChecks) > 0 || len(svc.MachineChecks) > 0 {
return true
}
}

return false
}

// HasMounts returns true if the configuration defines any mounts.
func (cfg *Config) HasMounts() bool {
return len(cfg.Mounts) > 0
}
15 changes: 15 additions & 0 deletions internal/command/deploy/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,21 @@ func DeployWithConfig(ctx context.Context, appConfig *appconfig.Config, userID i
}
}

strategy := flag.GetString(ctx, "strategy")
if strategy == "" {
strategy = appConfig.DeployStrategy()
}
if strategy == "bluegreen" {
if appConfig.HasMounts() {
fmt.Fprint(io.ErrOut, volumesRequirementMsg)
return ErrValidationError
}
if !appConfig.HasHealthChecks() {
fmt.Fprint(io.ErrOut, healthChecksRequirementMsg)
return ErrValidationError
}
}

httpFailover := flag.GetHTTPSFailover(ctx)
usingWireguard := flag.GetWireguard(ctx)
recreateBuilder := flag.GetRecreateBuilder(ctx)
Expand Down
15 changes: 14 additions & 1 deletion internal/command/deploy/strategy_bluegreen.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ var (
safeToDestroyValue = "safe_to_destroy"
)

const healthChecksRequirementMsg = "\n\nYou need to define at least 1 check in order to use bluegreen deployments. Refer to https://fly.io/docs/reference/configuration/#services-tcp_checks\n"
const volumesRequirementMsg = "\n\nBluegreen deployments are not supported when machines have attached volumes.\n"

type RollbackLog struct {
// this ensures that user invoked aborts after green machines are healthy
// doesn't cause the greeen machines to be removed. eg. if someone aborts after cordoning blue machines
Expand Down Expand Up @@ -688,6 +691,16 @@ func (bg *blueGreen) Deploy(ctx context.Context) error {
return ErrOrgLimit
}

if bg.appConfig.HasMounts() && len(bg.blueMachines) != 0 {
fmt.Fprint(bg.io.ErrOut, volumesRequirementMsg)
return ErrValidationError
}

if !bg.appConfig.HasHealthChecks() && len(bg.blueMachines) != 0 {
fmt.Fprint(bg.io.ErrOut, healthChecksRequirementMsg)
return ErrValidationError
}

fmt.Fprintf(bg.io.ErrOut, "\nVerifying if app can be safely deployed \n")

err = bg.DetectMultipleImageVersions(ctx)
Expand All @@ -709,7 +722,7 @@ func (bg *blueGreen) Deploy(ctx context.Context) error {
}

if totalChecks == 0 && len(bg.blueMachines) != 0 {
fmt.Fprintf(bg.io.ErrOut, "\n\nYou need to define at least 1 check in order to use blue-green deployments. Refer to https://fly.io/docs/reference/configuration/#services-tcp_checks\n")
fmt.Fprint(bg.io.ErrOut, healthChecksRequirementMsg)
return ErrValidationError
}

Expand Down
17 changes: 12 additions & 5 deletions internal/command/deploy/strategy_bluegreen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,18 @@ func newBlueGreenStrategy(client flapsutil.FlapsClient, numberOfExistingMachines
apiClient: &mockWebClient{},
flaps: client,
maxConcurrent: 10,
appConfig: &appconfig.Config{},
io: ios,
colorize: ios.ColorScheme(),
timeout: 1 * time.Second,
blueMachines: machines,
appConfig: &appconfig.Config{
Checks: map[string]*appconfig.ToplevelCheck{
"alive": {
Type: fly.Pointer("tcp"),
Port: fly.Pointer(8080),
},
},
},
io: ios,
colorize: ios.ColorScheme(),
timeout: 1 * time.Second,
blueMachines: machines,
}
strategy.initialize()

Expand Down
Loading