package ps import ( "errors" "fmt" "path/filepath" "strings" "github.com/dokku/dokku/plugins/common" dockeroptions "github.com/dokku/dokku/plugins/docker-options" "github.com/gofrs/flock" ) // CommandInspect displays a sanitized version of docker inspect for an app func CommandInspect(appName string) error { if err := common.VerifyAppName(appName); err != nil { return err } scheduler := common.GetAppScheduler(appName) _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "scheduler-inspect", Args: []string{scheduler, appName}, StreamStdio: true, }) return err } // CommandRebuild rebuilds an app from source func CommandRebuild(appName string, allApps bool, parallelCount int) error { if allApps { return common.RunCommandAgainstAllApps(Rebuild, "rebuild", parallelCount) } if err := common.VerifyAppName(appName); err != nil { return err } return Rebuild(appName) } // CommandReport displays a ps report for one or more apps func CommandReport(appName string, format string, infoFlag string) error { if len(appName) == 0 { apps, err := common.DokkuApps() if err != nil { if errors.Is(err, common.NoAppsExist) { common.LogWarn(err.Error()) return nil } return err } for _, appName := range apps { if err := ReportSingleApp(appName, format, infoFlag); err != nil { return err } } return nil } return ReportSingleApp(appName, format, infoFlag) } // CommandRestart restarts an app func CommandRestart(appName string, processName string, allApps bool, parallelCount int) error { if allApps { if processName != "" { return errors.New("Unable to restart all apps when specifying a process name") } return common.RunCommandAgainstAllApps(Restart, "restart", parallelCount) } if err := common.VerifyAppName(appName); err != nil { return err } if processName != "" { return RestartProcess(appName, processName) } return Restart(appName) } // CommandRestore starts previously running apps e.g. after reboot func CommandRestore(appName string, allApps bool, parallelCount int) error { _, err := common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "pre-restore", StreamStdio: true, }) if err != nil { return fmt.Errorf("Error running pre-restore: %s", err) } if allApps { if err := restorePrep(); err != nil { return err } return common.RunCommandAgainstAllApps(Restore, "restore", parallelCount) } if appName == "" { common.LogWarn("Restore specified without app, assuming --all") if err := restorePrep(); err != nil { return err } return common.RunCommandAgainstAllApps(Restore, "restore", parallelCount) } if err := common.VerifyAppName(appName); err != nil { return err } return Restore(appName) } // CommandRetire ensures old containers are retired func CommandRetire(appName string) error { lockFile := filepath.Join(common.GetDataDirectory("ps"), "retire") scheduler := "" if appName == "" { scheduler = common.GetGlobalScheduler() } else { scheduler = common.GetAppScheduler(appName) } fileLock := flock.New(lockFile) locked, err := fileLock.TryLock() if err != nil { return &RetireLockFailed{&err} } defer fileLock.Unlock() if !locked { return &RetireLockFailed{} } common.LogInfo1("Retiring old containers and images") _, err = common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "scheduler-retire", Args: []string{scheduler, appName}, StreamStdio: true, }) if err != nil { return fmt.Errorf("Error retiring containers: %w", err) } common.LogInfo1("Retiring expired run containers") _, err = common.CallPlugnTrigger(common.PlugnTriggerInput{ Trigger: "scheduler-run-retire", StreamStdio: true, }) if err != nil { return fmt.Errorf("Error retiring expired run containers: %w", err) } return err } // CommandScale gets or sets how many instances of a given process to run func CommandScale(appName string, skipDeploy bool, processTuples []string) error { if err := common.VerifyAppName(appName); err != nil { return err } if len(processTuples) == 0 { return scaleReport(appName) } if !canScaleApp(appName) { return fmt.Errorf("App %s contains an app.json file with a formations key and cannot be manually scaled", appName) } common.LogInfo1(fmt.Sprintf("Scaling %s processes: %s", appName, strings.Join(processTuples, " "))) return scaleSet(scaleSetInput{ appName: appName, skipDeploy: skipDeploy, clearExisting: false, processTuples: processTuples, deployOnlyChanged: true, }) } // CommandSet sets or clears a ps property for an app func CommandSet(appName string, property string, value string) error { if property == "restart-policy" { if !isValidRestartPolicy(value) { return errors.New("Invalid restart-policy specified") } common.LogInfo2Quiet(fmt.Sprintf("Setting %s to %s", property, value)) return dockeroptions.SetDockerOptionForPhases(appName, []string{"deploy"}, "restart", value) } common.CommandPropertySet("ps", appName, property, value, DefaultProperties, GlobalProperties) return nil } // CommandStart starts an app func CommandStart(appName string, allApps bool, parallelCount int) error { if allApps { return common.RunCommandAgainstAllApps(Start, "start", parallelCount) } if err := common.VerifyAppName(appName); err != nil { return err } return Start(appName) } // CommandStop stops an app func CommandStop(appName string, allApps bool, parallelCount int) error { if allApps { return common.RunCommandAgainstAllApps(Stop, "stop", parallelCount) } if err := common.VerifyAppName(appName); err != nil { return err } return Stop(appName) }