From e8134f9c2cd6ba70e2c1046d753e9f852ec0451e Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 18 Mar 2019 16:43:31 -0400 Subject: [PATCH] feat: implement resource:report --- docs/advanced-usage/resource-management.md | 103 +++++++++--------- plugins/common/properties.go | 20 ++++ plugins/resource/Makefile | 6 +- plugins/resource/resource.go | 68 ++++++++++++ .../resource/src/subcommands/report/report.go | 33 ++++++ .../resource/src/triggers/report/report.go | 15 +++ 6 files changed, 193 insertions(+), 52 deletions(-) create mode 100644 plugins/resource/src/subcommands/report/report.go create mode 100644 plugins/resource/src/triggers/report/report.go diff --git a/docs/advanced-usage/resource-management.md b/docs/advanced-usage/resource-management.md index 3ac2ef5f7..3a30ede35 100644 --- a/docs/advanced-usage/resource-management.md +++ b/docs/advanced-usage/resource-management.md @@ -305,44 +305,44 @@ dokku resource:report ``` =====> node-js-app resource information - Resource limit cpu: - Resource limit memory: 1024 - Resource limit memory swap: 0 - Resource limit network: 10 - Resource limit network ingress: - Resource limit network egress: - Resource reservation cpu: - Resource reservation memory: 512 - Resource reservation memory swap: - Resource reservation network: 8 - Resource reservation network ingress: - Resource reservation network egress: + web limit cpu: + web limit memory: 1024 + web limit memory swap: 0 + web limit network: 10 + web limit network ingress: + web limit network egress: + web reservation cpu: + web reservation memory: 512 + web reservation memory swap: + web reservation network: 8 + web reservation network ingress: + web reservation network egress: =====> python-sample resource information - Resource limit cpu: - Resource limit memory: - Resource limit memory swap: - Resource limit network: - Resource limit network ingress: - Resource limit network egress: - Resource reservation cpu: - Resource reservation memory: - Resource reservation memory swap: - Resource reservation network: - Resource reservation network ingress: - Resource reservation network egress: + web limit cpu: + web limit memory: + web limit memory swap: + web limit network: + web limit network ingress: + web limit network egress: + web reservation cpu: + web reservation memory: + web reservation memory swap: + web reservation network: + web reservation network ingress: + web reservation network egress: =====> ruby-sample resource information - Resource limit cpu: - Resource limit memory: - Resource limit memory swap: - Resource limit network: - Resource limit network ingress: - Resource limit network egress: - Resource reservation cpu: - Resource reservation memory: - Resource reservation memory swap: - Resource reservation network: - Resource reservation network ingress: - Resource reservation network egress: + web limit cpu: + web limit memory: + web limit memory swap: + web limit network: + web limit network ingress: + web limit network egress: + web reservation cpu: + web reservation memory: + web reservation memory swap: + web reservation network: + web reservation network ingress: + web reservation network egress: ``` You can run the command for a specific app also. @@ -353,22 +353,27 @@ dokku resource:report node-js-app ``` =====> node-js-app resource information - Resource limit cpu: - Resource limit memory: 1024 - Resource limit memory swap: 0 - Resource limit network: 10 - Resource limit network ingress: - Resource limit network egress: - Resource reservation cpu: - Resource reservation memory: 512 - Resource reservation memory swap: - Resource reservation network: 8 - Resource reservation network ingress: - Resource reservation network egress: + web limit cpu: + web limit memory: 1024 + web limit memory swap: 0 + web limit network: 10 + web limit network ingress: + web limit network egress: + web reservation cpu: + web reservation memory: 512 + web reservation memory swap: + web reservation network: 8 + web reservation network ingress: + web reservation network egress: ``` You can pass flags which will output only the value of the specific information you want. For example: ```shell -dokku resource:report node-js-app --resource-memory +# Note the periods in the flag name +dokku resource:report node-js-app --resource-web.limit.memory +``` + +``` +1024 ``` diff --git a/plugins/common/properties.go b/plugins/common/properties.go index f09d02b84..d780003ff 100644 --- a/plugins/common/properties.go +++ b/plugins/common/properties.go @@ -77,6 +77,26 @@ func PropertyGet(pluginName string, appName string, property string) string { return PropertyGetDefault(pluginName, appName, property, "") } +// PropertyGetAll returns a map of all properties for a given app +func PropertyGetAll(pluginName string, appName string) (map[string]string, error) { + properties := make(map[string]string) + pluginAppConfigRoot := getPluginAppPropertyPath(pluginName, appName) + files, err := ioutil.ReadDir(pluginAppConfigRoot) + if err != nil { + return properties, err + } + + for _, file := range files { + if file.IsDir() { + continue + } + property := file.Name() + properties[property] = PropertyGet(pluginName, appName, property) + } + + return properties, nil +} + // PropertyGetDefault returns the value for a given property with a specified default value func PropertyGetDefault(pluginName, appName, property, defaultValue string) (val string) { if !PropertyExists(pluginName, appName, property) { diff --git a/plugins/resource/Makefile b/plugins/resource/Makefile index d2b724c7b..71ef38f1f 100644 --- a/plugins/resource/Makefile +++ b/plugins/resource/Makefile @@ -2,8 +2,8 @@ include ../../common.mk GO_ARGS ?= -a -SUBCOMMANDS = subcommands/limit subcommands/limit-clear subcommands/reserve subcommands/reserve-clear -TRIGGERS = triggers/install triggers/post-delete +SUBCOMMANDS = subcommands/limit subcommands/limit-clear subcommands/report subcommands/reserve subcommands/reserve-clear +TRIGGERS = triggers/install triggers/post-delete triggers/report build-in-docker: clean docker run --rm \ -v $$PWD/../..:$(GO_REPO_ROOT) \ @@ -23,7 +23,7 @@ subcommands/%: src/subcommands/*/%.go go build $(GO_ARGS) -o $@ $< clean: - rm -rf commands subcommands triggers install post-delete + rm -rf commands subcommands triggers install post-delete report src-clean: rm -rf .gitignore src triggers vendor Makefile *.go diff --git a/plugins/resource/resource.go b/plugins/resource/resource.go index 1f188893a..0bf0e251a 100644 --- a/plugins/resource/resource.go +++ b/plugins/resource/resource.go @@ -1,5 +1,15 @@ package resource +import ( + "fmt" + "os" + "reflect" + "sort" + "strings" + + "github.com/dokku/dokku/plugins/common" +) + // Resource is a collection of resource constraints for apps type Resource struct { CPU string `json:"cpu"` @@ -9,3 +19,61 @@ type Resource struct { NetworkIngress string `json:"network-ingress"` NetworkEgress string `json:"network-egress"` } + +// ReportSingleApp is an internal function that displays the app report for one or more apps +func ReportSingleApp(appName, infoFlag string) { + if err := common.VerifyAppName(appName); err != nil { + common.LogFail(err.Error()) + } + + resources, err := common.PropertyGetAll("resource", appName) + if err != nil { + common.LogFail(err.Error()) + } + + flags := []string{} + infoFlags := map[string]string{} + for key, value := range resources { + flag := fmt.Sprintf("--resource-%v", key) + flags = append(flags, flag) + infoFlags[flag] = value + } + sort.Strings(flags) + + if len(infoFlag) == 0 { + common.LogInfo2Quiet(fmt.Sprintf("%s resource information", appName)) + for _, k := range flags { + v := infoFlags[k] + key := strings.Replace(strings.Replace(strings.TrimPrefix(k, "--resource-"), "-", " ", -1), ".", " ", -1) + common.LogVerbose(fmt.Sprintf("%s%s", Right(fmt.Sprintf("%s:", key), 31, " "), v)) + } + return + } + + for _, k := range flags { + v := infoFlags[k] + if infoFlag == k { + fmt.Fprintln(os.Stdout, v) + return + } + } + + keys := reflect.ValueOf(infoFlags).MapKeys() + strkeys := make([]string, len(keys)) + for i := 0; i < len(keys); i++ { + strkeys[i] = keys[i].String() + } + common.LogFail(fmt.Sprintf("Invalid flag passed, valid flags: %s", strings.Join(strkeys, ", "))) +} + +func times(str string, n int) (out string) { + for i := 0; i < n; i++ { + out += str + } + return +} + +// Right right-pads the string with pad up to len runes +func Right(str string, length int, pad string) string { + return str + times(pad, length-len(str)) +} diff --git a/plugins/resource/src/subcommands/report/report.go b/plugins/resource/src/subcommands/report/report.go new file mode 100644 index 000000000..f8385c3bd --- /dev/null +++ b/plugins/resource/src/subcommands/report/report.go @@ -0,0 +1,33 @@ +package main + +import ( + "flag" + "strings" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/resource" +) + +// displays a resource report for one or more apps +func main() { + flag.Parse() + appName := flag.Arg(1) + infoFlag := flag.Arg(2) + + if strings.HasPrefix(appName, "--") { + common.LogFail("The resource:report command does not support flags without an app name") + } + + if len(appName) == 0 { + apps, err := common.DokkuApps() + if err != nil { + return + } + for _, appName := range apps { + resource.ReportSingleApp(appName, infoFlag) + } + return + } + + resource.ReportSingleApp(appName, infoFlag) +} diff --git a/plugins/resource/src/triggers/report/report.go b/plugins/resource/src/triggers/report/report.go new file mode 100644 index 000000000..94a73d62f --- /dev/null +++ b/plugins/resource/src/triggers/report/report.go @@ -0,0 +1,15 @@ +package main + +import ( + "flag" + + "github.com/dokku/dokku/plugins/resource" +) + +// displays a buildpacks report for one or more apps +func main() { + flag.Parse() + appName := flag.Arg(0) + + resource.ReportSingleApp(appName, "") +}