Skip to content

Commit

Permalink
add [metrics] support, fixed checks with processes, and got rid of vo…
Browse files Browse the repository at this point in the history
…lume names for machines
  • Loading branch information
tvdfly authored and dangra committed Jan 25, 2023
1 parent 1dcbf77 commit 786c7c4
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 107 deletions.
83 changes: 67 additions & 16 deletions internal/app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -824,13 +824,14 @@ func (c *Config) SetVolumes(volumes []scanner.Volume) {
c.Definition["mounts"] = volumes
}

type CmdAndMachineServices struct {
type ProcessConfig struct {
Cmd []string
MachineServices []api.MachineService
MachineChecks map[string]api.MachineCheck
}

func (c *Config) GetProcessNamesToCmdAndService() (map[string]CmdAndMachineServices, error) {
res := make(map[string]CmdAndMachineServices)
func (c *Config) GetProcessConfigs() (map[string]ProcessConfig, error) {
res := make(map[string]ProcessConfig)
processCount := 0
if c.Processes != nil {
processCount = len(c.Processes)
Expand All @@ -844,16 +845,30 @@ func (c *Config) GetProcessNamesToCmdAndService() (map[string]CmdAndMachineServi
}
}
if processCount > 0 {
for procName := range c.Processes {
res[procName] = CmdAndMachineServices{
Cmd: strings.Split(c.Processes[procName], " "),
for processName := range c.Processes {
res[processName] = ProcessConfig{
Cmd: strings.Split(c.Processes[processName], " "),
MachineServices: make([]api.MachineService, 0),
MachineChecks: make(map[string]api.MachineCheck),
}
}
} else {
res[defaultProcessName] = CmdAndMachineServices{
res[defaultProcessName] = ProcessConfig{
Cmd: strings.Split(c.Processes[defaultProcessName], " "),
MachineServices: make([]api.MachineService, 0),
MachineChecks: make(map[string]api.MachineCheck),
}
}
for checkName, check := range c.Checks {
fullCheckName := fmt.Sprintf("chk-%s-%s", checkName, check.String())
machineCheck, err := check.ToMachineCheck()
if err != nil {
return nil, err
}
for processName := range res {
procToUpdate := res[processName]
procToUpdate.MachineChecks[fullCheckName] = *machineCheck
res[processName] = procToUpdate
}
}
if c.HttpService != nil {
Expand All @@ -867,20 +882,56 @@ func (c *Config) GetProcessNamesToCmdAndService() (map[string]CmdAndMachineServi
for _, service := range c.Services {
if len(service.Processes) == 0 && processCount > 1 {
return nil, fmt.Errorf("error service has no processes set and app has %d processes defined; update fly.toml to set processes for each service", processCount)
} else if len(service.Processes) > 1 && processCount == 0 {
} else if len(service.Processes) > 0 && processCount == 0 {
return nil, fmt.Errorf("error services has %d processes defined, but no processes are defined in app config; add a [processes] section to fly.toml", processCount)
} else if len(service.Processes) == 0 {
servicesToUpdate := res[firstProcessNameOrDefault]
servicesToUpdate.MachineServices = append(servicesToUpdate.MachineServices, *service.ToMachineService())
res[firstProcessNameOrDefault] = servicesToUpdate
} else { // len(service.Processes) > 1
processName := firstProcessNameOrDefault
procConfigToUpdate, present := res[processName]
if !present {
return nil, fmt.Errorf("error service specifies '%s' as one of its processes, but no processes are defined with that name; update fly.toml [processes] to include a %s process", processName, processName)
}
procConfigToUpdate.MachineServices = append(procConfigToUpdate.MachineServices, *service.ToMachineService())
for _, httpCheck := range service.HttpChecks {
checkName := fmt.Sprintf("svcchk-%s", httpCheck.String(service.InternalPort))
machineCheck, err := httpCheck.ToMachineCheck(service.InternalPort)
if err != nil {
return nil, err
}
procConfigToUpdate.MachineChecks[checkName] = *machineCheck
}
for _, tcpCheck := range service.TcpChecks {
checkName := fmt.Sprintf("svcchk-%s", tcpCheck.String(service.InternalPort))
machineCheck, err := tcpCheck.ToMachineCheck(service.InternalPort)
if err != nil {
return nil, err
}
procConfigToUpdate.MachineChecks[checkName] = *machineCheck
}
res[processName] = procConfigToUpdate
} else { // len(service.Processes) > 0
for _, processName := range service.Processes {
if _, present := res[processName]; !present {
procConfigToUpdate, present := res[processName]
if !present {
return nil, fmt.Errorf("error service specifies '%s' as one of its processes, but no processes are defined with that name; update fly.toml [processes] to include a %s process", processName, processName)
}
servicesToUpdate := res[processName]
servicesToUpdate.MachineServices = append(servicesToUpdate.MachineServices, *service.ToMachineService())
res[processName] = servicesToUpdate
procConfigToUpdate.MachineServices = append(procConfigToUpdate.MachineServices, *service.ToMachineService())
for _, httpCheck := range service.HttpChecks {
checkName := fmt.Sprintf("svcchk-%s", httpCheck.String(service.InternalPort))
machineCheck, err := httpCheck.ToMachineCheck(service.InternalPort)
if err != nil {
return nil, err
}
procConfigToUpdate.MachineChecks[checkName] = *machineCheck
}
for _, tcpCheck := range service.TcpChecks {
checkName := fmt.Sprintf("svcchk-%s", tcpCheck.String(service.InternalPort))
machineCheck, err := tcpCheck.ToMachineCheck(service.InternalPort)
if err != nil {
return nil, err
}
procConfigToUpdate.MachineChecks[checkName] = *machineCheck
}
res[processName] = procConfigToUpdate
}
}
}
Expand Down
131 changes: 45 additions & 86 deletions internal/command/deploy/machines.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,28 +46,26 @@ type MachineDeploymentArgs struct {
}

type machineDeployment struct {
gqlClient graphql.Client
flapsClient *flaps.Client
io *iostreams.IOStreams
colorize *iostreams.ColorScheme
app *api.AppCompact
appConfig *app.Config
img *imgsrc.DeploymentImage
machineSet MachineSet
releaseCommandMachine MachineSet
releaseCommand string
volumeName string
volumeDestination string
appChecksForMachines map[string]api.MachineCheck
appCmdAndServicesForMachines map[string]app.CmdAndMachineServices
strategy string
releaseId string
releaseVersion int
autoConfirmAppsV2Migration bool
skipHealthChecks bool
restartOnly bool
waitTimeout time.Duration
leaseTimeout time.Duration
gqlClient graphql.Client
flapsClient *flaps.Client
io *iostreams.IOStreams
colorize *iostreams.ColorScheme
app *api.AppCompact
appConfig *app.Config
processConfigs map[string]app.ProcessConfig
img *imgsrc.DeploymentImage
machineSet MachineSet
releaseCommandMachine MachineSet
releaseCommand string
volumeDestination string
strategy string
releaseId string
releaseVersion int
autoConfirmAppsV2Migration bool
skipHealthChecks bool
restartOnly bool
waitTimeout time.Duration
leaseTimeout time.Duration
}

type MachineSet interface {
Expand Down Expand Up @@ -506,6 +504,10 @@ func NewMachineDeployment(ctx context.Context, args MachineDeploymentArgs) (Mach
if waitTimeout != DefaultWaitTimeout || leaseTimeout != DefaultLeaseTtl || args.WaitTimeout == 0 || args.LeaseTimeout == 0 {
terminal.Infof("Using wait timeout: %s and lease timeout: %s\n", waitTimeout, leaseTimeout)
}
processConfigs, err := appConfig.GetProcessConfigs()
if err != nil {
return nil, err
}
io := iostreams.FromContext(ctx)
md := &machineDeployment{
gqlClient: client.FromContext(ctx).API().GenqClient,
Expand All @@ -514,6 +516,7 @@ func NewMachineDeployment(ctx context.Context, args MachineDeploymentArgs) (Mach
colorize: io.ColorScheme(),
app: app,
appConfig: appConfig,
processConfigs: processConfigs,
img: args.DeploymentImage,
autoConfirmAppsV2Migration: args.AutoConfirmMigration,
skipHealthChecks: args.SkipHealthChecks,
Expand All @@ -523,10 +526,6 @@ func NewMachineDeployment(ctx context.Context, args MachineDeploymentArgs) (Mach
releaseCommand: releaseCmd,
}
md.setStrategy(args.Strategy)
err = md.translateServicesAndChecksForMachines()
if err != nil {
return nil, err
}
err = md.setVolumeConfig()
if err != nil {
return nil, err
Expand Down Expand Up @@ -639,6 +638,12 @@ func (md *machineDeployment) DeployMachinesApp(ctx context.Context) error {
// FIXME: combine this wait with the wait for start as one update line (or two per in noninteractive case)
if err != nil {
return err
} else {
md.logClearLinesAbove(1)
fmt.Fprintf(md.io.ErrOut, " Machine %s update finished: %s\n",
md.colorize.Bold(m.FormattedMachineId()),
md.colorize.Green("success"),
)
}
}
}
Expand Down Expand Up @@ -813,8 +818,10 @@ func (md *machineDeployment) updateReleaseCommandMachine(ctx context.Context) er
}

func (md *machineDeployment) setVolumeConfig() error {
if md.appConfig.Mounts.Source != "" {
return fmt.Errorf("error source setting under [mounts] is not supported for machines; remove source from fly.toml")
}
if md.appConfig.Mounts != nil {
md.volumeName = md.appConfig.Mounts.Source
md.volumeDestination = md.appConfig.Mounts.Destination
}
return nil
Expand Down Expand Up @@ -867,18 +874,11 @@ func (md *machineDeployment) validateVolumeConfig() error {
if len(mountsConfig) > 1 {
return fmt.Errorf("error machine %s has %d mounts and expected 1", mid, len(mountsConfig))
}
if md.volumeName == "" {
if len(mountsConfig) != 0 {
return fmt.Errorf("error machine %s has a volume mounted and app config does not specify a volume; remove the volume from the machine or add a [mounts] configuration to fly.toml", mid)
}
} else {
if len(mountsConfig) == 0 {
return fmt.Errorf("error machine %s does not have a volume configured and fly.toml expects one with name %s; remove the [mounts] configuration in fly.toml or use the machines API to add a volume to this machine", mid, md.volumeName)
}
mVolName := mountsConfig[0].Name
if md.volumeName != mVolName {
return fmt.Errorf("error machine %s has volume with name %s and fly.toml has [mounts] source set to %s; update the source to %s or use the machines API to attach a volume with name %s to this machine", mid, mVolName, md.volumeName, mVolName, md.volumeName)
}
if md.volumeDestination == "" && len(mountsConfig) != 0 {
return fmt.Errorf("error machine %s has a volume mounted and app config does not specify a volume; remove the volume from the machine or add a [mounts] configuration to fly.toml", mid)
}
if md.volumeDestination != "" && len(mountsConfig) == 0 {
return fmt.Errorf("error machine %s does not have a volume configured and fly.toml expects one with destination %s; remove the [mounts] configuration in fly.toml or use the machines API to add a volume to this machine", mid, md.volumeDestination)
}
}
return nil
Expand Down Expand Up @@ -959,10 +959,10 @@ func (md *machineDeployment) resolveUpdatedMachineConfig(origMachineRaw *api.Mac
}

launchInput.Config.Image = md.img.Tag
launchInput.Config.Checks = md.appChecksForMachines
launchInput.Config.Metrics = md.appConfig.Metrics

launchInput.Config.Restart = origMachineRaw.Config.Restart
launchInput.Config.Metrics = md.appConfig.Metrics
launchInput.Config.Env = md.appConfig.Env
if launchInput.Config.Env == nil {
launchInput.Config.Env = map[string]string{}
Expand All @@ -981,56 +981,15 @@ func (md *machineDeployment) resolveUpdatedMachineConfig(origMachineRaw *api.Mac
if origMachineRaw.Config.Guest != nil {
launchInput.Config.Guest = origMachineRaw.Config.Guest
}
processGroup := origMachineRaw.Config.Metadata[api.MachineConfigMetadataKeyFlyProcessGroup]
cmdAndServices := md.appCmdAndServicesForMachines[processGroup]
launchInput.Config.Services = cmdAndServices.MachineServices
launchInput.Config.Init = origMachineRaw.Config.Init
if cmdAndServices.Cmd != nil {
launchInput.Config.Init.Cmd = cmdAndServices.Cmd
}
processGroup := origMachineRaw.Config.Metadata[api.MachineConfigMetadataKeyFlyProcessGroup]
processConfig := md.processConfigs[processGroup]
launchInput.Config.Services = processConfig.MachineServices
launchInput.Config.Init.Cmd = processConfig.Cmd
launchInput.Config.Checks = processConfig.MachineChecks
return launchInput
}

func (md *machineDeployment) translateServicesAndChecksForMachines() error {
processNamesToCmdAndService, err := md.appConfig.GetProcessNamesToCmdAndService()
if err != nil {
return err
}
md.appCmdAndServicesForMachines = processNamesToCmdAndService
checkCount := 0
md.appChecksForMachines = make(map[string]api.MachineCheck)
for checkName, check := range md.appConfig.Checks {
fullCheckName := fmt.Sprintf("chk-%s-%s", checkName, check.String())
machineCheck, err := check.ToMachineCheck()
if err != nil {
return err
}
md.appChecksForMachines[fullCheckName] = *machineCheck
}
checkCount += len(md.appConfig.Checks)
for _, service := range md.appConfig.Services {
for i, httpCheck := range service.HttpChecks {
checkName := fmt.Sprintf("svcchk%d-%s", checkCount+i, httpCheck.String(service.InternalPort))
machineCheck, err := httpCheck.ToMachineCheck(service.InternalPort)
if err != nil {
return err
}
md.appChecksForMachines[checkName] = *machineCheck
}
checkCount += len(service.HttpChecks)
for i, tcpCheck := range service.TcpChecks {
checkName := fmt.Sprintf("svcchk%d-%s", checkCount+i, tcpCheck.String(service.InternalPort))
machineCheck, err := tcpCheck.ToMachineCheck(service.InternalPort)
if err != nil {
return err
}
md.appChecksForMachines[checkName] = *machineCheck
}
checkCount += len(service.TcpChecks)
}
return nil
}

func (md *machineDeployment) defaultMachineMetadata() map[string]string {
res := map[string]string{
api.MachineConfigMetadataKeyFlyPlatformVersion: api.MachineFlyPlatformVersion2,
Expand Down
9 changes: 9 additions & 0 deletions internal/command/launch/launch.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ func New() (cmd *cobra.Command) {
Description: "If a .dockerignore does not exist, create one from .gitignore files",
Default: false,
},
// FIXME: pipe this through
// flag.Int{
// Name: "internal-port",
// Description: "Set internal_port for all services in the generated fly.toml",
// Default: 8080,
// },
)

return
Expand All @@ -101,6 +107,7 @@ func run(ctx context.Context) (err error) {
configFilePath := filepath.Join(workingDir, "fly.toml")

if exists, _ := flyctl.ConfigFileExistsAtPath(configFilePath); exists {
// FIXME: don't assume nomad platform here... this might be a place where we need to support migration
cfg, err := app.LoadConfig(ctx, configFilePath, "nomad")
if err != nil {
return err
Expand Down Expand Up @@ -502,6 +509,8 @@ func run(ctx context.Context) (err error) {
}

if deployNow {
// FIXME: teach this how to do pick machines vs nomad
terminal.Errorf("BOOM: appConfig.ForMachines(): %t\n", appConfig.ForMachines())
return deploy.DeployWithConfig(ctx, appConfig)
}

Expand Down
11 changes: 6 additions & 5 deletions internal/command/machine/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,21 +100,22 @@ func runMachineClone(ctx context.Context) (err error) {

targetConfig := source.Config
if targetProcessGroup := flag.GetString(ctx, "process-group"); targetProcessGroup != "" {
allCmdAndServices, err := appConfig.GetProcessNamesToCmdAndService()
allProcessConfigs, err := appConfig.GetProcessConfigs()
if err != nil {
return err
}
cmdAndServices, present := allCmdAndServices[targetProcessGroup]
processConfig, present := allProcessConfigs[targetProcessGroup]
if !present {
return fmt.Errorf("process group %s is not present in app configuration, add a [processes] section to fly.toml", targetProcessGroup)
}
if targetProcessGroup == api.MachineProcessGroupFlyAppReleaseCommand {
return fmt.Errorf("invalid process group %s, %s is reserved for internal use", targetProcessGroup, api.MachineProcessGroupFlyAppReleaseCommand)
}
targetConfig.Metadata[api.MachineConfigMetadataKeyFlyProcessGroup] = targetProcessGroup
terminal.Infof("Setting process group to %s for new machine and updating cmd and services\n", targetProcessGroup)
targetConfig.Init.Cmd = cmdAndServices.Cmd
targetConfig.Services = cmdAndServices.MachineServices
terminal.Infof("Setting process group to %s for new machine and updating cmd, services, and checks\n", targetProcessGroup)
targetConfig.Init.Cmd = processConfig.Cmd
targetConfig.Services = processConfig.MachineServices
targetConfig.Checks = processConfig.MachineChecks
}
for _, mnt := range source.Config.Mounts {
var vol *api.Volume
Expand Down

0 comments on commit 786c7c4

Please sign in to comment.