Merge pull request #4280 from dokku/parallelize-internals

Parallelize report and scheduler retrieval
This commit is contained in:
Jose Diaz-Gonzalez
2020-12-30 03:16:27 -05:00
committed by GitHub
10 changed files with 302 additions and 129 deletions

View File

@@ -12,23 +12,36 @@ func ReportSingleApp(appName, infoFlag string) error {
return err
}
flags := map[string]common.ReportFunc{
"--app-dir": reportDir,
"--app-deploy-source": reportDeploySource,
"--app-locked": reportLocked,
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("app", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
}
func reportDir(appName string) string {
return common.AppRoot(appName)
}
func reportDeploySource(appName string) string {
deploySource := ""
if b, err := common.PlugnTriggerSetup("deploy-source", []string{appName}...).SetInput("").Output(); err != nil {
deploySource = strings.TrimSpace(string(b[:]))
}
return deploySource
}
func reportLocked(appName string) string {
locked := "false"
if appIsLocked(appName) {
locked = "true"
}
infoFlags := map[string]string{
"--app-dir": common.AppRoot(appName),
"--app-deploy-source": deploySource,
"--app-locked": locked,
}
trimPrefix := false
uppercaseFirstCharacter := true
return common.ReportSingleApp("app", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
return locked
}

View File

@@ -12,16 +12,21 @@ func ReportSingleApp(appName, infoFlag string) error {
return err
}
buildpacks, err := common.PropertyListGet("buildpacks", appName, "buildpacks")
if err != nil {
return err
}
infoFlags := map[string]string{
"--buildpacks-list": strings.Join(buildpacks, ","),
flags := map[string]common.ReportFunc{
"--buildpacks-list": reportList,
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("buildpacks", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
}
func reportList(appName string) string {
buildpacks, err := common.PropertyListGet("buildpacks", appName, "buildpacks")
if err != nil {
return ""
}
return strings.Join(buildpacks, ",")
}

View File

@@ -11,6 +11,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
"unicode"
"github.com/ryanuber/columnize"
@@ -64,17 +65,34 @@ func CommandUsage(helpHeader string, helpContent string) {
// GetAppScheduler fetches the scheduler for a given application
func GetAppScheduler(appName string) string {
if appName == "--global" {
appName = ""
}
appScheduler := ""
globalScheduler := ""
b, _ := PlugnTriggerOutput("config-get", []string{appName, "DOKKU_SCHEDULER"}...)
value := strings.TrimSpace(string(b[:]))
if value != "" {
return value
}
var wg sync.WaitGroup
return GetGlobalScheduler()
if appName != "--global" {
wg.Add(1)
go func() {
defer wg.Done()
b, _ := PlugnTriggerOutput("config-get", []string{appName, "DOKKU_SCHEDULER"}...)
value := strings.TrimSpace(string(b[:]))
if value != "" {
appScheduler = value
}
}()
}
wg.Add(1)
go func() {
defer wg.Done()
globalScheduler = GetGlobalScheduler()
}()
wg.Wait()
if appScheduler == "" {
appScheduler = globalScheduler
}
return appScheduler
}
// GetGlobalScheduler fetchs the global scheduler

View File

@@ -132,3 +132,35 @@ func createParallelWorkerPool(jobs chan string, results chan parallelCommandResu
wg.Wait()
close(results)
}
// ReportFunc is a type that declares functions
// useful for :report subcommands
type ReportFunc func(string) string
// CollectReport iterates over a set of report functions
// to collect the :report output in parallel
func CollectReport(appName string, infoFlag string, flags map[string]ReportFunc) map[string]string {
var sm sync.Map
var wg sync.WaitGroup
for flag, fn := range flags {
if infoFlag != "" && infoFlag != flag {
continue
}
wg.Add(1)
go func(flag string, fn ReportFunc) {
defer wg.Done()
s := fn(appName)
sm.Store(flag, s)
}(flag, fn)
}
wg.Wait()
infoFlags := map[string]string{}
sm.Range(func(key interface{}, value interface{}) bool {
infoFlags[key.(string)] = value.(string)
return true
})
return infoFlags
}

View File

@@ -206,21 +206,3 @@ func ClearNetworkConfig(appName string) bool {
}
return success
}
// ReportSingleApp is an internal function that displays the app report for one or more apps
func ReportSingleApp(appName, infoFlag string) error {
if err := common.VerifyAppName(appName); err != nil {
return err
}
infoFlags := map[string]string{
"--network-bind-all-interfaces": common.PropertyGet("network", appName, "bind-all-interfaces"),
"--network-attach-post-create": common.PropertyGet("network", appName, "attach-post-create"),
"--network-attach-post-deploy": common.PropertyGet("network", appName, "attach-post-deploy"),
"--network-web-listeners": strings.Join(GetListeners(appName, "web"), " "),
}
trimPrefix := false
uppercaseFirstCharacter := true
return common.ReportSingleApp("network", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
}

42
plugins/network/report.go Normal file
View File

@@ -0,0 +1,42 @@
package network
import (
"strings"
"github.com/dokku/dokku/plugins/common"
)
// ReportSingleApp is an internal function that displays the app report for one or more apps
func ReportSingleApp(appName, infoFlag string) error {
if err := common.VerifyAppName(appName); err != nil {
return err
}
flags := map[string]common.ReportFunc{
"--network-bind-all-interfaces": reportBindAllInterfaces,
"--network-attach-post-create": reportAttachPostCreate,
"--network-attach-post-deploy": reportAttachPostDeploy,
"--network-web-listeners": reportWebListeners,
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("network", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
}
func reportBindAllInterfaces(appName string) string {
return common.PropertyGet("network", appName, "bind-all-interfaces")
}
func reportAttachPostCreate(appName string) string {
return common.PropertyGet("network", appName, "attach-post-create")
}
func reportAttachPostDeploy(appName string) string {
return common.PropertyGet("network", appName, "attach-post-deploy")
}
func reportWebListeners(appName string) string {
return strings.Join(GetListeners(appName, "web"), " ")
}

View File

@@ -43,33 +43,6 @@ func IsAppProxyEnabled(appName string) bool {
return proxyEnabled
}
// ReportSingleApp is an internal function that displays the app report for one or more apps
func ReportSingleApp(appName string, infoFlag string) error {
if err := common.VerifyAppName(appName); err != nil {
return err
}
proxyEnabled := "false"
if IsAppProxyEnabled(appName) {
proxyEnabled = "true"
}
var proxyPortMap []string
for _, portMap := range getProxyPortMap(appName) {
proxyPortMap = append(proxyPortMap, portMap.String())
}
infoFlags := map[string]string{
"--proxy-enabled": proxyEnabled,
"--proxy-type": getAppProxyType(appName),
"--proxy-port-map": strings.Join(proxyPortMap, " "),
}
trimPrefix := false
uppercaseFirstCharacter := true
return common.ReportSingleApp("proxy", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
}
func addProxyPorts(appName string, proxyPortMap []PortMap) error {
allPortMaps := getProxyPortMap(appName)
allPortMaps = append(allPortMaps, proxyPortMap...)

47
plugins/proxy/report.go Normal file
View File

@@ -0,0 +1,47 @@
package proxy
import (
"strings"
"github.com/dokku/dokku/plugins/common"
)
// ReportSingleApp is an internal function that displays the app report for one or more apps
func ReportSingleApp(appName string, infoFlag string) error {
if err := common.VerifyAppName(appName); err != nil {
return err
}
flags := map[string]common.ReportFunc{
"--proxy-enabled": reportEnabled,
"--proxy-type": reportType,
"--proxy-port-map": reportPortMap,
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("proxy", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
}
func reportEnabled(appName string) string {
proxyEnabled := "false"
if IsAppProxyEnabled(appName) {
proxyEnabled = "true"
}
return proxyEnabled
}
func reportType(appName string) string {
return getAppProxyType(appName)
}
func reportPortMap(appName string) string {
var proxyPortMap []string
for _, portMap := range getProxyPortMap(appName) {
proxyPortMap = append(proxyPortMap, portMap.String())
}
return strings.Join(proxyPortMap, " ")
}

View File

@@ -2,7 +2,6 @@ package ps
import (
"fmt"
"strconv"
"strings"
"github.com/dokku/dokku/plugins/common"
@@ -24,64 +23,6 @@ func Rebuild(appName string) error {
return common.PlugnTrigger("receive-app", []string{appName}...)
}
// ReportSingleApp is an internal function that displays the ps report for one or more apps
func ReportSingleApp(appName, infoFlag string) error {
if err := common.VerifyAppName(appName); err != nil {
return err
}
policy, _ := getRestartPolicy(appName)
if policy == "" {
policy = DefaultProperties["restart-policy"]
}
canScale := "false"
if canScaleApp(appName) {
canScale = "true"
}
deployed := "false"
if common.IsDeployed(appName) {
deployed = "true"
}
runningState := getRunningState(appName)
count, err := getProcessCount(appName)
if err != nil {
count = -1
}
b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_APP_RESTORE"}...)
restore := strings.TrimSpace(string(b[:]))
if restore == "0" {
restore = "false"
} else {
restore = "true"
}
infoFlags := map[string]string{
"--deployed": deployed,
"--processes": strconv.Itoa(count),
"--ps-can-scale": canScale,
"--ps-restart-policy": policy,
"--restore": restore,
"--running": runningState,
}
scheduler := common.GetAppScheduler(appName)
if scheduler == "docker-local" {
processStatus := getProcessStatus(appName)
for process, value := range processStatus {
infoFlags[fmt.Sprintf("--status-%s", process)] = value
}
}
trimPrefix := false
uppercaseFirstCharacter := true
return common.ReportSingleApp("ps", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
}
// Restart restarts the app
func Restart(appName string) error {
if !common.IsDeployed(appName) {

120
plugins/ps/report.go Normal file
View File

@@ -0,0 +1,120 @@
package ps
import (
"fmt"
"strconv"
"strings"
"github.com/dokku/dokku/plugins/common"
)
// ReportSingleApp is an internal function that displays the ps report for one or more apps
func ReportSingleApp(appName, infoFlag string) error {
if err := common.VerifyAppName(appName); err != nil {
return err
}
flags := map[string]common.ReportFunc{
"--deployed": reportDeployed,
"--processes": reportProcesses,
"--ps-can-scale": reportCanScale,
"--ps-restart-policy": reportRestartPolicy,
"--restore": reportRestore,
"--running": reportRunningState,
}
extraFlags := addStatusFlags(appName, infoFlag)
for flag, fn := range extraFlags {
flags[flag] = fn
}
trimPrefix := false
uppercaseFirstCharacter := true
infoFlags := common.CollectReport(appName, infoFlag, flags)
return common.ReportSingleApp("ps", appName, infoFlag, infoFlags, trimPrefix, uppercaseFirstCharacter)
}
func addStatusFlags(appName string, infoFlag string) map[string]common.ReportFunc {
flags := map[string]common.ReportFunc{}
if infoFlag != "" && !strings.HasPrefix(infoFlag, "--status-") {
return flags
}
scheduler := common.GetAppScheduler(appName)
if scheduler != "docker-local" {
return flags
}
containerFiles := common.ListFilesWithPrefix(common.AppRoot(appName), "CONTAINER.")
for _, filename := range containerFiles {
// See https://github.com/golang/go/wiki/CommonMistakes#using-goroutines-on-loop-iterator-variables
containerFilePath := filename
process := strings.TrimPrefix(filename, fmt.Sprintf("%s/CONTAINER.", common.AppRoot(appName)))
flags[fmt.Sprintf("--status-%s", process)] = func(appName string) string {
containerID := common.ReadFirstLine(containerFilePath)
containerStatus, _ := common.DockerInspect(containerID, "{{ .State.Status }}")
if containerStatus == "" {
containerStatus = "missing"
}
return fmt.Sprintf("%s (CID: %s)", containerStatus, containerID[0:11])
}
}
return flags
}
func reportCanScale(appName string) string {
canScale := "false"
if canScaleApp(appName) {
canScale = "true"
}
return canScale
}
func reportDeployed(appName string) string {
deployed := "false"
if common.IsDeployed(appName) {
deployed = "true"
}
return deployed
}
func reportProcesses(appName string) string {
count, err := getProcessCount(appName)
if err != nil {
count = -1
}
return strconv.Itoa(count)
}
func reportRestartPolicy(appName string) string {
policy, _ := getRestartPolicy(appName)
if policy == "" {
policy = DefaultProperties["restart-policy"]
}
return policy
}
func reportRestore(appName string) string {
b, _ := common.PlugnTriggerOutput("config-get", []string{appName, "DOKKU_APP_RESTORE"}...)
restore := strings.TrimSpace(string(b[:]))
if restore == "0" {
restore = "false"
} else {
restore = "true"
}
return restore
}
func reportRunningState(appName string) string {
return getRunningState(appName)
}