From 54ef6514d51fa375c60a969b3d1452bfe6346ba1 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Tue, 26 Mar 2019 17:10:32 -0400 Subject: [PATCH 001/163] fix: turn off error logging for validate config This will allow validation to work if the default logging locations cannot be written to but the configuration is otherwise okay. This will be the case for users with custom nginx configurations that log to other places, such as syslog. --- plugins/nginx-vhosts/templates/validate.conf.sigil | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/plugins/nginx-vhosts/templates/validate.conf.sigil b/plugins/nginx-vhosts/templates/validate.conf.sigil index 232fd6d51..cb6a556c5 100644 --- a/plugins/nginx-vhosts/templates/validate.conf.sigil +++ b/plugins/nginx-vhosts/templates/validate.conf.sigil @@ -1,2 +1,6 @@ events { worker_connections 768; } -http { include {{ $.NGINX_CONF }}; } +http { + access_log off; + error_log /dev/null; + include {{ $.NGINX_CONF }}; +} From 2d1f241677eb0d076ac27d000b879e6a3dfda81f Mon Sep 17 00:00:00 2001 From: David Date: Fri, 5 Apr 2019 23:46:36 +0200 Subject: [PATCH 002/163] rails-database -> railsdatabase Naming the database rails-database throws ```Please specify a valid name for the service. Valid characters are: [A-Za-z0-9_]+``` --- docs/deployment/application-deployment.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/deployment/application-deployment.md b/docs/deployment/application-deployment.md index ade89e194..15f3bfe54 100644 --- a/docs/deployment/application-deployment.md +++ b/docs/deployment/application-deployment.md @@ -32,8 +32,8 @@ When you create a new app, Dokku by default *does not* provide any datastores su # plugin installation requires root, hence the user change sudo dokku plugin:install https://github.com/dokku/dokku-postgres.git -# create a postgres service with the name rails-database -dokku postgres:create rails-database +# create a postgres service with the name railsdatabase +dokku postgres:create railsdatabase ``` > Each service may take a few moments to create. @@ -45,7 +45,7 @@ Once the service creation is complete, set the `DATABASE_URL` environment variab ```shell # on the Dokku host # each official datastore offers a `link` method to link a service to any application -dokku postgres:link rails-database ruby-rails-sample +dokku postgres:link railsdatabase ruby-rails-sample ``` > You can link a single service to multiple applications or use one service per application. From 9486576addea05281279b21216f188d3ebeaeb00 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 16:56:38 -0400 Subject: [PATCH 003/163] fix: drop nil err reference from log output --- plugins/common/common.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/common/common.go b/plugins/common/common.go index 2348b534f..99dd24d65 100644 --- a/plugins/common/common.go +++ b/plugins/common/common.go @@ -328,7 +328,7 @@ func VerifyAppName(appName string) (err error) { dokkuRoot := MustGetEnv("DOKKU_ROOT") appRoot := strings.Join([]string{dokkuRoot, appName}, "/") if !DirectoryExists(appRoot) { - return fmt.Errorf("app %s does not exist: %v", appName, err) + return fmt.Errorf("app %s does not exist", appName) } r, _ := regexp.Compile("^[a-z0-9].*") if !r.MatchString(appName) { From 8ad74d081494a25fc4f9f5e8fa76ef35f66bd59f Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 16:56:51 -0400 Subject: [PATCH 004/163] fix: correct buildpack plugin version --- plugins/buildpacks/plugin.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/buildpacks/plugin.toml b/plugins/buildpacks/plugin.toml index 0b6f00dab..8804437b6 100644 --- a/plugins/buildpacks/plugin.toml +++ b/plugins/buildpacks/plugin.toml @@ -1,4 +1,4 @@ [plugin] description = "dokku core buildpacks plugin" -version = "0.14.2" +version = "0.14.6" [plugin.config] From 5b2d4b3559ff0a6be953c8455a20b1f4f29f907d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 16:57:02 -0400 Subject: [PATCH 005/163] feat: add initial documentation for resource management plugin --- docs/advanced-usage/resource-management.md | 256 +++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 docs/advanced-usage/resource-management.md diff --git a/docs/advanced-usage/resource-management.md b/docs/advanced-usage/resource-management.md new file mode 100644 index 000000000..64f4e3486 --- /dev/null +++ b/docs/advanced-usage/resource-management.md @@ -0,0 +1,256 @@ +# Resource Management + +> New as of 0.15.0 + +``` +dokku resource:limit --process-types [RESOURCE_OPTS...] +dokku resource:reserve --process-types [RESOURCE_OPTS...] +dokku resource:limit-defaults [RESOURCE_OPTS...] +dokku resource:reserve-defaults [RESOURCE_OPTS...] +dokku resource:limit-clear [] +dokku resource:reserve-clear [] +dokku resource:report [] +``` + +The resource plugin is meant to allow users to limit or reserve resources for a given app/process-type combination. + +## Usage + +By default, Dokku allows unlimited resource access to apps deployed on a server. In some cases, it may be desirable to limit this on a per-app or per-process-type basis. The `resource` plugin allows management of both resource "limits" and resource "reservations", where each resource request type has specific meaning to the scheduler in use for a given app. + +> The meaning of a values and it's units are specific to the scheduler in use for a given app. If a scheduler does not support a given resource type combination, it will be ignored. All resource commands require an app rebuild or deploy in order to take effect. + +Valid resource options include: + +- `--cpu` +- `--memory` +- `--memory-swap` +- `--network` +- `--network-ingress` +- `--network-egress` + +### Resource Limits + +When specified and supported, a resource limit will ensure that your app does not go _over_ the specified value. If this occurs, the underlying scheduler may either cap resource utilization, or it may decide to terminate and reschedule your process. + +Resource limits may be set via the `resource:limit` command: + +```shell +dokku resource:limit --memory 100 node-js-app +``` + +``` +=====> Setting resource limits for node-js-app + memory: 100 +``` + +Multiple resources can be limited in a single call: + +```shell +dokku resource:limit --cpu 100 --memory 100 node-js-app +``` + +``` +=====> Setting resource limits for node-js-app + cpu: 100 + memory: 100 +``` + +Resources can also be limited on a per-process type basis. If specified, this will override any generic limits set for the app. + +```shell +dokku resource:limit --cpu 100 --memory 100 --process-types worker node-js-app +``` + +``` +=====> Setting resource limits for node-js-app (worker) + cpu: 100 + memory: 100 +``` + +#### Default Resource Limits + +By default, Dokku does not limit any resources, though these can be specified at the server level via the `resource:limit-defaults` command. As with the `resource:limit` command, any and all resource request types can be specified. + +```shell +dokku resource:limit-defaults --memory 100 +``` + +``` +=====> Setting default resource limits + memory: 100 +``` + +### Clearing Resource Limits + +In cases where the values are incorrect - or there is no desire to limit resources - resource limits may be cleared using the `resource:limit-clear` command. + +```shell +dokku resource:limit-clear node-js-app +``` + +``` +-----> Clearing resource limit for node-js-app +``` + +Defaults can also be cleared by leaving the app unspecified. + +```shell +dokku resource:limit-clear +``` + +``` +-----> Clearing default resource limits +``` + +### Resource Reservations + +When specified and supported, a resource reservation will ensure that your server has _at least_ the specified resources before placing a given app's process. If there a resource exhaustion, future rebuilds and deploys may fail. + +Resource reservations may be set via the `resource:reserve` command: + +```shell +dokku resource:reserve --memory 100 node-js-app +``` + +``` +=====> Setting resource reservation for node-js-app + memory: 100 +``` + +Multiple resources can be limited in a single call: + +```shell +dokku resource:reserve --cpu 100 --memory 100 node-js-app +``` + +``` +=====> Setting resource reservation for node-js-app + cpu: 100 + memory: 100 +``` + +Resources can also be limited on a per-process type basis. If specified, this will override any generic limits set for the app. + +```shell +dokku resource:reserve --cpu 100 --memory 100 --process-types worker node-js-app +``` + +``` +=====> Setting resource reservation for node-js-app (worker) + cpu: 100 + memory: 100 +``` + +#### Default Resource Reservations + +By default, Dokku does not reserve any resources, though these can be specified at the server level via the `resource:reserve-defaults` command. As with the `resource:reserve` command, any and all resource request types can be specified. + +```shell +dokku resource:reserve-defaults --memory 100 +``` + +``` +=====> Setting default resource reservation + memory: 100 +``` + +#### Clearing Resource Reservations + +In cases where the values are incorrect - or there is no desire to reserve resources - resource reservations may be cleared using the `resource:reserve-clear` command. + +```shell +dokku resource:reserve-clear node-js-app +``` + +``` +-----> Clearing resource reservation for node-js-app +``` + +Defaults can also be cleared by leaving the app unspecified. + +```shell +dokku resource:reserve-clear +``` + +``` +-----> Clearing default resource reservation +``` + +### Displaying resource reports for an app + +You can get a report about the app's resource status using the `resource:report` command: + +```shell +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: +=====> 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: +=====> 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: +``` + +You can run the command for a specific app also. + +```shell +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: +``` + +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 +``` From 2e895286cf1f37fd3b8f38325d0b215986d4f308 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 17:37:41 -0400 Subject: [PATCH 006/163] docs: drop ability to process multiple process types at once --- docs/advanced-usage/resource-management.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/advanced-usage/resource-management.md b/docs/advanced-usage/resource-management.md index 64f4e3486..91a9bc9c4 100644 --- a/docs/advanced-usage/resource-management.md +++ b/docs/advanced-usage/resource-management.md @@ -3,8 +3,8 @@ > New as of 0.15.0 ``` -dokku resource:limit --process-types [RESOURCE_OPTS...] -dokku resource:reserve --process-types [RESOURCE_OPTS...] +dokku resource:limit --process-type [RESOURCE_OPTS...] +dokku resource:reserve --process-type [RESOURCE_OPTS...] dokku resource:limit-defaults [RESOURCE_OPTS...] dokku resource:reserve-defaults [RESOURCE_OPTS...] dokku resource:limit-clear [] @@ -59,7 +59,7 @@ dokku resource:limit --cpu 100 --memory 100 node-js-app Resources can also be limited on a per-process type basis. If specified, this will override any generic limits set for the app. ```shell -dokku resource:limit --cpu 100 --memory 100 --process-types worker node-js-app +dokku resource:limit --cpu 100 --memory 100 --process-type worker node-js-app ``` ``` @@ -133,7 +133,7 @@ dokku resource:reserve --cpu 100 --memory 100 node-js-app Resources can also be limited on a per-process type basis. If specified, this will override any generic limits set for the app. ```shell -dokku resource:reserve --cpu 100 --memory 100 --process-types worker node-js-app +dokku resource:reserve --cpu 100 --memory 100 --process-type worker node-js-app ``` ``` From ab88dbb03da3a7028838bd0839c2e08e453858ff Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 17:52:09 -0400 Subject: [PATCH 007/163] feat: Implement resource:limit and resource:reserve Also add the install and post-delete hooks --- plugins/resource/.gitignore | 6 + plugins/resource/Makefile | 37 +++ plugins/resource/glide.yaml | 3 + plugins/resource/plugin.toml | 4 + plugins/resource/resource.go | 12 + plugins/resource/src/commands/commands.go | 63 ++++ plugins/resource/src/glide.lock | 6 + plugins/resource/src/glide.yaml | 3 + .../resource/src/subcommands/limit/limit.go | 35 ++ .../src/subcommands/reserve/reserve.go | 35 ++ .../resource/src/triggers/install/install.go | 14 + .../src/triggers/post-delete/post-delete.go | 18 ++ .../github.com/ryanuber/columnize/.travis.yml | 3 + .../github.com/ryanuber/columnize/LICENSE | 20 ++ .../github.com/ryanuber/columnize/README.md | 69 ++++ .../ryanuber/columnize/columnize.go | 178 ++++++++++ .../ryanuber/columnize/columnize_test.go | 306 ++++++++++++++++++ plugins/resource/subcommands.go | 83 +++++ .../github.com/ryanuber/columnize/.travis.yml | 3 + .../github.com/ryanuber/columnize/LICENSE | 20 ++ .../github.com/ryanuber/columnize/README.md | 69 ++++ .../ryanuber/columnize/columnize.go | 178 ++++++++++ .../ryanuber/columnize/columnize_test.go | 306 ++++++++++++++++++ 23 files changed, 1471 insertions(+) create mode 100644 plugins/resource/.gitignore create mode 100644 plugins/resource/Makefile create mode 100644 plugins/resource/glide.yaml create mode 100644 plugins/resource/plugin.toml create mode 100644 plugins/resource/resource.go create mode 100644 plugins/resource/src/commands/commands.go create mode 100644 plugins/resource/src/glide.lock create mode 100644 plugins/resource/src/glide.yaml create mode 100644 plugins/resource/src/subcommands/limit/limit.go create mode 100644 plugins/resource/src/subcommands/reserve/reserve.go create mode 100644 plugins/resource/src/triggers/install/install.go create mode 100644 plugins/resource/src/triggers/post-delete/post-delete.go create mode 100644 plugins/resource/src/vendor/github.com/ryanuber/columnize/.travis.yml create mode 100644 plugins/resource/src/vendor/github.com/ryanuber/columnize/LICENSE create mode 100644 plugins/resource/src/vendor/github.com/ryanuber/columnize/README.md create mode 100644 plugins/resource/src/vendor/github.com/ryanuber/columnize/columnize.go create mode 100644 plugins/resource/src/vendor/github.com/ryanuber/columnize/columnize_test.go create mode 100644 plugins/resource/subcommands.go create mode 100644 plugins/resource/vendor/github.com/ryanuber/columnize/.travis.yml create mode 100644 plugins/resource/vendor/github.com/ryanuber/columnize/LICENSE create mode 100644 plugins/resource/vendor/github.com/ryanuber/columnize/README.md create mode 100644 plugins/resource/vendor/github.com/ryanuber/columnize/columnize.go create mode 100644 plugins/resource/vendor/github.com/ryanuber/columnize/columnize_test.go diff --git a/plugins/resource/.gitignore b/plugins/resource/.gitignore new file mode 100644 index 000000000..56aebe3b7 --- /dev/null +++ b/plugins/resource/.gitignore @@ -0,0 +1,6 @@ +/commands +/subcommands/* +/triggers/* +/install +/post-delete +/report diff --git a/plugins/resource/Makefile b/plugins/resource/Makefile new file mode 100644 index 000000000..fde0627dd --- /dev/null +++ b/plugins/resource/Makefile @@ -0,0 +1,37 @@ +include ../../common.mk + +GO_ARGS ?= -a + +SUBCOMMANDS = subcommands/limit subcommands/reserve +TRIGGERS = triggers/install triggers/post-delete +build-in-docker: clean + docker run --rm \ + -v $$PWD/../..:$(GO_REPO_ROOT) \ + -w $(GO_REPO_ROOT)/plugins/resource \ + $(BUILD_IMAGE) \ + bash -c "GO_ARGS='$(GO_ARGS)' make -j4 build" || exit $$? + +build: commands subcommands triggers + $(MAKE) triggers-copy + +commands: **/**/commands.go + go build $(GO_ARGS) -o commands src/commands/commands.go + +subcommands: $(SUBCOMMANDS) + +subcommands/%: src/subcommands/*/%.go + go build $(GO_ARGS) -o $@ $< + +clean: + rm -rf commands subcommands triggers install post-delete + +src-clean: + rm -rf .gitignore src triggers vendor Makefile *.go + +triggers: $(TRIGGERS) + +triggers/%: src/triggers/*/%.go + go build $(GO_ARGS) -o $@ $< + +triggers-copy: + cp triggers/* . diff --git a/plugins/resource/glide.yaml b/plugins/resource/glide.yaml new file mode 100644 index 000000000..3f7a96657 --- /dev/null +++ b/plugins/resource/glide.yaml @@ -0,0 +1,3 @@ +package: . +import: +- package: github.com/ryanuber/columnize diff --git a/plugins/resource/plugin.toml b/plugins/resource/plugin.toml new file mode 100644 index 000000000..3e302605c --- /dev/null +++ b/plugins/resource/plugin.toml @@ -0,0 +1,4 @@ +[plugin] +description = "dokku core resource plugin" +version = "0.14.6" +[plugin.config] diff --git a/plugins/resource/resource.go b/plugins/resource/resource.go new file mode 100644 index 000000000..f782c52a2 --- /dev/null +++ b/plugins/resource/resource.go @@ -0,0 +1,12 @@ +package resource + +type Resource struct { + Cpu string `json:"cpu"` + Memory string `json:"memory"` + MemorySwap string `json:"memory-swap"` + Network string `json:"network"` + NetworkIngress string `json:"network-ingress"` + NetworkEgress string `json:"network-egress"` +} + +const PluginName = "resource" diff --git a/plugins/resource/src/commands/commands.go b/plugins/resource/src/commands/commands.go new file mode 100644 index 000000000..3a13ddf6e --- /dev/null +++ b/plugins/resource/src/commands/commands.go @@ -0,0 +1,63 @@ +package main + +import ( + "flag" + "fmt" + "os" + "strconv" + "strings" + + "github.com/dokku/dokku/plugins/common" + columnize "github.com/ryanuber/columnize" +) + +const ( + helpHeader = `Usage: dokku resource[:COMMAND] + +Manages resource settings for an app + +Additional commands:` + + helpContent = ` + resource:limit [--process-type process-type] [RESOURCE_OPTS...] , Limit resources for a given app/process-type combination + resource:reserve [--process-type process-type] [RESOURCE_OPTS...] , Reserve resources for a given app/process-type combination +` +) + +func main() { + flag.Usage = usage + flag.Parse() + + cmd := flag.Arg(0) + switch cmd { + case "resource", "resource:help": + usage() + case "help": + command := common.NewShellCmd(fmt.Sprintf("ps -o command= %d", os.Getppid())) + command.ShowOutput = false + output, err := command.Output() + + if err == nil && strings.Contains(string(output), "--all") { + fmt.Println(helpContent) + } else { + fmt.Print("\n resource, Manages resource settings for an app\n") + } + default: + dokkuNotImplementExitCode, err := strconv.Atoi(os.Getenv("DOKKU_NOT_IMPLEMENTED_EXIT")) + if err != nil { + fmt.Println("failed to retrieve DOKKU_NOT_IMPLEMENTED_EXIT environment variable") + dokkuNotImplementExitCode = 10 + } + os.Exit(dokkuNotImplementExitCode) + } +} + +func usage() { + config := columnize.DefaultConfig() + config.Delim = "," + config.Prefix = " " + config.Empty = "" + content := strings.Split(helpContent, "\n")[1:] + fmt.Println(helpHeader) + fmt.Println(columnize.Format(content, config)) +} diff --git a/plugins/resource/src/glide.lock b/plugins/resource/src/glide.lock new file mode 100644 index 000000000..fe4a98f07 --- /dev/null +++ b/plugins/resource/src/glide.lock @@ -0,0 +1,6 @@ +hash: 1ddab5de41d1514c2722bd7e24758ad4b60bf6956eb5b9b925fa071a1427f149 +updated: 2017-01-03T17:16:50.97156327-08:00 +imports: +- name: github.com/ryanuber/columnize + version: 0fbbb3f0e3fbdc5bae7c6cd5f6c1887ebfb76360 +testImports: [] diff --git a/plugins/resource/src/glide.yaml b/plugins/resource/src/glide.yaml new file mode 100644 index 000000000..3f7a96657 --- /dev/null +++ b/plugins/resource/src/glide.yaml @@ -0,0 +1,3 @@ +package: . +import: +- package: github.com/ryanuber/columnize diff --git a/plugins/resource/src/subcommands/limit/limit.go b/plugins/resource/src/subcommands/limit/limit.go new file mode 100644 index 000000000..f8256d709 --- /dev/null +++ b/plugins/resource/src/subcommands/limit/limit.go @@ -0,0 +1,35 @@ +package main + +import ( + "flag" + "os" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/resource" +) + +func main() { + args := flag.NewFlagSet("resource:limit", flag.ExitOnError) + processeType := args.String("process-type", "", "process-type: A process type to manage") + cpu := args.String("cpu", "", "cpu: The amount of cpu to limit processes by") + memory := args.String("memory", "", "memory: The amount of memory to limit processes by") + memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to limit processes by") + network := args.String("network", "", "network: The amount of network bandwidth to limit processes by") + networkIngress := args.String("network-ingress", "", "network-ingress: The amount of ingress network bandwidth to limit processes by") + networkEgress := args.String("network-egress", "", "network-egress: The amount of egress network bandwidth to limit processes by") + args.Parse(os.Args[2:]) + + resources := resource.Resource{ + Cpu: *cpu, + Memory: *memory, + MemorySwap: *memorySwap, + Network: *network, + NetworkIngress: *networkIngress, + NetworkEgress: *networkEgress, + } + + err := resource.CommandLimit(args.Args(), *processeType, resources) + if err != nil { + common.LogFail(err.Error()) + } +} diff --git a/plugins/resource/src/subcommands/reserve/reserve.go b/plugins/resource/src/subcommands/reserve/reserve.go new file mode 100644 index 000000000..f76167b39 --- /dev/null +++ b/plugins/resource/src/subcommands/reserve/reserve.go @@ -0,0 +1,35 @@ +package main + +import ( + "flag" + "os" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/resource" +) + +func main() { + args := flag.NewFlagSet("resource:reserve", flag.ExitOnError) + processeType := args.String("process-type", "", "process-type: A process type to manage") + cpu := args.String("cpu", "", "cpu: The amount of cpu to reserve for processes") + memory := args.String("memory", "", "memory: The amount of memory to reserve for processes") + memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to reserve for processes") + network := args.String("network", "", "network: The amount of network bandwidth to reserve for processes") + networkIngress := args.String("network-ingress", "", "network-ingress: The amount of ingress network bandwidth to reserve for processes") + networkEgress := args.String("network-egress", "", "network-egress: The amount of egress network bandwidth to reserve for processes") + args.Parse(os.Args[2:]) + + resources := resource.Resource{ + Cpu: *cpu, + Memory: *memory, + MemorySwap: *memorySwap, + Network: *network, + NetworkIngress: *networkIngress, + NetworkEgress: *networkEgress, + } + + err := resource.CommandReserve(args.Args(), *processeType, resources) + if err != nil { + common.LogFail(err.Error()) + } +} diff --git a/plugins/resource/src/triggers/install/install.go b/plugins/resource/src/triggers/install/install.go new file mode 100644 index 000000000..77f7d14ed --- /dev/null +++ b/plugins/resource/src/triggers/install/install.go @@ -0,0 +1,14 @@ +package main + +import ( + "fmt" + + "github.com/dokku/dokku/plugins/common" +) + +// runs the install step for the resource plugin +func main() { + if err := common.PropertySetup("resource"); err != nil { + common.LogFail(fmt.Sprintf("Unable to install the resource plugin: %s", err.Error())) + } +} diff --git a/plugins/resource/src/triggers/post-delete/post-delete.go b/plugins/resource/src/triggers/post-delete/post-delete.go new file mode 100644 index 000000000..d22ceccea --- /dev/null +++ b/plugins/resource/src/triggers/post-delete/post-delete.go @@ -0,0 +1,18 @@ +package main + +import ( + "flag" + + "github.com/dokku/dokku/plugins/common" +) + +// destroys the resource property for a given app container +func main() { + flag.Parse() + appName := flag.Arg(0) + + err := common.PropertyDestroy("resource", appName) + if err != nil { + common.LogFail(err.Error()) + } +} diff --git a/plugins/resource/src/vendor/github.com/ryanuber/columnize/.travis.yml b/plugins/resource/src/vendor/github.com/ryanuber/columnize/.travis.yml new file mode 100644 index 000000000..1a0bbea6c --- /dev/null +++ b/plugins/resource/src/vendor/github.com/ryanuber/columnize/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: + - tip diff --git a/plugins/resource/src/vendor/github.com/ryanuber/columnize/LICENSE b/plugins/resource/src/vendor/github.com/ryanuber/columnize/LICENSE new file mode 100644 index 000000000..b9c0e2b68 --- /dev/null +++ b/plugins/resource/src/vendor/github.com/ryanuber/columnize/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016 Ryan Uber + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/resource/src/vendor/github.com/ryanuber/columnize/README.md b/plugins/resource/src/vendor/github.com/ryanuber/columnize/README.md new file mode 100644 index 000000000..e47634fc6 --- /dev/null +++ b/plugins/resource/src/vendor/github.com/ryanuber/columnize/README.md @@ -0,0 +1,69 @@ +Columnize +========= + +Easy column-formatted output for golang + +[![Build Status](https://travis-ci.org/ryanuber/columnize.svg)](https://travis-ci.org/ryanuber/columnize) +[![GoDoc](https://godoc.org/github.com/ryanuber/columnize?status.svg)](https://godoc.org/github.com/ryanuber/columnize) + +Columnize is a really small Go package that makes building CLI's a little bit +easier. In some CLI designs, you want to output a number similar items in a +human-readable way with nicely aligned columns. However, figuring out how wide +to make each column is a boring problem to solve and eats your valuable time. + +Here is an example: + +```go +package main + +import ( + "fmt" + "github.com/ryanuber/columnize" +) + +func main() { + output := []string{ + "Name | Gender | Age", + "Bob | Male | 38", + "Sally | Female | 26", + } + result := columnize.SimpleFormat(output) + fmt.Println(result) +} +``` + +As you can see, you just pass in a list of strings. And the result: + +``` +Name Gender Age +Bob Male 38 +Sally Female 26 +``` + +Columnize is tolerant of missing or empty fields, or even empty lines, so +passing in extra lines for spacing should show up as you would expect. + +Configuration +============= + +Columnize is configured using a `Config`, which can be obtained by calling the +`DefaultConfig()` method. You can then tweak the settings in the resulting +`Config`: + +``` +config := columnize.DefaultConfig() +config.Delim = "|" +config.Glue = " " +config.Prefix = "" +config.Empty = "" +``` + +* `Delim` is the string by which columns of **input** are delimited +* `Glue` is the string by which columns of **output** are delimited +* `Prefix` is a string by which each line of **output** is prefixed +* `Empty` is a string used to replace blank values found in output + +You can then pass the `Config` in using the `Format` method (signature below) to +have text formatted to your liking. + +See the [godoc](https://godoc.org/github.com/ryanuber/columnize) page for usage. diff --git a/plugins/resource/src/vendor/github.com/ryanuber/columnize/columnize.go b/plugins/resource/src/vendor/github.com/ryanuber/columnize/columnize.go new file mode 100644 index 000000000..915716a10 --- /dev/null +++ b/plugins/resource/src/vendor/github.com/ryanuber/columnize/columnize.go @@ -0,0 +1,178 @@ +package columnize + +import ( + "bytes" + "fmt" + "strings" +) + +// Config can be used to tune certain parameters which affect the way +// in which Columnize will format output text. +type Config struct { + // The string by which the lines of input will be split. + Delim string + + // The string by which columns of output will be separated. + Glue string + + // The string by which columns of output will be prefixed. + Prefix string + + // A replacement string to replace empty fields + Empty string +} + +// DefaultConfig returns a *Config with default values. +func DefaultConfig() *Config { + return &Config{ + Delim: "|", + Glue: " ", + Prefix: "", + Empty: "", + } +} + +// MergeConfig merges two config objects together and returns the resulting +// configuration. Values from the right take precedence over the left side. +func MergeConfig(a, b *Config) *Config { + var result Config = *a + + // Return quickly if either side was nil + if a == nil || b == nil { + return &result + } + + if b.Delim != "" { + result.Delim = b.Delim + } + if b.Glue != "" { + result.Glue = b.Glue + } + if b.Prefix != "" { + result.Prefix = b.Prefix + } + if b.Empty != "" { + result.Empty = b.Empty + } + + return &result +} + +// stringFormat, given a set of column widths and the number of columns in +// the current line, returns a sprintf-style format string which can be used +// to print output aligned properly with other lines using the same widths set. +func stringFormat(c *Config, widths []int, columns int) string { + // Create the buffer with an estimate of the length + buf := bytes.NewBuffer(make([]byte, 0, (6+len(c.Glue))*columns)) + + // Start with the prefix, if any was given. The buffer will not return an + // error so it does not need to be handled + buf.WriteString(c.Prefix) + + // Create the format string from the discovered widths + for i := 0; i < columns && i < len(widths); i++ { + if i == columns-1 { + buf.WriteString("%s\n") + } else { + fmt.Fprintf(buf, "%%-%ds%s", widths[i], c.Glue) + } + } + return buf.String() +} + +// elementsFromLine returns a list of elements, each representing a single +// item which will belong to a column of output. +func elementsFromLine(config *Config, line string) []interface{} { + seperated := strings.Split(line, config.Delim) + elements := make([]interface{}, len(seperated)) + for i, field := range seperated { + value := strings.TrimSpace(field) + + // Apply the empty value, if configured. + if value == "" && config.Empty != "" { + value = config.Empty + } + elements[i] = value + } + return elements +} + +// runeLen calculates the number of visible "characters" in a string +func runeLen(s string) int { + l := 0 + for _ = range s { + l++ + } + return l +} + +// widthsFromLines examines a list of strings and determines how wide each +// column should be considering all of the elements that need to be printed +// within it. +func widthsFromLines(config *Config, lines []string) []int { + widths := make([]int, 0, 8) + + for _, line := range lines { + elems := elementsFromLine(config, line) + for i := 0; i < len(elems); i++ { + l := runeLen(elems[i].(string)) + if len(widths) <= i { + widths = append(widths, l) + } else if widths[i] < l { + widths[i] = l + } + } + } + return widths +} + +// Format is the public-facing interface that takes a list of strings and +// returns nicely aligned column-formatted text. +func Format(lines []string, config *Config) string { + conf := MergeConfig(DefaultConfig(), config) + widths := widthsFromLines(conf, lines) + + // Estimate the buffer size + glueSize := len(conf.Glue) + var size int + for _, w := range widths { + size += w + glueSize + } + size *= len(lines) + + // Create the buffer + buf := bytes.NewBuffer(make([]byte, 0, size)) + + // Create a cache for the string formats + fmtCache := make(map[int]string, 16) + + // Create the formatted output using the format string + for _, line := range lines { + elems := elementsFromLine(conf, line) + + // Get the string format using cache + numElems := len(elems) + stringfmt, ok := fmtCache[numElems] + if !ok { + stringfmt = stringFormat(conf, widths, numElems) + fmtCache[numElems] = stringfmt + } + + fmt.Fprintf(buf, stringfmt, elems...) + } + + // Get the string result + result := buf.String() + + // Remove trailing newline without removing leading/trailing space + if n := len(result); n > 0 && result[n-1] == '\n' { + result = result[:n-1] + } + + return result +} + +// SimpleFormat is a convenience function to format text with the defaults. +func SimpleFormat(lines []string) string { + return Format(lines, nil) +} diff --git a/plugins/resource/src/vendor/github.com/ryanuber/columnize/columnize_test.go b/plugins/resource/src/vendor/github.com/ryanuber/columnize/columnize_test.go new file mode 100644 index 000000000..89dabaa38 --- /dev/null +++ b/plugins/resource/src/vendor/github.com/ryanuber/columnize/columnize_test.go @@ -0,0 +1,306 @@ +package columnize + +import ( + "fmt" + "testing" + + crand "crypto/rand" +) + +func TestListOfStringsInput(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | y | z", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestEmptyLinesOutput(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "", + "x | y | z", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestLeadingSpacePreserved(t *testing.T) { + input := []string{ + "| Column B | Column C", + "x | y | z", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := " Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestColumnWidthCalculator(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "Longer than A | Longer than B | Longer than C", + "short | short | short", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "Longer than A Longer than B Longer than C\n" + expected += "short short short" + + if output != expected { + printableProof := fmt.Sprintf("\nGot: %+q", output) + printableProof += fmt.Sprintf("\nExpected: %+q", expected) + t.Fatalf("\n%s", printableProof) + } +} + +func TestColumnWidthCalculatorNonASCII(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "⌘⌘⌘⌘⌘⌘⌘⌘ | Longer than B | Longer than C", + "short | short | short", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "⌘⌘⌘⌘⌘⌘⌘⌘ Longer than B Longer than C\n" + expected += "short short short" + + if output != expected { + printableProof := fmt.Sprintf("\nGot: %+q", output) + printableProof += fmt.Sprintf("\nExpected: %+q", expected) + t.Fatalf("\n%s", printableProof) + } +} + +func BenchmarkColumnWidthCalculator(b *testing.B) { + // Generate the input + input := []string{ + "UUID A | UUID B | UUID C | Column D | Column E", + } + + format := "%s|%s|%s|%s" + short := "short" + + uuid := func() string { + buf := make([]byte, 16) + if _, err := crand.Read(buf); err != nil { + panic(fmt.Errorf("failed to read random bytes: %v", err)) + } + + return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", + buf[0:4], + buf[4:6], + buf[6:8], + buf[8:10], + buf[10:16]) + } + + for i := 0; i < 1000; i++ { + l := fmt.Sprintf(format, uuid()[:8], uuid()[:12], uuid(), short, short) + input = append(input, l) + } + + config := DefaultConfig() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + Format(input, config) + } +} + +func TestVariedInputSpacing(t *testing.T) { + input := []string{ + "Column A |Column B| Column C", + "x|y| z", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestUnmatchedColumnCounts(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "Value A | Value B", + "Value A | Value B | Value C | Value D", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "Value A Value B\n" + expected += "Value A Value B Value C Value D" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestAlternateDelimiter(t *testing.T) { + input := []string{ + "Column | A % Column | B % Column | C", + "Value A % Value B % Value C", + } + + config := DefaultConfig() + config.Delim = "%" + output := Format(input, config) + + expected := "Column | A Column | B Column | C\n" + expected += "Value A Value B Value C" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestAlternateSpacingString(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | y | z", + } + + config := DefaultConfig() + config.Glue = " " + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestSimpleFormat(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | y | z", + } + + output := SimpleFormat(input) + + expected := "Column A Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestAlternatePrefixString(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | y | z", + } + + config := DefaultConfig() + config.Prefix = " " + output := Format(input, config) + + expected := " Column A Column B Column C\n" + expected += " x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestEmptyFieldReplacement(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | | z", + } + + config := DefaultConfig() + config.Empty = "" + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "x z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestEmptyConfigValues(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | y | z", + } + + config := Config{} + output := Format(input, &config) + + expected := "Column A Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestMergeConfig(t *testing.T) { + conf1 := &Config{Delim: "a", Glue: "a", Prefix: "a", Empty: "a"} + conf2 := &Config{Delim: "b", Glue: "b", Prefix: "b", Empty: "b"} + conf3 := &Config{Delim: "c", Prefix: "c"} + + m := MergeConfig(conf1, conf2) + if m.Delim != "b" || m.Glue != "b" || m.Prefix != "b" || m.Empty != "b" { + t.Fatalf("bad: %#v", m) + } + + m = MergeConfig(conf1, conf3) + if m.Delim != "c" || m.Glue != "a" || m.Prefix != "c" || m.Empty != "a" { + t.Fatalf("bad: %#v", m) + } + + m = MergeConfig(conf1, nil) + if m.Delim != "a" || m.Glue != "a" || m.Prefix != "a" || m.Empty != "a" { + t.Fatalf("bad: %#v", m) + } + + m = MergeConfig(conf1, &Config{}) + if m.Delim != "a" || m.Glue != "a" || m.Prefix != "a" || m.Empty != "a" { + t.Fatalf("bad: %#v", m) + } +} diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go new file mode 100644 index 000000000..8c5612b96 --- /dev/null +++ b/plugins/resource/subcommands.go @@ -0,0 +1,83 @@ +package resource + +import ( + "errors" + "fmt" + "github.com/dokku/dokku/plugins/common" +) + +// CommandLimit implements resource:limit +func CommandLimit(args []string, processType string, r Resource) (err error) { + return setRequestType(args, processType, r, "limit") +} + +// CommandReserve implements resource:reserve +func CommandReserve(args []string, processType string, r Resource) (err error) { + return setRequestType(args, processType, r, "reserve") +} + +func setRequestType(args []string, processType string, r Resource, requestType string) (err error) { + var appName string + appName, err = getAppName(args) + if err != nil { + return + } + + if err = common.VerifyAppName(appName); err != nil { + common.LogFail(err.Error()) + } + + if len(processType) == 0 { + processType = "_all_" + } + + resources := map[string]string{ + "cpu": r.Cpu, + "memory": r.Memory, + "memory-swap": r.MemorySwap, + "network": r.Network, + "network-ingress": r.NetworkIngress, + "network-egress": r.NetworkEgress, + } + + hasValues := false + for _, value := range resources { + if value != "" { + hasValues = true + } + } + + if !hasValues { + return errors.New("Please specify a resource to modify") + } + + if requestType == "limit" { + common.LogInfo2Quiet(fmt.Sprintf("Setting resource limits for %v", appName)) + } else if requestType == "reserve" { + common.LogInfo2Quiet(fmt.Sprintf("Setting resource reservation for %v", appName)) + } + + for key, value := range resources { + if value != "" { + common.LogVerbose(fmt.Sprintf("%v: %v", key, value)) + } + + property := fmt.Sprintf("%v.%v.%v", processType, requestType, key) + err = common.PropertyWrite(PluginName, appName, property, value) + if err != nil { + return + } + } + + return +} + +func getAppName(args []string) (appName string, err error) { + if len(args) >= 1 { + appName = args[0] + } else { + err = errors.New("Please specify an app to run the command on") + } + + return +} diff --git a/plugins/resource/vendor/github.com/ryanuber/columnize/.travis.yml b/plugins/resource/vendor/github.com/ryanuber/columnize/.travis.yml new file mode 100644 index 000000000..1a0bbea6c --- /dev/null +++ b/plugins/resource/vendor/github.com/ryanuber/columnize/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: + - tip diff --git a/plugins/resource/vendor/github.com/ryanuber/columnize/LICENSE b/plugins/resource/vendor/github.com/ryanuber/columnize/LICENSE new file mode 100644 index 000000000..b9c0e2b68 --- /dev/null +++ b/plugins/resource/vendor/github.com/ryanuber/columnize/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2016 Ryan Uber + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/plugins/resource/vendor/github.com/ryanuber/columnize/README.md b/plugins/resource/vendor/github.com/ryanuber/columnize/README.md new file mode 100644 index 000000000..e47634fc6 --- /dev/null +++ b/plugins/resource/vendor/github.com/ryanuber/columnize/README.md @@ -0,0 +1,69 @@ +Columnize +========= + +Easy column-formatted output for golang + +[![Build Status](https://travis-ci.org/ryanuber/columnize.svg)](https://travis-ci.org/ryanuber/columnize) +[![GoDoc](https://godoc.org/github.com/ryanuber/columnize?status.svg)](https://godoc.org/github.com/ryanuber/columnize) + +Columnize is a really small Go package that makes building CLI's a little bit +easier. In some CLI designs, you want to output a number similar items in a +human-readable way with nicely aligned columns. However, figuring out how wide +to make each column is a boring problem to solve and eats your valuable time. + +Here is an example: + +```go +package main + +import ( + "fmt" + "github.com/ryanuber/columnize" +) + +func main() { + output := []string{ + "Name | Gender | Age", + "Bob | Male | 38", + "Sally | Female | 26", + } + result := columnize.SimpleFormat(output) + fmt.Println(result) +} +``` + +As you can see, you just pass in a list of strings. And the result: + +``` +Name Gender Age +Bob Male 38 +Sally Female 26 +``` + +Columnize is tolerant of missing or empty fields, or even empty lines, so +passing in extra lines for spacing should show up as you would expect. + +Configuration +============= + +Columnize is configured using a `Config`, which can be obtained by calling the +`DefaultConfig()` method. You can then tweak the settings in the resulting +`Config`: + +``` +config := columnize.DefaultConfig() +config.Delim = "|" +config.Glue = " " +config.Prefix = "" +config.Empty = "" +``` + +* `Delim` is the string by which columns of **input** are delimited +* `Glue` is the string by which columns of **output** are delimited +* `Prefix` is a string by which each line of **output** is prefixed +* `Empty` is a string used to replace blank values found in output + +You can then pass the `Config` in using the `Format` method (signature below) to +have text formatted to your liking. + +See the [godoc](https://godoc.org/github.com/ryanuber/columnize) page for usage. diff --git a/plugins/resource/vendor/github.com/ryanuber/columnize/columnize.go b/plugins/resource/vendor/github.com/ryanuber/columnize/columnize.go new file mode 100644 index 000000000..915716a10 --- /dev/null +++ b/plugins/resource/vendor/github.com/ryanuber/columnize/columnize.go @@ -0,0 +1,178 @@ +package columnize + +import ( + "bytes" + "fmt" + "strings" +) + +// Config can be used to tune certain parameters which affect the way +// in which Columnize will format output text. +type Config struct { + // The string by which the lines of input will be split. + Delim string + + // The string by which columns of output will be separated. + Glue string + + // The string by which columns of output will be prefixed. + Prefix string + + // A replacement string to replace empty fields + Empty string +} + +// DefaultConfig returns a *Config with default values. +func DefaultConfig() *Config { + return &Config{ + Delim: "|", + Glue: " ", + Prefix: "", + Empty: "", + } +} + +// MergeConfig merges two config objects together and returns the resulting +// configuration. Values from the right take precedence over the left side. +func MergeConfig(a, b *Config) *Config { + var result Config = *a + + // Return quickly if either side was nil + if a == nil || b == nil { + return &result + } + + if b.Delim != "" { + result.Delim = b.Delim + } + if b.Glue != "" { + result.Glue = b.Glue + } + if b.Prefix != "" { + result.Prefix = b.Prefix + } + if b.Empty != "" { + result.Empty = b.Empty + } + + return &result +} + +// stringFormat, given a set of column widths and the number of columns in +// the current line, returns a sprintf-style format string which can be used +// to print output aligned properly with other lines using the same widths set. +func stringFormat(c *Config, widths []int, columns int) string { + // Create the buffer with an estimate of the length + buf := bytes.NewBuffer(make([]byte, 0, (6+len(c.Glue))*columns)) + + // Start with the prefix, if any was given. The buffer will not return an + // error so it does not need to be handled + buf.WriteString(c.Prefix) + + // Create the format string from the discovered widths + for i := 0; i < columns && i < len(widths); i++ { + if i == columns-1 { + buf.WriteString("%s\n") + } else { + fmt.Fprintf(buf, "%%-%ds%s", widths[i], c.Glue) + } + } + return buf.String() +} + +// elementsFromLine returns a list of elements, each representing a single +// item which will belong to a column of output. +func elementsFromLine(config *Config, line string) []interface{} { + seperated := strings.Split(line, config.Delim) + elements := make([]interface{}, len(seperated)) + for i, field := range seperated { + value := strings.TrimSpace(field) + + // Apply the empty value, if configured. + if value == "" && config.Empty != "" { + value = config.Empty + } + elements[i] = value + } + return elements +} + +// runeLen calculates the number of visible "characters" in a string +func runeLen(s string) int { + l := 0 + for _ = range s { + l++ + } + return l +} + +// widthsFromLines examines a list of strings and determines how wide each +// column should be considering all of the elements that need to be printed +// within it. +func widthsFromLines(config *Config, lines []string) []int { + widths := make([]int, 0, 8) + + for _, line := range lines { + elems := elementsFromLine(config, line) + for i := 0; i < len(elems); i++ { + l := runeLen(elems[i].(string)) + if len(widths) <= i { + widths = append(widths, l) + } else if widths[i] < l { + widths[i] = l + } + } + } + return widths +} + +// Format is the public-facing interface that takes a list of strings and +// returns nicely aligned column-formatted text. +func Format(lines []string, config *Config) string { + conf := MergeConfig(DefaultConfig(), config) + widths := widthsFromLines(conf, lines) + + // Estimate the buffer size + glueSize := len(conf.Glue) + var size int + for _, w := range widths { + size += w + glueSize + } + size *= len(lines) + + // Create the buffer + buf := bytes.NewBuffer(make([]byte, 0, size)) + + // Create a cache for the string formats + fmtCache := make(map[int]string, 16) + + // Create the formatted output using the format string + for _, line := range lines { + elems := elementsFromLine(conf, line) + + // Get the string format using cache + numElems := len(elems) + stringfmt, ok := fmtCache[numElems] + if !ok { + stringfmt = stringFormat(conf, widths, numElems) + fmtCache[numElems] = stringfmt + } + + fmt.Fprintf(buf, stringfmt, elems...) + } + + // Get the string result + result := buf.String() + + // Remove trailing newline without removing leading/trailing space + if n := len(result); n > 0 && result[n-1] == '\n' { + result = result[:n-1] + } + + return result +} + +// SimpleFormat is a convenience function to format text with the defaults. +func SimpleFormat(lines []string) string { + return Format(lines, nil) +} diff --git a/plugins/resource/vendor/github.com/ryanuber/columnize/columnize_test.go b/plugins/resource/vendor/github.com/ryanuber/columnize/columnize_test.go new file mode 100644 index 000000000..89dabaa38 --- /dev/null +++ b/plugins/resource/vendor/github.com/ryanuber/columnize/columnize_test.go @@ -0,0 +1,306 @@ +package columnize + +import ( + "fmt" + "testing" + + crand "crypto/rand" +) + +func TestListOfStringsInput(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | y | z", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestEmptyLinesOutput(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "", + "x | y | z", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestLeadingSpacePreserved(t *testing.T) { + input := []string{ + "| Column B | Column C", + "x | y | z", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := " Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestColumnWidthCalculator(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "Longer than A | Longer than B | Longer than C", + "short | short | short", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "Longer than A Longer than B Longer than C\n" + expected += "short short short" + + if output != expected { + printableProof := fmt.Sprintf("\nGot: %+q", output) + printableProof += fmt.Sprintf("\nExpected: %+q", expected) + t.Fatalf("\n%s", printableProof) + } +} + +func TestColumnWidthCalculatorNonASCII(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "⌘⌘⌘⌘⌘⌘⌘⌘ | Longer than B | Longer than C", + "short | short | short", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "⌘⌘⌘⌘⌘⌘⌘⌘ Longer than B Longer than C\n" + expected += "short short short" + + if output != expected { + printableProof := fmt.Sprintf("\nGot: %+q", output) + printableProof += fmt.Sprintf("\nExpected: %+q", expected) + t.Fatalf("\n%s", printableProof) + } +} + +func BenchmarkColumnWidthCalculator(b *testing.B) { + // Generate the input + input := []string{ + "UUID A | UUID B | UUID C | Column D | Column E", + } + + format := "%s|%s|%s|%s" + short := "short" + + uuid := func() string { + buf := make([]byte, 16) + if _, err := crand.Read(buf); err != nil { + panic(fmt.Errorf("failed to read random bytes: %v", err)) + } + + return fmt.Sprintf("%08x-%04x-%04x-%04x-%12x", + buf[0:4], + buf[4:6], + buf[6:8], + buf[8:10], + buf[10:16]) + } + + for i := 0; i < 1000; i++ { + l := fmt.Sprintf(format, uuid()[:8], uuid()[:12], uuid(), short, short) + input = append(input, l) + } + + config := DefaultConfig() + b.ResetTimer() + + for i := 0; i < b.N; i++ { + Format(input, config) + } +} + +func TestVariedInputSpacing(t *testing.T) { + input := []string{ + "Column A |Column B| Column C", + "x|y| z", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestUnmatchedColumnCounts(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "Value A | Value B", + "Value A | Value B | Value C | Value D", + } + + config := DefaultConfig() + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "Value A Value B\n" + expected += "Value A Value B Value C Value D" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestAlternateDelimiter(t *testing.T) { + input := []string{ + "Column | A % Column | B % Column | C", + "Value A % Value B % Value C", + } + + config := DefaultConfig() + config.Delim = "%" + output := Format(input, config) + + expected := "Column | A Column | B Column | C\n" + expected += "Value A Value B Value C" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestAlternateSpacingString(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | y | z", + } + + config := DefaultConfig() + config.Glue = " " + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestSimpleFormat(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | y | z", + } + + output := SimpleFormat(input) + + expected := "Column A Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestAlternatePrefixString(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | y | z", + } + + config := DefaultConfig() + config.Prefix = " " + output := Format(input, config) + + expected := " Column A Column B Column C\n" + expected += " x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestEmptyFieldReplacement(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | | z", + } + + config := DefaultConfig() + config.Empty = "" + output := Format(input, config) + + expected := "Column A Column B Column C\n" + expected += "x z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestEmptyConfigValues(t *testing.T) { + input := []string{ + "Column A | Column B | Column C", + "x | y | z", + } + + config := Config{} + output := Format(input, &config) + + expected := "Column A Column B Column C\n" + expected += "x y z" + + if output != expected { + t.Fatalf("\nexpected:\n%s\n\ngot:\n%s", expected, output) + } +} + +func TestMergeConfig(t *testing.T) { + conf1 := &Config{Delim: "a", Glue: "a", Prefix: "a", Empty: "a"} + conf2 := &Config{Delim: "b", Glue: "b", Prefix: "b", Empty: "b"} + conf3 := &Config{Delim: "c", Prefix: "c"} + + m := MergeConfig(conf1, conf2) + if m.Delim != "b" || m.Glue != "b" || m.Prefix != "b" || m.Empty != "b" { + t.Fatalf("bad: %#v", m) + } + + m = MergeConfig(conf1, conf3) + if m.Delim != "c" || m.Glue != "a" || m.Prefix != "c" || m.Empty != "a" { + t.Fatalf("bad: %#v", m) + } + + m = MergeConfig(conf1, nil) + if m.Delim != "a" || m.Glue != "a" || m.Prefix != "a" || m.Empty != "a" { + t.Fatalf("bad: %#v", m) + } + + m = MergeConfig(conf1, &Config{}) + if m.Delim != "a" || m.Glue != "a" || m.Prefix != "a" || m.Empty != "a" { + t.Fatalf("bad: %#v", m) + } +} From 49e44ce59b1c5325fb3575c3ca434d59dd8dcd31 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 17:57:25 -0400 Subject: [PATCH 008/163] fix: correct stickler-ci issues --- plugins/resource/resource.go | 5 ++--- plugins/resource/src/subcommands/limit/limit.go | 2 +- plugins/resource/src/subcommands/reserve/reserve.go | 2 +- plugins/resource/subcommands.go | 4 ++-- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/plugins/resource/resource.go b/plugins/resource/resource.go index f782c52a2..90c286d50 100644 --- a/plugins/resource/resource.go +++ b/plugins/resource/resource.go @@ -1,12 +1,11 @@ package resource +// A collection of resource constraints for apps type Resource struct { - Cpu string `json:"cpu"` + CPU string `json:"cpu"` Memory string `json:"memory"` MemorySwap string `json:"memory-swap"` Network string `json:"network"` NetworkIngress string `json:"network-ingress"` NetworkEgress string `json:"network-egress"` } - -const PluginName = "resource" diff --git a/plugins/resource/src/subcommands/limit/limit.go b/plugins/resource/src/subcommands/limit/limit.go index f8256d709..7046b637a 100644 --- a/plugins/resource/src/subcommands/limit/limit.go +++ b/plugins/resource/src/subcommands/limit/limit.go @@ -20,7 +20,7 @@ func main() { args.Parse(os.Args[2:]) resources := resource.Resource{ - Cpu: *cpu, + CPU: *cpu, Memory: *memory, MemorySwap: *memorySwap, Network: *network, diff --git a/plugins/resource/src/subcommands/reserve/reserve.go b/plugins/resource/src/subcommands/reserve/reserve.go index f76167b39..579abd5ca 100644 --- a/plugins/resource/src/subcommands/reserve/reserve.go +++ b/plugins/resource/src/subcommands/reserve/reserve.go @@ -20,7 +20,7 @@ func main() { args.Parse(os.Args[2:]) resources := resource.Resource{ - Cpu: *cpu, + CPU: *cpu, Memory: *memory, MemorySwap: *memorySwap, Network: *network, diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index 8c5612b96..b53cc95b9 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -32,7 +32,7 @@ func setRequestType(args []string, processType string, r Resource, requestType s } resources := map[string]string{ - "cpu": r.Cpu, + "cpu": r.CPU, "memory": r.Memory, "memory-swap": r.MemorySwap, "network": r.Network, @@ -63,7 +63,7 @@ func setRequestType(args []string, processType string, r Resource, requestType s } property := fmt.Sprintf("%v.%v.%v", processType, requestType, key) - err = common.PropertyWrite(PluginName, appName, property, value) + err = common.PropertyWrite("resource", appName, property, value) if err != nil { return } From ff35e9592459566fb6c8cd5c5fb0be5b8d3c2cb3 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 17:58:32 -0400 Subject: [PATCH 009/163] fix: correct type comment declaration --- plugins/resource/resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/resource/resource.go b/plugins/resource/resource.go index 90c286d50..1f188893a 100644 --- a/plugins/resource/resource.go +++ b/plugins/resource/resource.go @@ -1,6 +1,6 @@ package resource -// A collection of resource constraints for apps +// Resource is a collection of resource constraints for apps type Resource struct { CPU string `json:"cpu"` Memory string `json:"memory"` From b36ab54b87abbd80e2b9faf06eec4df9e22c7579 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 18:21:33 -0400 Subject: [PATCH 010/163] fix: correct variable name --- plugins/resource/src/subcommands/limit/limit.go | 4 ++-- plugins/resource/src/subcommands/reserve/reserve.go | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/resource/src/subcommands/limit/limit.go b/plugins/resource/src/subcommands/limit/limit.go index 7046b637a..d45438e3a 100644 --- a/plugins/resource/src/subcommands/limit/limit.go +++ b/plugins/resource/src/subcommands/limit/limit.go @@ -10,7 +10,7 @@ import ( func main() { args := flag.NewFlagSet("resource:limit", flag.ExitOnError) - processeType := args.String("process-type", "", "process-type: A process type to manage") + processType := args.String("process-type", "", "process-type: A process type to manage") cpu := args.String("cpu", "", "cpu: The amount of cpu to limit processes by") memory := args.String("memory", "", "memory: The amount of memory to limit processes by") memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to limit processes by") @@ -28,7 +28,7 @@ func main() { NetworkEgress: *networkEgress, } - err := resource.CommandLimit(args.Args(), *processeType, resources) + err := resource.CommandLimit(args.Args(), *processType, resources) if err != nil { common.LogFail(err.Error()) } diff --git a/plugins/resource/src/subcommands/reserve/reserve.go b/plugins/resource/src/subcommands/reserve/reserve.go index 579abd5ca..afeccb551 100644 --- a/plugins/resource/src/subcommands/reserve/reserve.go +++ b/plugins/resource/src/subcommands/reserve/reserve.go @@ -10,7 +10,7 @@ import ( func main() { args := flag.NewFlagSet("resource:reserve", flag.ExitOnError) - processeType := args.String("process-type", "", "process-type: A process type to manage") + processType := args.String("process-type", "", "process-type: A process type to manage") cpu := args.String("cpu", "", "cpu: The amount of cpu to reserve for processes") memory := args.String("memory", "", "memory: The amount of memory to reserve for processes") memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to reserve for processes") @@ -28,7 +28,7 @@ func main() { NetworkEgress: *networkEgress, } - err := resource.CommandReserve(args.Args(), *processeType, resources) + err := resource.CommandReserve(args.Args(), *processType, resources) if err != nil { common.LogFail(err.Error()) } From 9808ecde811269f612edd819cb402d80123617a1 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 19:57:17 -0400 Subject: [PATCH 011/163] feat: implement default limit management Also add generic report information when no flags are specified --- docs/advanced-usage/resource-management.md | 72 ++++++++++++-- plugins/resource/Makefile | 2 +- plugins/resource/src/commands/commands.go | 2 + .../limit-defaults/limit-defaults.go | 35 +++++++ .../reserve-defaults/reserve-defaults.go | 35 +++++++ plugins/resource/subcommands.go | 95 ++++++++++++++++--- 6 files changed, 215 insertions(+), 26 deletions(-) create mode 100644 plugins/resource/src/subcommands/limit-defaults/limit-defaults.go create mode 100644 plugins/resource/src/subcommands/reserve-defaults/reserve-defaults.go diff --git a/docs/advanced-usage/resource-management.md b/docs/advanced-usage/resource-management.md index 91a9bc9c4..a646a303c 100644 --- a/docs/advanced-usage/resource-management.md +++ b/docs/advanced-usage/resource-management.md @@ -3,12 +3,12 @@ > New as of 0.15.0 ``` -dokku resource:limit --process-type [RESOURCE_OPTS...] -dokku resource:reserve --process-type [RESOURCE_OPTS...] -dokku resource:limit-defaults [RESOURCE_OPTS...] -dokku resource:reserve-defaults [RESOURCE_OPTS...] -dokku resource:limit-clear [] -dokku resource:reserve-clear [] +dokku resource:limit [--process-type ] [RESOURCE_OPTS...] +dokku resource:reserve [--process-type ] [RESOURCE_OPTS...] +dokku resource:limit-defaults [--process-type ] [RESOURCE_OPTS...] +dokku resource:reserve-defaults [--process-type ] [RESOURCE_OPTS...] +dokku resource:limit-clear [--process-type ] [] +dokku resource:reserve-clear [--process-type ] [] dokku resource:report [] ``` @@ -68,17 +68,43 @@ dokku resource:limit --cpu 100 --memory 100 --process-type worker node-js-app memory: 100 ``` +Running the `resource:limits` command without any flags will display the currently configured app reservation. + +```shell +dokku resource:limits node-js-app +=====> resource limits node-js-app information + cpu: 100 + memory: 100 + memory-swap: + network: + network-ingress: + network-egress: +``` + #### Default Resource Limits By default, Dokku does not limit any resources, though these can be specified at the server level via the `resource:limit-defaults` command. As with the `resource:limit` command, any and all resource request types can be specified. ```shell -dokku resource:limit-defaults --memory 100 +dokku resource:limit-defaults --memory 200 ``` ``` =====> Setting default resource limits - memory: 100 + memory: 200 +``` + +Running the `resource:limit-defaults` command without any flags will display the currently configured default limits. + +```shell +dokku resource:limit-defaults +=====> resource limits default information + cpu: + memory: 200 + memory-swap: + network: + network-ingress: + network-egress: ``` ### Clearing Resource Limits @@ -142,17 +168,43 @@ dokku resource:reserve --cpu 100 --memory 100 --process-type worker node-js-app memory: 100 ``` +Running the `resource:reserve` command without any flags will display the currently configured app reservation. + +```shell +dokku resource:reserve node-js-app +=====> resource reservation node-js-app information + cpu: 100 + memory: 100 + memory-swap: + network: + network-ingress: + network-egress: +``` + #### Default Resource Reservations By default, Dokku does not reserve any resources, though these can be specified at the server level via the `resource:reserve-defaults` command. As with the `resource:reserve` command, any and all resource request types can be specified. ```shell -dokku resource:reserve-defaults --memory 100 +dokku resource:reserve-defaults --memory 200 ``` ``` =====> Setting default resource reservation - memory: 100 + memory: 200 +``` + +Running the `resource:reserve-defaults` command without any flags will display the currently configured default reservation. + +```shell +dokku resource:reserve-defaults +=====> resource reservation default information + cpu: + memory: 200 + memory-swap: + network: + network-ingress: + network-egress: ``` #### Clearing Resource Reservations diff --git a/plugins/resource/Makefile b/plugins/resource/Makefile index fde0627dd..dca4586ed 100644 --- a/plugins/resource/Makefile +++ b/plugins/resource/Makefile @@ -2,7 +2,7 @@ include ../../common.mk GO_ARGS ?= -a -SUBCOMMANDS = subcommands/limit subcommands/reserve +SUBCOMMANDS = subcommands/limit subcommands/limit-defaults subcommands/reserve subcommands/reserve-defaults TRIGGERS = triggers/install triggers/post-delete build-in-docker: clean docker run --rm \ diff --git a/plugins/resource/src/commands/commands.go b/plugins/resource/src/commands/commands.go index 3a13ddf6e..0387b7735 100644 --- a/plugins/resource/src/commands/commands.go +++ b/plugins/resource/src/commands/commands.go @@ -21,6 +21,8 @@ Additional commands:` helpContent = ` resource:limit [--process-type process-type] [RESOURCE_OPTS...] , Limit resources for a given app/process-type combination resource:reserve [--process-type process-type] [RESOURCE_OPTS...] , Reserve resources for a given app/process-type combination + resource:limit-defaults [--process-type process-type] [RESOURCE_OPTS...] , Limit resources for a given app/process-type combination + resource:reserve-defaults [--process-type process-type] [RESOURCE_OPTS...] , Reserve resources for a given app/process-type combination ` ) diff --git a/plugins/resource/src/subcommands/limit-defaults/limit-defaults.go b/plugins/resource/src/subcommands/limit-defaults/limit-defaults.go new file mode 100644 index 000000000..9efaa8c0e --- /dev/null +++ b/plugins/resource/src/subcommands/limit-defaults/limit-defaults.go @@ -0,0 +1,35 @@ +package main + +import ( + "flag" + "os" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/resource" +) + +func main() { + args := flag.NewFlagSet("resource:limit-defaults", flag.ExitOnError) + processType := args.String("process-type", "", "process-type: A process type to manage") + cpu := args.String("cpu", "", "cpu: The amount of cpu to limit processes by") + memory := args.String("memory", "", "memory: The amount of memory to limit processes by") + memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to limit processes by") + network := args.String("network", "", "network: The amount of network bandwidth to limit processes by") + networkIngress := args.String("network-ingress", "", "network-ingress: The amount of ingress network bandwidth to limit processes by") + networkEgress := args.String("network-egress", "", "network-egress: The amount of egress network bandwidth to limit processes by") + args.Parse(os.Args[2:]) + + resources := resource.Resource{ + CPU: *cpu, + Memory: *memory, + MemorySwap: *memorySwap, + Network: *network, + NetworkIngress: *networkIngress, + NetworkEgress: *networkEgress, + } + + err := resource.CommandLimitDefaults(args.Args(), *processType, resources) + if err != nil { + common.LogFail(err.Error()) + } +} diff --git a/plugins/resource/src/subcommands/reserve-defaults/reserve-defaults.go b/plugins/resource/src/subcommands/reserve-defaults/reserve-defaults.go new file mode 100644 index 000000000..f7edb412a --- /dev/null +++ b/plugins/resource/src/subcommands/reserve-defaults/reserve-defaults.go @@ -0,0 +1,35 @@ +package main + +import ( + "flag" + "os" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/resource" +) + +func main() { + args := flag.NewFlagSet("resource:reserve-defaults", flag.ExitOnError) + processType := args.String("process-type", "", "process-type: A process type to manage") + cpu := args.String("cpu", "", "cpu: The amount of cpu to reserve for processes") + memory := args.String("memory", "", "memory: The amount of memory to reserve for processes") + memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to reserve for processes") + network := args.String("network", "", "network: The amount of network bandwidth to reserve for processes") + networkIngress := args.String("network-ingress", "", "network-ingress: The amount of ingress network bandwidth to reserve for processes") + networkEgress := args.String("network-egress", "", "network-egress: The amount of egress network bandwidth to reserve for processes") + args.Parse(os.Args[2:]) + + resources := resource.Resource{ + CPU: *cpu, + Memory: *memory, + MemorySwap: *memorySwap, + Network: *network, + NetworkIngress: *networkIngress, + NetworkEgress: *networkEgress, + } + + err := resource.CommandReserveDefaults(args.Args(), *processType, resources) + if err != nil { + common.LogFail(err.Error()) + } +} diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index b53cc95b9..df014668f 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -8,15 +8,6 @@ import ( // CommandLimit implements resource:limit func CommandLimit(args []string, processType string, r Resource) (err error) { - return setRequestType(args, processType, r, "limit") -} - -// CommandReserve implements resource:reserve -func CommandReserve(args []string, processType string, r Resource) (err error) { - return setRequestType(args, processType, r, "reserve") -} - -func setRequestType(args []string, processType string, r Resource, requestType string) (err error) { var appName string appName, err = getAppName(args) if err != nil { @@ -27,6 +18,35 @@ func setRequestType(args []string, processType string, r Resource, requestType s common.LogFail(err.Error()) } + return setRequestType(appName, processType, r, "limit") +} + +// CommandLimitDefaults implements resource:limit-defaults +func CommandLimitDefaults(args []string, processType string, r Resource) (err error) { + return setRequestType("_all_", "", r, "limit") +} + +// CommandReserve implements resource:reserve +func CommandReserve(args []string, processType string, r Resource) (err error) { + var appName string + appName, err = getAppName(args) + if err != nil { + return + } + + if err = common.VerifyAppName(appName); err != nil { + common.LogFail(err.Error()) + } + + return setRequestType(appName, processType, r, "reserve") +} + +// CommandReserveDefaults implements resource:reserve-defaults +func CommandReserveDefaults(args []string, processType string, r Resource) (err error) { + return setRequestType("_all_", "", r, "reserve") +} + +func setRequestType(appName string, processType string, r Resource, requestType string) (err error) { if len(processType) == 0 { processType = "_all_" } @@ -48,21 +68,29 @@ func setRequestType(args []string, processType string, r Resource, requestType s } if !hasValues { - return errors.New("Please specify a resource to modify") + return reportRequestType(appName, processType, requestType) } - if requestType == "limit" { - common.LogInfo2Quiet(fmt.Sprintf("Setting resource limits for %v", appName)) - } else if requestType == "reserve" { - common.LogInfo2Quiet(fmt.Sprintf("Setting resource reservation for %v", appName)) + noun := "limits" + if requestType == "reserve" { + noun = "reservation" } + message := fmt.Sprintf("Setting resource %v for %v", noun, appName) + if appName == "_all_" { + message = fmt.Sprintf("Setting default resource %v", noun) + } + + if processType != "_all_" { + message = fmt.Sprintf("%v (%v)", message, processType) + } + common.LogInfo2Quiet(message) for key, value := range resources { if value != "" { common.LogVerbose(fmt.Sprintf("%v: %v", key, value)) } - property := fmt.Sprintf("%v.%v.%v", processType, requestType, key) + property := propertyKey(processType, requestType, key) err = common.PropertyWrite("resource", appName, property, value) if err != nil { return @@ -72,6 +100,43 @@ func setRequestType(args []string, processType string, r Resource, requestType s return } +func reportRequestType(appName string, processType string, requestType string) (err error) { + noun := "limits" + if requestType == "reserve" { + noun = "reservation" + } + + humanAppName := appName + if appName == "_all_" { + humanAppName = "default" + } + message := fmt.Sprintf("resource %v %v information", noun, humanAppName) + if processType != "_all_" { + message = fmt.Sprintf("%v (%v)", message, processType) + } + common.LogInfo2Quiet(message) + + resources := map[string]bool{ + "cpu": true, + "memory": true, + "memory-swap": true, + "network": true, + "network-ingress": true, + "network-egress": true, + } + + for key, _ := range resources { + property := propertyKey(processType, requestType, key) + value := common.PropertyGet("resource", appName, property) + common.LogVerbose(fmt.Sprintf("%v: %v", key, value)) + } + return nil +} + +func propertyKey(processType string, requestType string, key string) string { + return fmt.Sprintf("%v.%v.%v", processType, requestType, key) +} + func getAppName(args []string) (appName string, err error) { if len(args) >= 1 { appName = args[0] From bdfde9a481aa8b6311c93aaebdba910fec5fdef2 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 20:06:48 -0400 Subject: [PATCH 012/163] feat: initial stub implementation for resource:*-clear commands --- plugins/resource/Makefile | 2 +- .../subcommands/limit-clear/limit-clear.go | 20 +++++++++++++++++++ .../reserve-clear/reserve-clear.go | 20 +++++++++++++++++++ plugins/resource/subcommands.go | 10 ++++++++++ 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 plugins/resource/src/subcommands/limit-clear/limit-clear.go create mode 100644 plugins/resource/src/subcommands/reserve-clear/reserve-clear.go diff --git a/plugins/resource/Makefile b/plugins/resource/Makefile index dca4586ed..783f03f82 100644 --- a/plugins/resource/Makefile +++ b/plugins/resource/Makefile @@ -2,7 +2,7 @@ include ../../common.mk GO_ARGS ?= -a -SUBCOMMANDS = subcommands/limit subcommands/limit-defaults subcommands/reserve subcommands/reserve-defaults +SUBCOMMANDS = subcommands/limit subcommands/limit-clear subcommands/limit-defaults subcommands/reserve subcommands/reserve-clear subcommands/reserve-defaults TRIGGERS = triggers/install triggers/post-delete build-in-docker: clean docker run --rm \ diff --git a/plugins/resource/src/subcommands/limit-clear/limit-clear.go b/plugins/resource/src/subcommands/limit-clear/limit-clear.go new file mode 100644 index 000000000..b794dfc30 --- /dev/null +++ b/plugins/resource/src/subcommands/limit-clear/limit-clear.go @@ -0,0 +1,20 @@ +package main + +import ( + "flag" + "os" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/resource" +) + +func main() { + args := flag.NewFlagSet("resource:limit-clear", flag.ExitOnError) + processType := args.String("process-type", "", "process-type: A process type to clear") + args.Parse(os.Args[2:]) + + err := resource.CommandLimitClear(args.Args(), *processType) + if err != nil { + common.LogFail(err.Error()) + } +} diff --git a/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go b/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go new file mode 100644 index 000000000..4f882388d --- /dev/null +++ b/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go @@ -0,0 +1,20 @@ +package main + +import ( + "flag" + "os" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/resource" +) + +func main() { + args := flag.NewFlagSet("resource:reserve-clear", flag.ExitOnError) + processType := args.String("process-type", "", "process-type: A process type to clear") + args.Parse(os.Args[2:]) + + err := resource.CommandReserveClear(args.Args(), *processType) + if err != nil { + common.LogFail(err.Error()) + } +} diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index df014668f..9ee9271f2 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -21,6 +21,11 @@ func CommandLimit(args []string, processType string, r Resource) (err error) { return setRequestType(appName, processType, r, "limit") } +// CommandLimitClear implements resource:limit-clear +func CommandLimitClear(args []string, processType string) (err error) { + return nil +} + // CommandLimitDefaults implements resource:limit-defaults func CommandLimitDefaults(args []string, processType string, r Resource) (err error) { return setRequestType("_all_", "", r, "limit") @@ -41,6 +46,11 @@ func CommandReserve(args []string, processType string, r Resource) (err error) { return setRequestType(appName, processType, r, "reserve") } +// CommandReserveClear implements resource:reserve-clear +func CommandReserveClear(args []string, processType string) (err error) { + return nil +} + // CommandReserveDefaults implements resource:reserve-defaults func CommandReserveDefaults(args []string, processType string, r Resource) (err error) { return setRequestType("_all_", "", r, "reserve") From 04a6e30e457920631c4d64c2551698e4e60e1658 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 20:53:38 -0400 Subject: [PATCH 013/163] refactor: drop `-defaults` command in favor of --global flag This makes usage a bit simpler, at the cost of interface complexity. --- docs/advanced-usage/resource-management.md | 96 ++++++++++++++++--- plugins/resource/Makefile | 2 +- plugins/resource/src/commands/commands.go | 6 +- .../subcommands/limit-clear/limit-clear.go | 3 +- .../limit-defaults/limit-defaults.go | 35 ------- .../resource/src/subcommands/limit/limit.go | 3 +- .../reserve-clear/reserve-clear.go | 3 +- .../reserve-defaults/reserve-defaults.go | 35 ------- .../src/subcommands/reserve/reserve.go | 3 +- plugins/resource/subcommands.go | 50 +++++----- 10 files changed, 114 insertions(+), 122 deletions(-) delete mode 100644 plugins/resource/src/subcommands/limit-defaults/limit-defaults.go delete mode 100644 plugins/resource/src/subcommands/reserve-defaults/reserve-defaults.go diff --git a/docs/advanced-usage/resource-management.md b/docs/advanced-usage/resource-management.md index a646a303c..7725cefec 100644 --- a/docs/advanced-usage/resource-management.md +++ b/docs/advanced-usage/resource-management.md @@ -3,10 +3,8 @@ > New as of 0.15.0 ``` -dokku resource:limit [--process-type ] [RESOURCE_OPTS...] -dokku resource:reserve [--process-type ] [RESOURCE_OPTS...] -dokku resource:limit-defaults [--process-type ] [RESOURCE_OPTS...] -dokku resource:reserve-defaults [--process-type ] [RESOURCE_OPTS...] +dokku resource:limit [--global] [--process-type ] [RESOURCE_OPTS...] +dokku resource:reserve [--global] [--process-type ] [RESOURCE_OPTS...] dokku resource:limit-clear [--process-type ] [] dokku resource:reserve-clear [--process-type ] [] dokku resource:report [] @@ -68,6 +66,8 @@ dokku resource:limit --cpu 100 --memory 100 --process-type worker node-js-app memory: 100 ``` +#### Displaying Resource Limits + Running the `resource:limits` command without any flags will display the currently configured app reservation. ```shell @@ -81,12 +81,12 @@ dokku resource:limits node-js-app network-egress: ``` -#### Default Resource Limits +#### Global Resource Limits -By default, Dokku does not limit any resources, though these can be specified at the server level via the `resource:limit-defaults` command. As with the `resource:limit` command, any and all resource request types can be specified. +By default, Dokku does not limit any resources, though these can be specified at the server level via the `--global` flag on the `resource:limit` command. ```shell -dokku resource:limit-defaults --memory 200 +dokku resource:limit --global --memory 200 ``` ``` @@ -94,10 +94,26 @@ dokku resource:limit-defaults --memory 200 memory: 200 ``` -Running the `resource:limit-defaults` command without any flags will display the currently configured default limits. +This may also be combined with the `--process-type` flag to set global limits on a process-type level: ```shell -dokku resource:limit-defaults +dokku resource:limit --global --memory 150 --process-type web node-js-app +``` + +``` +=====> Setting default resource limits (web) + memory: 150 +``` + +#### Displaying Global Resource Limits + +Running the `resource:limit` command with only the `--global` flag will display the currently configured default limits. + +```shell +dokku resource:limit --global +``` + +``` =====> resource limits default information cpu: memory: 200 @@ -107,7 +123,23 @@ dokku resource:limit-defaults network-egress: ``` -### Clearing Resource Limits +This may also be combined with the `--process-type` flag to see global limits on a process-type level: + +```shell +dokku resource:limit --global --process-type web +``` + +``` +=====> resource limits default information (web) + cpu: + memory-swap: + memory: 150 + network-egress: + network-ingress: + network: +``` + +#### Clearing Resource Limits In cases where the values are incorrect - or there is no desire to limit resources - resource limits may be cleared using the `resource:limit-clear` command. @@ -168,6 +200,8 @@ dokku resource:reserve --cpu 100 --memory 100 --process-type worker node-js-app memory: 100 ``` +#### Displaying Resource Reservations + Running the `resource:reserve` command without any flags will display the currently configured app reservation. ```shell @@ -181,12 +215,12 @@ dokku resource:reserve node-js-app network-egress: ``` -#### Default Resource Reservations +#### Global Resource Reservations -By default, Dokku does not reserve any resources, though these can be specified at the server level via the `resource:reserve-defaults` command. As with the `resource:reserve` command, any and all resource request types can be specified. +By default, Dokku does not reserve any resources, though these can be specified at the server level via the `--global` flag on the `resource:reserve` command. ```shell -dokku resource:reserve-defaults --memory 200 +dokku resource:reserve --global --memory 200 ``` ``` @@ -194,10 +228,26 @@ dokku resource:reserve-defaults --memory 200 memory: 200 ``` -Running the `resource:reserve-defaults` command without any flags will display the currently configured default reservation. +This may also be combined with the `--process-type` flag to set global reservations on a process-type level: ```shell -dokku resource:reserve-defaults +dokku resource:reserve --global --memory 150 --process-type web node-js-app +``` + +``` +=====> Setting default resource reservation (web) + memory: 150 +``` + +#### Displaying Global Resource Reservations + +Running the `resource:reserve` command with only the `--global` flag will display the currently configured default reservation. + +```shell +dokku resource:reserve --global +``` + +``` =====> resource reservation default information cpu: memory: 200 @@ -207,6 +257,22 @@ dokku resource:reserve-defaults network-egress: ``` +This may also be combined with the `--process-type` flag to see global reservations on a process-type level: + +```shell +dokku resource:reserve --global --process-type web +``` + +``` +=====> resource reservation default information (web) + cpu: + memory-swap: + memory: 150 + network-egress: + network-ingress: + network: +``` + #### Clearing Resource Reservations In cases where the values are incorrect - or there is no desire to reserve resources - resource reservations may be cleared using the `resource:reserve-clear` command. diff --git a/plugins/resource/Makefile b/plugins/resource/Makefile index 783f03f82..d2b724c7b 100644 --- a/plugins/resource/Makefile +++ b/plugins/resource/Makefile @@ -2,7 +2,7 @@ include ../../common.mk GO_ARGS ?= -a -SUBCOMMANDS = subcommands/limit subcommands/limit-clear subcommands/limit-defaults subcommands/reserve subcommands/reserve-clear subcommands/reserve-defaults +SUBCOMMANDS = subcommands/limit subcommands/limit-clear subcommands/reserve subcommands/reserve-clear TRIGGERS = triggers/install triggers/post-delete build-in-docker: clean docker run --rm \ diff --git a/plugins/resource/src/commands/commands.go b/plugins/resource/src/commands/commands.go index 0387b7735..5e13ed013 100644 --- a/plugins/resource/src/commands/commands.go +++ b/plugins/resource/src/commands/commands.go @@ -19,10 +19,8 @@ Manages resource settings for an app Additional commands:` helpContent = ` - resource:limit [--process-type process-type] [RESOURCE_OPTS...] , Limit resources for a given app/process-type combination - resource:reserve [--process-type process-type] [RESOURCE_OPTS...] , Reserve resources for a given app/process-type combination - resource:limit-defaults [--process-type process-type] [RESOURCE_OPTS...] , Limit resources for a given app/process-type combination - resource:reserve-defaults [--process-type process-type] [RESOURCE_OPTS...] , Reserve resources for a given app/process-type combination + resource:limit [--global] [--process-type process-type] [RESOURCE_OPTS...] , Limit resources for a given app/process-type combination + resource:reserve [--global] [--process-type process-type] [RESOURCE_OPTS...] , Reserve resources for a given app/process-type combination ` ) diff --git a/plugins/resource/src/subcommands/limit-clear/limit-clear.go b/plugins/resource/src/subcommands/limit-clear/limit-clear.go index b794dfc30..32000d6d6 100644 --- a/plugins/resource/src/subcommands/limit-clear/limit-clear.go +++ b/plugins/resource/src/subcommands/limit-clear/limit-clear.go @@ -11,9 +11,10 @@ import ( func main() { args := flag.NewFlagSet("resource:limit-clear", flag.ExitOnError) processType := args.String("process-type", "", "process-type: A process type to clear") + global := args.Bool("global", false, "global: Whether to clear global settings or not") args.Parse(os.Args[2:]) - err := resource.CommandLimitClear(args.Args(), *processType) + err := resource.CommandLimitClear(args.Args(), *processType, *global) if err != nil { common.LogFail(err.Error()) } diff --git a/plugins/resource/src/subcommands/limit-defaults/limit-defaults.go b/plugins/resource/src/subcommands/limit-defaults/limit-defaults.go deleted file mode 100644 index 9efaa8c0e..000000000 --- a/plugins/resource/src/subcommands/limit-defaults/limit-defaults.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "flag" - "os" - - "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/resource" -) - -func main() { - args := flag.NewFlagSet("resource:limit-defaults", flag.ExitOnError) - processType := args.String("process-type", "", "process-type: A process type to manage") - cpu := args.String("cpu", "", "cpu: The amount of cpu to limit processes by") - memory := args.String("memory", "", "memory: The amount of memory to limit processes by") - memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to limit processes by") - network := args.String("network", "", "network: The amount of network bandwidth to limit processes by") - networkIngress := args.String("network-ingress", "", "network-ingress: The amount of ingress network bandwidth to limit processes by") - networkEgress := args.String("network-egress", "", "network-egress: The amount of egress network bandwidth to limit processes by") - args.Parse(os.Args[2:]) - - resources := resource.Resource{ - CPU: *cpu, - Memory: *memory, - MemorySwap: *memorySwap, - Network: *network, - NetworkIngress: *networkIngress, - NetworkEgress: *networkEgress, - } - - err := resource.CommandLimitDefaults(args.Args(), *processType, resources) - if err != nil { - common.LogFail(err.Error()) - } -} diff --git a/plugins/resource/src/subcommands/limit/limit.go b/plugins/resource/src/subcommands/limit/limit.go index d45438e3a..024835acd 100644 --- a/plugins/resource/src/subcommands/limit/limit.go +++ b/plugins/resource/src/subcommands/limit/limit.go @@ -11,6 +11,7 @@ import ( func main() { args := flag.NewFlagSet("resource:limit", flag.ExitOnError) processType := args.String("process-type", "", "process-type: A process type to manage") + global := args.Bool("global", false, "global: Whether to clear global settings or not") cpu := args.String("cpu", "", "cpu: The amount of cpu to limit processes by") memory := args.String("memory", "", "memory: The amount of memory to limit processes by") memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to limit processes by") @@ -28,7 +29,7 @@ func main() { NetworkEgress: *networkEgress, } - err := resource.CommandLimit(args.Args(), *processType, resources) + err := resource.CommandLimit(args.Args(), *processType, resources, *global) if err != nil { common.LogFail(err.Error()) } diff --git a/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go b/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go index 4f882388d..c27a02bba 100644 --- a/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go +++ b/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go @@ -11,9 +11,10 @@ import ( func main() { args := flag.NewFlagSet("resource:reserve-clear", flag.ExitOnError) processType := args.String("process-type", "", "process-type: A process type to clear") + global := args.Bool("global", false, "global: Whether to clear global settings or not") args.Parse(os.Args[2:]) - err := resource.CommandReserveClear(args.Args(), *processType) + err := resource.CommandReserveClear(args.Args(), *processType, *global) if err != nil { common.LogFail(err.Error()) } diff --git a/plugins/resource/src/subcommands/reserve-defaults/reserve-defaults.go b/plugins/resource/src/subcommands/reserve-defaults/reserve-defaults.go deleted file mode 100644 index f7edb412a..000000000 --- a/plugins/resource/src/subcommands/reserve-defaults/reserve-defaults.go +++ /dev/null @@ -1,35 +0,0 @@ -package main - -import ( - "flag" - "os" - - "github.com/dokku/dokku/plugins/common" - "github.com/dokku/dokku/plugins/resource" -) - -func main() { - args := flag.NewFlagSet("resource:reserve-defaults", flag.ExitOnError) - processType := args.String("process-type", "", "process-type: A process type to manage") - cpu := args.String("cpu", "", "cpu: The amount of cpu to reserve for processes") - memory := args.String("memory", "", "memory: The amount of memory to reserve for processes") - memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to reserve for processes") - network := args.String("network", "", "network: The amount of network bandwidth to reserve for processes") - networkIngress := args.String("network-ingress", "", "network-ingress: The amount of ingress network bandwidth to reserve for processes") - networkEgress := args.String("network-egress", "", "network-egress: The amount of egress network bandwidth to reserve for processes") - args.Parse(os.Args[2:]) - - resources := resource.Resource{ - CPU: *cpu, - Memory: *memory, - MemorySwap: *memorySwap, - Network: *network, - NetworkIngress: *networkIngress, - NetworkEgress: *networkEgress, - } - - err := resource.CommandReserveDefaults(args.Args(), *processType, resources) - if err != nil { - common.LogFail(err.Error()) - } -} diff --git a/plugins/resource/src/subcommands/reserve/reserve.go b/plugins/resource/src/subcommands/reserve/reserve.go index afeccb551..a62a34d55 100644 --- a/plugins/resource/src/subcommands/reserve/reserve.go +++ b/plugins/resource/src/subcommands/reserve/reserve.go @@ -11,6 +11,7 @@ import ( func main() { args := flag.NewFlagSet("resource:reserve", flag.ExitOnError) processType := args.String("process-type", "", "process-type: A process type to manage") + global := args.Bool("global", false, "global: Whether to clear global settings or not") cpu := args.String("cpu", "", "cpu: The amount of cpu to reserve for processes") memory := args.String("memory", "", "memory: The amount of memory to reserve for processes") memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to reserve for processes") @@ -28,7 +29,7 @@ func main() { NetworkEgress: *networkEgress, } - err := resource.CommandReserve(args.Args(), *processType, resources) + err := resource.CommandReserve(args.Args(), *processType, resources, *global) if err != nil { common.LogFail(err.Error()) } diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index 9ee9271f2..d93ebbd2d 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -7,55 +7,49 @@ import ( ) // CommandLimit implements resource:limit -func CommandLimit(args []string, processType string, r Resource) (err error) { - var appName string - appName, err = getAppName(args) - if err != nil { - return - } +func CommandLimit(args []string, processType string, r Resource, global bool) (err error) { + appName := "_all_" + if !global { + appName, err = getAppName(args) + if err != nil { + return + } - if err = common.VerifyAppName(appName); err != nil { - common.LogFail(err.Error()) + if err = common.VerifyAppName(appName); err != nil { + common.LogFail(err.Error()) + } } return setRequestType(appName, processType, r, "limit") } // CommandLimitClear implements resource:limit-clear -func CommandLimitClear(args []string, processType string) (err error) { +func CommandLimitClear(args []string, processType string, global bool) (err error) { return nil } -// CommandLimitDefaults implements resource:limit-defaults -func CommandLimitDefaults(args []string, processType string, r Resource) (err error) { - return setRequestType("_all_", "", r, "limit") -} - // CommandReserve implements resource:reserve -func CommandReserve(args []string, processType string, r Resource) (err error) { - var appName string - appName, err = getAppName(args) - if err != nil { - return - } +func CommandReserve(args []string, processType string, r Resource, global bool) (err error) { + appName := "_all_" + if !global { + appName, err = getAppName(args) + if err != nil { + return + } - if err = common.VerifyAppName(appName); err != nil { - common.LogFail(err.Error()) + if err = common.VerifyAppName(appName); err != nil { + common.LogFail(err.Error()) + } } return setRequestType(appName, processType, r, "reserve") } // CommandReserveClear implements resource:reserve-clear -func CommandReserveClear(args []string, processType string) (err error) { +func CommandReserveClear(args []string, processType string, global bool) (err error) { return nil } -// CommandReserveDefaults implements resource:reserve-defaults -func CommandReserveDefaults(args []string, processType string, r Resource) (err error) { - return setRequestType("_all_", "", r, "reserve") -} - func setRequestType(appName string, processType string, r Resource, requestType string) (err error) { if len(processType) == 0 { processType = "_all_" From 8d2355683199d75d8820d8065e21d931a7020399 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Wed, 13 Mar 2019 21:39:56 -0400 Subject: [PATCH 014/163] fix: omit 2nd, unused value from range --- plugins/resource/subcommands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index d93ebbd2d..d783adeff 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -129,7 +129,7 @@ func reportRequestType(appName string, processType string, requestType string) ( "network-egress": true, } - for key, _ := range resources { + for key := range resources { property := propertyKey(processType, requestType, key) value := common.PropertyGet("resource", appName, property) common.LogVerbose(fmt.Sprintf("%v: %v", key, value)) From c5717510ee45a18af1c2941e828767efb7dcf92f Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Thu, 14 Mar 2019 12:36:37 -0400 Subject: [PATCH 015/163] feat: always get keys in order Ranging over maps is unordered in golang, so we need to use the correct data structure if we need the output in an ordered way. --- plugins/resource/subcommands.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index d783adeff..444cc12b0 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -120,16 +120,16 @@ func reportRequestType(appName string, processType string, requestType string) ( } common.LogInfo2Quiet(message) - resources := map[string]bool{ - "cpu": true, - "memory": true, - "memory-swap": true, - "network": true, - "network-ingress": true, - "network-egress": true, + resources := []string{ + "cpu", + "memory", + "memory-swap", + "network", + "network-ingress", + "network-egress", } - for key := range resources { + for _, key := range resources { property := propertyKey(processType, requestType, key) value := common.PropertyGet("resource", appName, property) common.LogVerbose(fmt.Sprintf("%v: %v", key, value)) From ab9f516c089fbaa4196d6bfce3f161124cd063ed Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Thu, 14 Mar 2019 12:37:33 -0400 Subject: [PATCH 016/163] refactor: clean up duplication in retrieving and verifying the APP argument --- plugins/resource/subcommands.go | 46 ++++++++++++++------------------- 1 file changed, 20 insertions(+), 26 deletions(-) diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index 444cc12b0..80631225f 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -8,16 +8,9 @@ import ( // CommandLimit implements resource:limit func CommandLimit(args []string, processType string, r Resource, global bool) (err error) { - appName := "_all_" - if !global { - appName, err = getAppName(args) - if err != nil { - return - } - - if err = common.VerifyAppName(appName); err != nil { - common.LogFail(err.Error()) - } + appName, err = getAppName(args, global) + if err != nil { + return err } return setRequestType(appName, processType, r, "limit") @@ -30,16 +23,9 @@ func CommandLimitClear(args []string, processType string, global bool) (err erro // CommandReserve implements resource:reserve func CommandReserve(args []string, processType string, r Resource, global bool) (err error) { - appName := "_all_" - if !global { - appName, err = getAppName(args) - if err != nil { - return - } - - if err = common.VerifyAppName(appName); err != nil { - common.LogFail(err.Error()) - } + appName, err = getAppName(args, global) + if err != nil { + return err } return setRequestType(appName, processType, r, "reserve") @@ -141,12 +127,20 @@ func propertyKey(processType string, requestType string, key string) string { return fmt.Sprintf("%v.%v.%v", processType, requestType, key) } -func getAppName(args []string) (appName string, err error) { - if len(args) >= 1 { - appName = args[0] - } else { - err = errors.New("Please specify an app to run the command on") +func getAppName(args []string, global bool) (appName string, err error) { + appName := "_all_" + if global { + return appName, nil } - return + if len(args) < 1 { + return "", errors.New("Please specify an app to run the command on") + } + + appName = args[0] + if err = common.VerifyAppName(appName); err != nil { + return "", err + } + + return appName, nil } From 9acb9e527d68bee5bac5d84f320b2e524d201163 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Thu, 14 Mar 2019 12:38:59 -0400 Subject: [PATCH 017/163] feat: implement resource:*-clear commands --- plugins/resource/subcommands.go | 49 +++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index 80631225f..edf9b4038 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -18,7 +18,12 @@ func CommandLimit(args []string, processType string, r Resource, global bool) (e // CommandLimitClear implements resource:limit-clear func CommandLimitClear(args []string, processType string, global bool) (err error) { - return nil + appName, err = getAppName(args, global) + if err != nil { + return err + } + + return clearByRequestType(appName, processType, "limit") } // CommandReserve implements resource:reserve @@ -32,7 +37,47 @@ func CommandReserve(args []string, processType string, r Resource, global bool) } // CommandReserveClear implements resource:reserve-clear -func CommandReserveClear(args []string, processType string, global bool) (err error) { +func CommandReserveClear(args []string, processType string, global bool) error { + appName, err = getAppName(args, global) + if err != nil { + return err + } + + return clearByRequestType(appName, processType, "reserve") +} + +func clearByRequestType(appName, processType, requestType) error { + noun := "limits" + if requestType == "reserve" { + noun = "reservation" + } + + humanAppName := appName + if appName == "_all_" { + humanAppName = "default" + } + message := fmt.Sprintf("clearing %v %v", humanAppName, noun) + if processType != "_all_" { + message = fmt.Sprintf("%v (%v)", message, processType) + } + common.LogInfo2Quiet(message) + + resources := []string{ + "cpu", + "memory", + "memory-swap", + "network", + "network-ingress", + "network-egress", + } + + for _, key := range resources { + property := propertyKey(processType, requestType, key) + err = common.PropertyDelete("resource", appName, property) + if err != nil { + return err + } + } return nil } From 982895ff71f6b4b28f9cc921954b06ad6c2b849e Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 18 Mar 2019 15:30:44 -0400 Subject: [PATCH 018/163] fix: add missing argument --- plugins/resource/subcommands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index edf9b4038..cab48632a 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -46,7 +46,7 @@ func CommandReserveClear(args []string, processType string, global bool) error { return clearByRequestType(appName, processType, "reserve") } -func clearByRequestType(appName, processType, requestType) error { +func clearByRequestType(appName string, processType string, requestType string) error { noun := "limits" if requestType == "reserve" { noun = "reservation" From 3ab271f756b4dabd833a75d8c1ec2f556b557a23 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 18 Mar 2019 15:32:20 -0400 Subject: [PATCH 019/163] refactor: drop implicit returns --- plugins/resource/subcommands.go | 35 +++++++++++++++++---------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index cab48632a..a476df739 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -7,8 +7,8 @@ import ( ) // CommandLimit implements resource:limit -func CommandLimit(args []string, processType string, r Resource, global bool) (err error) { - appName, err = getAppName(args, global) +func CommandLimit(args []string, processType string, r Resource, global bool) error { + appName, err := getAppName(args, global) if err != nil { return err } @@ -17,8 +17,8 @@ func CommandLimit(args []string, processType string, r Resource, global bool) (e } // CommandLimitClear implements resource:limit-clear -func CommandLimitClear(args []string, processType string, global bool) (err error) { - appName, err = getAppName(args, global) +func CommandLimitClear(args []string, processType string, global bool) error { + appName, err := getAppName(args, global) if err != nil { return err } @@ -27,8 +27,8 @@ func CommandLimitClear(args []string, processType string, global bool) (err erro } // CommandReserve implements resource:reserve -func CommandReserve(args []string, processType string, r Resource, global bool) (err error) { - appName, err = getAppName(args, global) +func CommandReserve(args []string, processType string, r Resource, global bool) error { + appName, err := getAppName(args, global) if err != nil { return err } @@ -38,7 +38,7 @@ func CommandReserve(args []string, processType string, r Resource, global bool) // CommandReserveClear implements resource:reserve-clear func CommandReserveClear(args []string, processType string, global bool) error { - appName, err = getAppName(args, global) + appName, err := getAppName(args, global) if err != nil { return err } @@ -73,7 +73,7 @@ func clearByRequestType(appName string, processType string, requestType string) for _, key := range resources { property := propertyKey(processType, requestType, key) - err = common.PropertyDelete("resource", appName, property) + err := common.PropertyDelete("resource", appName, property) if err != nil { return err } @@ -81,7 +81,7 @@ func clearByRequestType(appName string, processType string, requestType string) return nil } -func setRequestType(appName string, processType string, r Resource, requestType string) (err error) { +func setRequestType(appName string, processType string, r Resource, requestType string) error { if len(processType) == 0 { processType = "_all_" } @@ -103,7 +103,8 @@ func setRequestType(appName string, processType string, r Resource, requestType } if !hasValues { - return reportRequestType(appName, processType, requestType) + reportRequestType(appName, processType, requestType) + return nil } noun := "limits" @@ -126,16 +127,16 @@ func setRequestType(appName string, processType string, r Resource, requestType } property := propertyKey(processType, requestType, key) - err = common.PropertyWrite("resource", appName, property, value) + err := common.PropertyWrite("resource", appName, property, value) if err != nil { - return + return err } } - return + return nil } -func reportRequestType(appName string, processType string, requestType string) (err error) { +func reportRequestType(appName string, processType string, requestType string) { noun := "limits" if requestType == "reserve" { noun = "reservation" @@ -165,14 +166,14 @@ func reportRequestType(appName string, processType string, requestType string) ( value := common.PropertyGet("resource", appName, property) common.LogVerbose(fmt.Sprintf("%v: %v", key, value)) } - return nil + return } func propertyKey(processType string, requestType string, key string) string { return fmt.Sprintf("%v.%v.%v", processType, requestType, key) } -func getAppName(args []string, global bool) (appName string, err error) { +func getAppName(args []string, global bool) (string, error) { appName := "_all_" if global { return appName, nil @@ -183,7 +184,7 @@ func getAppName(args []string, global bool) (appName string, err error) { } appName = args[0] - if err = common.VerifyAppName(appName); err != nil { + if err := common.VerifyAppName(appName); err != nil { return "", err } From 278a80cc2580144f16e61146dc4535833ea741dc Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 18 Mar 2019 15:41:17 -0400 Subject: [PATCH 020/163] feat: switch from _all_ to _default_ for process type This is a special process type that signifies it's the default value for everything. --- plugins/resource/subcommands.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index a476df739..040c9ef44 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -57,7 +57,7 @@ func clearByRequestType(appName string, processType string, requestType string) humanAppName = "default" } message := fmt.Sprintf("clearing %v %v", humanAppName, noun) - if processType != "_all_" { + if processType != "_default_" { message = fmt.Sprintf("%v (%v)", message, processType) } common.LogInfo2Quiet(message) @@ -83,7 +83,7 @@ func clearByRequestType(appName string, processType string, requestType string) func setRequestType(appName string, processType string, r Resource, requestType string) error { if len(processType) == 0 { - processType = "_all_" + processType = "_default_" } resources := map[string]string{ @@ -116,7 +116,7 @@ func setRequestType(appName string, processType string, r Resource, requestType message = fmt.Sprintf("Setting default resource %v", noun) } - if processType != "_all_" { + if processType != "_default_" { message = fmt.Sprintf("%v (%v)", message, processType) } common.LogInfo2Quiet(message) @@ -147,7 +147,7 @@ func reportRequestType(appName string, processType string, requestType string) { humanAppName = "default" } message := fmt.Sprintf("resource %v %v information", noun, humanAppName) - if processType != "_all_" { + if processType != "_default_" { message = fmt.Sprintf("%v (%v)", message, processType) } common.LogInfo2Quiet(message) From c9bbddcfcbbcad64dd9453afec77ba614598fc67 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 18 Mar 2019 16:01:19 -0400 Subject: [PATCH 021/163] refactor: drop global resource limits Global limits make it harder to reason about the inheritance in the system. Rather than support global limits, Dokku should provide primitives to automatically set them on an app at provision time, or explicitly set them for an app. Both of these are likely best suited for a higher-level provisioner, or a community plugin. --- docs/advanced-usage/resource-management.md | 10 +-- plugins/resource/src/commands/commands.go | 7 +- .../subcommands/limit-clear/limit-clear.go | 3 +- .../resource/src/subcommands/limit/limit.go | 3 +- .../reserve-clear/reserve-clear.go | 3 +- .../src/subcommands/reserve/reserve.go | 3 +- plugins/resource/subcommands.go | 80 +++++++++---------- 7 files changed, 52 insertions(+), 57 deletions(-) diff --git a/docs/advanced-usage/resource-management.md b/docs/advanced-usage/resource-management.md index 7725cefec..3ac2ef5f7 100644 --- a/docs/advanced-usage/resource-management.md +++ b/docs/advanced-usage/resource-management.md @@ -3,11 +3,11 @@ > New as of 0.15.0 ``` -dokku resource:limit [--global] [--process-type ] [RESOURCE_OPTS...] -dokku resource:reserve [--global] [--process-type ] [RESOURCE_OPTS...] -dokku resource:limit-clear [--process-type ] [] -dokku resource:reserve-clear [--process-type ] [] -dokku resource:report [] +resource:limit [--process-type ] [RESOURCE_OPTS...] # Limit resources for a given app/process-type combination +resource:limit-clear [--process-type ] # Limit resources for a given app/process-type combination +resource:report [] [] # Displays a resource report for one or more apps +resource:reserve [--process-type ] [RESOURCE_OPTS...] # Reserve resources for a given app/process-type combination +resource:reserve-clear [--process-type ] # Reserve resources for a given app/process-type combination ``` The resource plugin is meant to allow users to limit or reserve resources for a given app/process-type combination. diff --git a/plugins/resource/src/commands/commands.go b/plugins/resource/src/commands/commands.go index 5e13ed013..a998d631d 100644 --- a/plugins/resource/src/commands/commands.go +++ b/plugins/resource/src/commands/commands.go @@ -19,8 +19,11 @@ Manages resource settings for an app Additional commands:` helpContent = ` - resource:limit [--global] [--process-type process-type] [RESOURCE_OPTS...] , Limit resources for a given app/process-type combination - resource:reserve [--global] [--process-type process-type] [RESOURCE_OPTS...] , Reserve resources for a given app/process-type combination + resource:limit [--process-type ] [RESOURCE_OPTS...] , Limit resources for a given app/process-type combination + resource:limit-clear [--process-type ] , Limit resources for a given app/process-type combination + resource:report [] [], Displays a resource report for one or more apps + resource:reserve [--process-type ] [RESOURCE_OPTS...] , Reserve resources for a given app/process-type combination + resource:reserve-clear [--process-type ] , Reserve resources for a given app/process-type combination ` ) diff --git a/plugins/resource/src/subcommands/limit-clear/limit-clear.go b/plugins/resource/src/subcommands/limit-clear/limit-clear.go index 32000d6d6..b794dfc30 100644 --- a/plugins/resource/src/subcommands/limit-clear/limit-clear.go +++ b/plugins/resource/src/subcommands/limit-clear/limit-clear.go @@ -11,10 +11,9 @@ import ( func main() { args := flag.NewFlagSet("resource:limit-clear", flag.ExitOnError) processType := args.String("process-type", "", "process-type: A process type to clear") - global := args.Bool("global", false, "global: Whether to clear global settings or not") args.Parse(os.Args[2:]) - err := resource.CommandLimitClear(args.Args(), *processType, *global) + err := resource.CommandLimitClear(args.Args(), *processType) if err != nil { common.LogFail(err.Error()) } diff --git a/plugins/resource/src/subcommands/limit/limit.go b/plugins/resource/src/subcommands/limit/limit.go index 024835acd..d45438e3a 100644 --- a/plugins/resource/src/subcommands/limit/limit.go +++ b/plugins/resource/src/subcommands/limit/limit.go @@ -11,7 +11,6 @@ import ( func main() { args := flag.NewFlagSet("resource:limit", flag.ExitOnError) processType := args.String("process-type", "", "process-type: A process type to manage") - global := args.Bool("global", false, "global: Whether to clear global settings or not") cpu := args.String("cpu", "", "cpu: The amount of cpu to limit processes by") memory := args.String("memory", "", "memory: The amount of memory to limit processes by") memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to limit processes by") @@ -29,7 +28,7 @@ func main() { NetworkEgress: *networkEgress, } - err := resource.CommandLimit(args.Args(), *processType, resources, *global) + err := resource.CommandLimit(args.Args(), *processType, resources) if err != nil { common.LogFail(err.Error()) } diff --git a/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go b/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go index c27a02bba..4f882388d 100644 --- a/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go +++ b/plugins/resource/src/subcommands/reserve-clear/reserve-clear.go @@ -11,10 +11,9 @@ import ( func main() { args := flag.NewFlagSet("resource:reserve-clear", flag.ExitOnError) processType := args.String("process-type", "", "process-type: A process type to clear") - global := args.Bool("global", false, "global: Whether to clear global settings or not") args.Parse(os.Args[2:]) - err := resource.CommandReserveClear(args.Args(), *processType, *global) + err := resource.CommandReserveClear(args.Args(), *processType) if err != nil { common.LogFail(err.Error()) } diff --git a/plugins/resource/src/subcommands/reserve/reserve.go b/plugins/resource/src/subcommands/reserve/reserve.go index a62a34d55..afeccb551 100644 --- a/plugins/resource/src/subcommands/reserve/reserve.go +++ b/plugins/resource/src/subcommands/reserve/reserve.go @@ -11,7 +11,6 @@ import ( func main() { args := flag.NewFlagSet("resource:reserve", flag.ExitOnError) processType := args.String("process-type", "", "process-type: A process type to manage") - global := args.Bool("global", false, "global: Whether to clear global settings or not") cpu := args.String("cpu", "", "cpu: The amount of cpu to reserve for processes") memory := args.String("memory", "", "memory: The amount of memory to reserve for processes") memorySwap := args.String("memory-swap", "", "memory-swap: The amount of swap memory to reserve for processes") @@ -29,7 +28,7 @@ func main() { NetworkEgress: *networkEgress, } - err := resource.CommandReserve(args.Args(), *processType, resources, *global) + err := resource.CommandReserve(args.Args(), *processType, resources) if err != nil { common.LogFail(err.Error()) } diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index 040c9ef44..09a5dab73 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -7,8 +7,8 @@ import ( ) // CommandLimit implements resource:limit -func CommandLimit(args []string, processType string, r Resource, global bool) error { - appName, err := getAppName(args, global) +func CommandLimit(args []string, processType string, r Resource) error { + appName, err := getAppName(args) if err != nil { return err } @@ -17,8 +17,8 @@ func CommandLimit(args []string, processType string, r Resource, global bool) er } // CommandLimitClear implements resource:limit-clear -func CommandLimitClear(args []string, processType string, global bool) error { - appName, err := getAppName(args, global) +func CommandLimitClear(args []string, processType string) error { + appName, err := getAppName(args) if err != nil { return err } @@ -27,8 +27,8 @@ func CommandLimitClear(args []string, processType string, global bool) error { } // CommandReserve implements resource:reserve -func CommandReserve(args []string, processType string, r Resource, global bool) error { - appName, err := getAppName(args, global) +func CommandReserve(args []string, processType string, r Resource) error { + appName, err := getAppName(args) if err != nil { return err } @@ -37,8 +37,8 @@ func CommandReserve(args []string, processType string, r Resource, global bool) } // CommandReserveClear implements resource:reserve-clear -func CommandReserveClear(args []string, processType string, global bool) error { - appName, err := getAppName(args, global) +func CommandReserveClear(args []string, processType string) error { + appName, err := getAppName(args) if err != nil { return err } @@ -52,31 +52,40 @@ func clearByRequestType(appName string, processType string, requestType string) noun = "reservation" } - humanAppName := appName - if appName == "_all_" { - humanAppName = "default" - } - message := fmt.Sprintf("clearing %v %v", humanAppName, noun) - if processType != "_default_" { + message := fmt.Sprintf("clearing %v %v", appName, noun) + if processType != "_default_" && processType != "" { message = fmt.Sprintf("%v (%v)", message, processType) } common.LogInfo2Quiet(message) - resources := []string{ - "cpu", - "memory", - "memory-swap", - "network", - "network-ingress", - "network-egress", - } - - for _, key := range resources { - property := propertyKey(processType, requestType, key) - err := common.PropertyDelete("resource", appName, property) + if processType == "" { + resources, err := common.PropertyGetAll("resource", appName) if err != nil { return err } + for key, _ := range resources { + err := common.PropertyDelete("resource", appName, key) + if err != nil { + return err + } + } + } else { + resources := []string{ + "cpu", + "memory", + "memory-swap", + "network", + "network-ingress", + "network-egress", + } + + for _, key := range resources { + property := propertyKey(processType, requestType, key) + err := common.PropertyDelete("resource", appName, property) + if err != nil { + return err + } + } } return nil } @@ -112,10 +121,6 @@ func setRequestType(appName string, processType string, r Resource, requestType noun = "reservation" } message := fmt.Sprintf("Setting resource %v for %v", noun, appName) - if appName == "_all_" { - message = fmt.Sprintf("Setting default resource %v", noun) - } - if processType != "_default_" { message = fmt.Sprintf("%v (%v)", message, processType) } @@ -142,11 +147,7 @@ func reportRequestType(appName string, processType string, requestType string) { noun = "reservation" } - humanAppName := appName - if appName == "_all_" { - humanAppName = "default" - } - message := fmt.Sprintf("resource %v %v information", noun, humanAppName) + message := fmt.Sprintf("resource %v %v information", noun, appName) if processType != "_default_" { message = fmt.Sprintf("%v (%v)", message, processType) } @@ -173,17 +174,12 @@ func propertyKey(processType string, requestType string, key string) string { return fmt.Sprintf("%v.%v.%v", processType, requestType, key) } -func getAppName(args []string, global bool) (string, error) { - appName := "_all_" - if global { - return appName, nil - } - +func getAppName(args []string) (string, error) { if len(args) < 1 { return "", errors.New("Please specify an app to run the command on") } - appName = args[0] + appName := args[0] if err := common.VerifyAppName(appName); err != nil { return "", err } From e8134f9c2cd6ba70e2c1046d753e9f852ec0451e Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 18 Mar 2019 16:43:31 -0400 Subject: [PATCH 022/163] 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, "") +} From 00c9e5a5e9e6350531b2cf77e7afd7952fa4f79d Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 18 Mar 2019 18:21:44 -0400 Subject: [PATCH 023/163] feat: first pass at implement resource limitation on a pre-process basis --- docs/advanced-usage/resource-management.md | 2 + .../advanced-usage/schedulers/docker-local.md | 14 ++++ docs/development/plugin-triggers.md | 42 +++++++++++ plugins/resource/.gitignore | 2 + plugins/resource/Makefile | 5 +- .../docker-args-process-deploy.go | 74 +++++++++++++++++++ .../resource/src/triggers/report/report.go | 2 +- 7 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go diff --git a/docs/advanced-usage/resource-management.md b/docs/advanced-usage/resource-management.md index 3a30ede35..02227dd88 100644 --- a/docs/advanced-usage/resource-management.md +++ b/docs/advanced-usage/resource-management.md @@ -27,6 +27,8 @@ Valid resource options include: - `--network-ingress` - `--network-egress` +Resource limits and reservations are applied only during the `run` and `deploy` phases of an application, and will not impact the `build` phase of an application. + ### Resource Limits When specified and supported, a resource limit will ensure that your app does not go _over_ the specified value. If this occurs, the underlying scheduler may either cap resource utilization, or it may decide to terminate and reschedule your process. diff --git a/docs/advanced-usage/schedulers/docker-local.md b/docs/advanced-usage/schedulers/docker-local.md index 574486e36..2394d2029 100644 --- a/docs/advanced-usage/schedulers/docker-local.md +++ b/docs/advanced-usage/schedulers/docker-local.md @@ -53,3 +53,17 @@ This plugin implements various functionality through `plugn` triggers to integra - `scheduler-stop` - `scheduler-tags-create` - `scheduler-tags-destroy` + +## Supported Resource Management Properties + +The `docker-local` scheduler supports a minimal list of resource _limits_ and _reservations_. The following properties are supported: + +### Resource Limits + +- cpu: (docker option: `--cpus`), and is specified in number of CPUs a process can access. +- memory: (docker option: `--memory`) and should be specified with a suffix of `b` (bytes), `k` (kilobytes), `m` (megabytes), `g` (gigabytes) +- memory-swap: (docker option: `--memory-swap`) and should be specified with a suffix of `b` (bytes), `k` (kilobytes), `m` (megabytes), `g` (gigabytes) + +### Resource Reservations + +- memory: (docker option: `--memory-reservation`) and should be specified with a suffix of `b` (bytes), `k` (kilobytes), `m` (megabytes), `g` (gigabytes) diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 4f4abbf5e..2ed8b55f2 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -248,6 +248,8 @@ echo 'derp.dkr.ecr.us-east-1.amazonaws.com' ### `docker-args-build` +> Warning: Deprecated, please use `docker-args-process-build` instead + - Description: - Invoked by: `internal function dokku_build() (build phase)` - Arguments: `$APP $IMAGE_SOURCE_TYPE` @@ -271,6 +273,8 @@ echo -n "$STDIN$output" ### `docker-args-deploy` +> Warning: Deprecated, please use `docker-args-process-deploy` instead + - Description: - Invoked by: `dokku deploy` - Arguments: `$APP $IMAGE_TAG [$PROC_TYPE $CONTAINER_INDEX]` @@ -289,6 +293,8 @@ verify_app_name "$APP" ### `docker-args-run` +> Warning: Deprecated, please use `docker-args-process-run` instead + - Description: - Invoked by: `dokku run` - Arguments: `$APP $IMAGE_TAG` @@ -305,6 +311,42 @@ verify_app_name "$APP" # TODO ``` +### `docker-args-process-deploy` + +- Description: `$PROC_TYPE` may be set to magic `_all_` process type to signify global docker deploy options. +- Invoked by: `dokku deploy` +- Arguments: `$APP $IMAGE_TAG $IMAGE_SOURCE_TYPE [$PROC_TYPE $CONTAINER_INDEX]` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) +verify_app_name "$APP" + +# TODO +``` + +### `docker-args-process-run` + +- Description: `$PROC_TYPE` may be set to magic `_all_` process type to signify global docker run options. +- Invoked by: `dokku run` +- Arguments: `$APP $IMAGE_TAG $IMAGE_SOURCE_TYPE [$PROC_TYPE $CONTAINER_INDEX]` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" +APP="$1"; IMAGE_TAG="$2"; IMAGE=$(get_app_image_name $APP $IMAGE_TAG) +verify_app_name "$APP" + +# TODO +``` + ### `git-post-pull` - Description: diff --git a/plugins/resource/.gitignore b/plugins/resource/.gitignore index 56aebe3b7..efc010131 100644 --- a/plugins/resource/.gitignore +++ b/plugins/resource/.gitignore @@ -4,3 +4,5 @@ /install /post-delete /report +/docker-args-process-deploy +/docker-args-process-run diff --git a/plugins/resource/Makefile b/plugins/resource/Makefile index 71ef38f1f..2fcb2d81d 100644 --- a/plugins/resource/Makefile +++ b/plugins/resource/Makefile @@ -3,7 +3,7 @@ include ../../common.mk GO_ARGS ?= -a SUBCOMMANDS = subcommands/limit subcommands/limit-clear subcommands/report subcommands/reserve subcommands/reserve-clear -TRIGGERS = triggers/install triggers/post-delete triggers/report +TRIGGERS = triggers/docker-args-process-deploy 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 report + rm -rf commands subcommands triggers docker-args-process-deploy docker-args-process-run install post-delete report src-clean: rm -rf .gitignore src triggers vendor Makefile *.go @@ -35,3 +35,4 @@ triggers/%: src/triggers/*/%.go triggers-copy: cp triggers/* . + ln -s docker-args-process-deploy docker-args-process-run diff --git a/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go b/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go new file mode 100644 index 000000000..958f21f10 --- /dev/null +++ b/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go @@ -0,0 +1,74 @@ +package main + +import ( + "flag" + "fmt" + "io/ioutil" + "os" + "strings" + + "github.com/dokku/dokku/plugins/common" +) + +// outputs the process-specific docker options +func main() { + stdin, err := ioutil.ReadAll(os.Stdin) + if err != nil { + common.LogFail(err.Error()) + } + + flag.Parse() + appName := flag.Arg(0) + procType := flag.Arg(3) + + resources, err := common.PropertyGetAll("resource", appName) + if err != nil { + fmt.Print(string(stdin)) + common.LogFail(err.Error()) + } + + limits := make(map[string]string) + reservations := make(map[string]string) + + validLimits := map[string]bool{ + "cpu": true, + "memory": true, + "memory-swap": true, + } + validReservations := map[string]bool{ + "memory": true, + } + validPrefixes := []string{"_default_.", fmt.Sprintf("%s.", procType)} + for _, validPrefix := range validPrefixes { + for key, value := range resources { + if !strings.HasPrefix(key, validPrefix) { + continue + } + parts := strings.SplitN(strings.TrimPrefix(key, validPrefix), ".", 2) + if parts[0] == "limit" { + if !validLimits[parts[1]] { + continue + } + + limits[parts[1]] = value + } + if parts[0] == "reserve" { + if !validReservations[parts[1]] { + continue + } + + reservations[parts[1]] = value + } + } + } + + for key, value := range limits { + fmt.Print(" --%s=%s ", key, value) + } + + for key, value := range reservations { + fmt.Print(" --%s-reservation=%s ", key, value) + } + + fmt.Print(string(stdin)) +} diff --git a/plugins/resource/src/triggers/report/report.go b/plugins/resource/src/triggers/report/report.go index 94a73d62f..b34ad49e0 100644 --- a/plugins/resource/src/triggers/report/report.go +++ b/plugins/resource/src/triggers/report/report.go @@ -6,7 +6,7 @@ import ( "github.com/dokku/dokku/plugins/resource" ) -// displays a buildpacks report for one or more apps +// displays a resource report for one or more apps func main() { flag.Parse() appName := flag.Arg(0) From 8db3b8e333f0fa12e71c1facc4a4722de9591a07 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 22 Mar 2019 14:18:38 -0400 Subject: [PATCH 024/163] fix: do not barf if there are no resources specified for an application --- .../docker-args-process-deploy/docker-args-process-deploy.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go b/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go index 958f21f10..a28acb3f0 100644 --- a/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go +++ b/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go @@ -24,7 +24,7 @@ func main() { resources, err := common.PropertyGetAll("resource", appName) if err != nil { fmt.Print(string(stdin)) - common.LogFail(err.Error()) + return } limits := make(map[string]string) From 3f0126a4715fe37ac93fa093cb6f124a4a539485 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Fri, 22 Mar 2019 16:13:01 -0400 Subject: [PATCH 025/163] fix: do not output empty arguments Also ensure we use Printf instead of Print... --- .../docker-args-process-deploy.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go b/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go index a28acb3f0..5b92d85df 100644 --- a/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go +++ b/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go @@ -63,11 +63,17 @@ func main() { } for key, value := range limits { - fmt.Print(" --%s=%s ", key, value) + if value == "" { + continue + } + fmt.Printf(" --%s=%s ", key, value) } for key, value := range reservations { - fmt.Print(" --%s-reservation=%s ", key, value) + if value == "" { + continue + } + fmt.Printf(" --%s-reservation=%s ", key, value) } fmt.Print(string(stdin)) From 243d5ecc43ae950615c6c4a26f171e4b43fe7196 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 11:30:45 -0400 Subject: [PATCH 026/163] feat: integrate resource plugin with scheduler-docker-local --- docs/development/plugin-triggers.md | 16 ++ plugins/app-json/internal-functions | 4 + plugins/common/functions | 6 + .../scheduler-docker-local/scheduler-deploy | 3 + plugins/scheduler-docker-local/scheduler-run | 6 +- tests/unit/10_apps.bats | 4 +- tests/unit/20_resource.bats | 137 ++++++++++++++++++ 7 files changed, 173 insertions(+), 3 deletions(-) mode change 100755 => 100644 tests/unit/10_apps.bats create mode 100644 tests/unit/20_resource.bats diff --git a/docs/development/plugin-triggers.md b/docs/development/plugin-triggers.md index 2ed8b55f2..443e0d0be 100644 --- a/docs/development/plugin-triggers.md +++ b/docs/development/plugin-triggers.md @@ -1199,6 +1199,22 @@ fi plugn trigger receive-app $APP $newrev ``` +### `resource-get-property` + +- Description: Fetches a given resource property value +- Invoked by: +- Arguments: `$APP` `$PROC_TYPE` `$RESOURCE_TYPE` `$PROPERTY` +- Example: + +```shell +#!/usr/bin/env bash + +set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x +APP="$1"; PROC_TYPE="$2" RESOURCE_TYPE="$3" PROPERTY="$4" + +# TODO +``` + ### `retire-container-failed` - Description: Allows you to run commands if/when retiring old containers has failed diff --git a/plugins/app-json/internal-functions b/plugins/app-json/internal-functions index a3351df47..46feb92d1 100755 --- a/plugins/app-json/internal-functions +++ b/plugins/app-json/internal-functions @@ -83,6 +83,10 @@ execute_script() { # strip --restart args from DOCKER_ARGS local DOCKER_ARGS=$(sed -e "s/--restart=[[:graph:]]\+[[:blank:]]\?//g" <<<"$DOCKER_ARGS") + local IMAGE_SOURCE_TYPE="dockerfile" + is_image_herokuish_based "$IMAGE" && IMAGE_SOURCE_TYPE="herokuish" + DOCKER_ARGS+=$(: | plugn trigger docker-args-process-deploy "$APP" "$IMAGE_TAG" "$IMAGE_SOURCE_TYPE" "$PHASE_SCRIPT_KEY") + # eval args as array to respect escapes declare -a ARG_ARRAY eval "ARG_ARRAY=($DOCKER_ARGS)" diff --git a/plugins/common/functions b/plugins/common/functions index 0cf7fc690..c5e9af265 100755 --- a/plugins/common/functions +++ b/plugins/common/functions @@ -533,6 +533,9 @@ dokku_build() { local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$IMAGE_SOURCE_TYPE") [[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" -e TRACE=true " + local IMAGE_SOURCE_TYPE="herokuish" + DOCKER_ARGS+=$(: | plugn trigger docker-args-process-run "$APP" "$IMAGE_TAG" "$IMAGE_SOURCE_TYPE" "$PHASE_SCRIPT_KEY") + declare -a ARG_ARRAY eval "ARG_ARRAY=($DOCKER_ARGS)" # shellcheck disable=SC2086 @@ -558,6 +561,9 @@ dokku_build() { [[ "$DOKKU_DOCKERFILE_CACHE_BUILD" == "false" ]] && DOKKU_DOCKER_BUILD_OPTS="$DOKKU_DOCKER_BUILD_OPTS --no-cache" local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$IMAGE_SOURCE_TYPE") + local IMAGE_SOURCE_TYPE="dockerfile" + DOCKER_ARGS+=$(: | plugn trigger docker-args-process-run "$APP" "$IMAGE_TAG" "$IMAGE_SOURCE_TYPE" "$PHASE_SCRIPT_KEY") + # strip --volume and -v args from DOCKER_ARGS local DOCKER_ARGS=$(sed -e "s/--volume=[[:graph:]]\+[[:blank:]]\?//g" -e "s/-v[[:blank:]]\?[[:graph:]]\+[[:blank:]]\?//g" <<<"$DOCKER_ARGS") declare -a ARG_ARRAY diff --git a/plugins/scheduler-docker-local/scheduler-deploy b/plugins/scheduler-docker-local/scheduler-deploy index ef042625d..99f73a262 100755 --- a/plugins/scheduler-docker-local/scheduler-deploy +++ b/plugins/scheduler-docker-local/scheduler-deploy @@ -25,6 +25,8 @@ scheduler-docker-local-scheduler-deploy() { plugn trigger pre-deploy "$APP" "$IMAGE_TAG" is_image_herokuish_based "$IMAGE" && DOKKU_HEROKUISH=true + local IMAGE_SOURCE_TYPE="dockerfile" + [[ "$DOKKU_HEROKUISH" == "true" ]] && IMAGE_SOURCE_TYPE="herokuish" local DOKKU_SCALE_FILE="$DOKKU_ROOT/$APP/DOKKU_SCALE" local oldids=$(get_app_container_ids "$APP") @@ -69,6 +71,7 @@ scheduler-docker-local-scheduler-deploy() { local DOCKER_ARGS DOCKER_ARGS=$(: | plugn trigger docker-args-deploy "$APP" "$IMAGE_TAG" "$PROC_TYPE" "$CONTAINER_INDEX") DOCKER_ARGS+=" -e DYNO=$PROC_TYPE.$CONTAINER_INDEX " + DOCKER_ARGS+=$(: | plugn trigger docker-args-process-deploy "$APP" "$IMAGE_TAG" "$IMAGE_SOURCE_TYPE" "$PROC_TYPE" "$CONTAINER_INDEX") [[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" -e TRACE=true " declare -a ARG_ARRAY diff --git a/plugins/scheduler-docker-local/scheduler-run b/plugins/scheduler-docker-local/scheduler-run index c28318c60..0b3a228ff 100755 --- a/plugins/scheduler-docker-local/scheduler-run +++ b/plugins/scheduler-docker-local/scheduler-run @@ -27,6 +27,10 @@ scheduler-docker-local-scheduler-run() { local DOCKER_ARGS=$(: | plugn trigger docker-args-run "$APP" "$IMAGE_TAG") [[ "$DOKKU_TRACE" ]] && local DOCKER_ARGS+=" -e TRACE=true " + local IMAGE_SOURCE_TYPE="dockerfile" + is_image_herokuish_based "$IMAGE" && IMAGE_SOURCE_TYPE="herokuish" + DOCKER_ARGS+=$(: | plugn trigger docker-args-process-run "$APP" "$IMAGE_TAG" "$IMAGE_SOURCE_TYPE" "$PHASE_SCRIPT_KEY") + declare -a ARG_ARRAY eval "ARG_ARRAY=($DOCKER_ARGS)" @@ -38,7 +42,7 @@ scheduler-docker-local-scheduler-run() { fi has_tty && DOKKU_RUN_OPTS+=" -i -t" - is_image_herokuish_based "$IMAGE" && local EXEC_CMD="/exec" + [[ "$IMAGE_SOURCE_TYPE" == "herokuish" ]] && local EXEC_CMD="/exec" DOKKU_QUIET_OUTPUT=1 extract_procfile "$APP" diff --git a/tests/unit/10_apps.bats b/tests/unit/10_apps.bats old mode 100755 new mode 100644 index e92e07a2c..ccb4bbf44 --- a/tests/unit/10_apps.bats +++ b/tests/unit/10_apps.bats @@ -2,11 +2,11 @@ load test_helper -setup () { +setup() { global_setup } -teardown () { +teardown() { global_teardown } diff --git a/tests/unit/20_resource.bats b/tests/unit/20_resource.bats new file mode 100644 index 000000000..372fad236 --- /dev/null +++ b/tests/unit/20_resource.bats @@ -0,0 +1,137 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + global_setup + create_app +} + +teardown() { + destroy_app + global_teardown +} + +@test "(docker-options) resource:limit" { + run /bin/bash -c "dokku resource:limit $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_output_contains "resource limits $TEST_APP information" + + deploy_app + run /bin/bash -c "dokku resource:limit --memory 512MB $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "0" + + dokku ps:rebuild "$TEST_APP" + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "536870912" + + run /bin/bash -c "dokku resource:limit --memory 1024MB --process-type worker $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + dokku ps:rebuild "$TEST_APP" + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "536870912" + + run /bin/bash -c "dokku resource:limit-clear --process-type worker $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + dokku ps:rebuild "$TEST_APP" + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "536870912" + + run /bin/bash -c "dokku resource:limit-clear --process-type web $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + dokku ps:rebuild "$TEST_APP" + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "0" +} + +@test "(docker-options) resource:reserve" { + run /bin/bash -c "dokku resource:reserve $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_output_contains "resource reservation $TEST_APP information" + + deploy_app + run /bin/bash -c "dokku resource:reserve --memory 512MB $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "0" + + dokku ps:rebuild "$TEST_APP" + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "536870912" + + run /bin/bash -c "dokku resource:reserve --memory 1024MB --process-type worker $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + dokku ps:rebuild "$TEST_APP" + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "536870912" + + run /bin/bash -c "dokku resource:reserve-clear --process-type worker $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + dokku ps:rebuild "$TEST_APP" + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "536870912" + + run /bin/bash -c "dokku resource:reserve-clear --process-type web $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + dokku ps:rebuild "$TEST_APP" + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "0" +} From c2898ec040ef4a78352e69dc40e0b2c8c16e6e97 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 11:31:18 -0400 Subject: [PATCH 027/163] feat: add trigger to allow retrieving resource properties on the fly --- plugins/resource/.gitignore | 1 + plugins/resource/Makefile | 4 +-- plugins/resource/resource.go | 20 ++++++++++++++ .../resource-get-property.go | 26 +++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 plugins/resource/src/triggers/resource-get-property/resource-get-property.go diff --git a/plugins/resource/.gitignore b/plugins/resource/.gitignore index efc010131..bdb6a1395 100644 --- a/plugins/resource/.gitignore +++ b/plugins/resource/.gitignore @@ -6,3 +6,4 @@ /report /docker-args-process-deploy /docker-args-process-run +/resource-get-property diff --git a/plugins/resource/Makefile b/plugins/resource/Makefile index 2fcb2d81d..582d8d44b 100644 --- a/plugins/resource/Makefile +++ b/plugins/resource/Makefile @@ -3,7 +3,7 @@ include ../../common.mk GO_ARGS ?= -a SUBCOMMANDS = subcommands/limit subcommands/limit-clear subcommands/report subcommands/reserve subcommands/reserve-clear -TRIGGERS = triggers/docker-args-process-deploy triggers/install triggers/post-delete triggers/report +TRIGGERS = triggers/docker-args-process-deploy triggers/install triggers/post-delete triggers/report triggers/resource-get-property 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 docker-args-process-deploy docker-args-process-run install post-delete report + rm -rf commands subcommands triggers docker-args-process-deploy docker-args-process-run install post-delete report resource-get-property src-clean: rm -rf .gitignore src triggers vendor Makefile *.go diff --git a/plugins/resource/resource.go b/plugins/resource/resource.go index 0bf0e251a..0d8e0187f 100644 --- a/plugins/resource/resource.go +++ b/plugins/resource/resource.go @@ -1,6 +1,7 @@ package resource import ( + "errors" "fmt" "os" "reflect" @@ -66,6 +67,25 @@ func ReportSingleApp(appName, infoFlag string) { common.LogFail(fmt.Sprintf("Invalid flag passed, valid flags: %s", strings.Join(strkeys, ", "))) } +func GetResourceValue(appName string, procType string, resourceType string, prefix string) (string, error) { + resources, err := common.PropertyGetAll("resource", appName) + if err != nil { + return "", err + } + + for key, value := range resources { + parts := strings.SplitN(strings.TrimPrefix(key, prefix), ".", 2) + if parts[0] != resourceType { + return "", err + } + if parts[1] == prefix { + return value, nil + } + } + + return "", errors.New("No value found") +} + func times(str string, n int) (out string) { for i := 0; i < n; i++ { out += str diff --git a/plugins/resource/src/triggers/resource-get-property/resource-get-property.go b/plugins/resource/src/triggers/resource-get-property/resource-get-property.go new file mode 100644 index 000000000..d8b0759ba --- /dev/null +++ b/plugins/resource/src/triggers/resource-get-property/resource-get-property.go @@ -0,0 +1,26 @@ +package main + +import ( + "flag" + "fmt" + "os" + + "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/resource" +) + +// writes the resource property to stdout for a given app container +func main() { + flag.Parse() + appName := flag.Arg(0) + procType := flag.Arg(1) + resourceType := flag.Arg(1) + property := flag.Arg(1) + + value, err := resource.GetResourceValue(appName, procType, resourceType, property) + if err != nil { + common.LogFail(err.Error()) + } + + fmt.Fprintln(os.Stdout, value) +} From 1683095233f1ac5d9d6247927fd787679f6fa576 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 12:41:06 -0400 Subject: [PATCH 028/163] fix: try and decrease number of compiles during release process --- Makefile | 1 + deb.mk | 55 ++++++++------------------------------- package.mk | 35 +++++++++++++++++++++++++ plugins/resource/Makefile | 2 +- rpm.mk | 46 +++++--------------------------- 5 files changed, 54 insertions(+), 85 deletions(-) create mode 100644 package.mk diff --git a/Makefile b/Makefile index 40715fcdc..61718d955 100644 --- a/Makefile +++ b/Makefile @@ -32,6 +32,7 @@ include common.mk .PHONY: all apt-update install version copyfiles copyplugin man-db plugins dependencies sshcommand procfile-util plugn docker aufs stack count dokku-installer vagrant-acl-add vagrant-dokku go-build include tests.mk +include package.mk include deb.mk include rpm.mk include arch.mk diff --git a/deb.mk b/deb.mk index 6cc71853a..016139c4d 100644 --- a/deb.mk +++ b/deb.mk @@ -100,53 +100,20 @@ deb-herokuish: --license 'MIT License' \ . -deb-dokku: - rm -rf /tmp/tmp /tmp/build dokku_*_$(DOKKU_ARCHITECTURE).deb - mkdir -p /tmp/tmp /tmp/build +deb-dokku: /tmp/build-dokku/var/lib/dokku/GIT_REV + rm -f $(BUILD_DIRECTORY)/dokku_*_$(DOKKU_ARCHITECTURE).deb - cp -r debian /tmp/build/DEBIAN - mkdir -p /tmp/build/usr/share/bash-completion/completions - mkdir -p /tmp/build/usr/bin - mkdir -p /tmp/build/usr/share/doc/dokku - mkdir -p /tmp/build/usr/share/dokku/contrib - mkdir -p /tmp/build/usr/share/lintian/overrides - mkdir -p /tmp/build/usr/share/man/man1 - mkdir -p /tmp/build/var/lib/dokku/core-plugins/available - - cp dokku /tmp/build/usr/bin - cp LICENSE /tmp/build/usr/share/doc/dokku/copyright - cp contrib/bash-completion /tmp/build/usr/share/bash-completion/completions/dokku - find . -name ".DS_Store" -depth -exec rm {} \; - $(MAKE) go-build - cp common.mk /tmp/build/var/lib/dokku/core-plugins/common.mk - cp -r plugins/* /tmp/build/var/lib/dokku/core-plugins/available - find plugins/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | while read plugin; do cd /tmp/build/var/lib/dokku/core-plugins/available/$$plugin && if [ -e Makefile ]; then $(MAKE) src-clean; fi; done - find plugins/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | while read plugin; do touch /tmp/build/var/lib/dokku/core-plugins/available/$$plugin/.core; done - rm /tmp/build/var/lib/dokku/core-plugins/common.mk - $(MAKE) help2man - $(MAKE) addman - cp /usr/local/share/man/man1/dokku.1 /tmp/build/usr/share/man/man1/dokku.1 - gzip -9 /tmp/build/usr/share/man/man1/dokku.1 - cp contrib/dokku-installer.py /tmp/build/usr/share/dokku/contrib -ifeq ($(DOKKU_VERSION),master) - git describe --tags > /tmp/build/var/lib/dokku/VERSION -else - echo $(DOKKU_VERSION) > /tmp/build/var/lib/dokku/VERSION -endif - cat /tmp/build/var/lib/dokku/VERSION | cut -d '-' -f 1 | cut -d 'v' -f 2 > /tmp/build/var/lib/dokku/STABLE_VERSION + cat /tmp/build-dokku/var/lib/dokku/VERSION | cut -d '-' -f 1 | cut -d 'v' -f 2 > /tmp/build-dokku/var/lib/dokku/STABLE_VERSION ifneq (,$(findstring false,$(IS_RELEASE))) - sed -i.bak -e "s/^/`date +%s`:/" /tmp/build/var/lib/dokku/STABLE_VERSION && rm /tmp/build/var/lib/dokku/STABLE_VERSION.bak + sed -i.bak -e "s/^/`date +%s`:/" /tmp/build-dokku/var/lib/dokku/STABLE_VERSION && rm /tmp/build-dokku/var/lib/dokku/STABLE_VERSION.bak endif - rm -f /tmp/build/DEBIAN/lintian-overrides - cp debian/lintian-overrides /tmp/build/usr/share/lintian/overrides/dokku -ifdef DOKKU_GIT_REV - echo "$(DOKKU_GIT_REV)" > /tmp/build/var/lib/dokku/GIT_REV -else - git rev-parse HEAD > /tmp/build/var/lib/dokku/GIT_REV -endif - sed -i.bak "s/^Version: .*/Version: `cat /tmp/build/var/lib/dokku/STABLE_VERSION`/g" /tmp/build/DEBIAN/control && rm /tmp/build/DEBIAN/control.bak - dpkg-deb --build /tmp/build "$(BUILD_DIRECTORY)/dokku_`cat /tmp/build/var/lib/dokku/VERSION`_$(DOKKU_ARCHITECTURE).deb" - lintian "$(BUILD_DIRECTORY)/dokku_`cat /tmp/build/var/lib/dokku/VERSION`_$(DOKKU_ARCHITECTURE).deb" + + cp -r debian /tmp/build-dokku/DEBIAN + rm -f /tmp/build-dokku/DEBIAN/lintian-overrides + cp debian/lintian-overrides /tmp/build-dokku/usr/share/lintian/overrides/dokku + sed -i.bak "s/^Version: .*/Version: `cat /tmp/build-dokku/var/lib/dokku/STABLE_VERSION`/g" /tmp/build-dokku/DEBIAN/control && rm /tmp/build-dokku/DEBIAN/control.bak + dpkg-deb --build /tmp/build-dokku "$(BUILD_DIRECTORY)/dokku_`cat /tmp/build-dokku/var/lib/dokku/VERSION`_$(DOKKU_ARCHITECTURE).deb" + lintian "$(BUILD_DIRECTORY)/dokku_`cat /tmp/build-dokku/var/lib/dokku/VERSION`_$(DOKKU_ARCHITECTURE).deb" deb-dokku-update: rm -rf /tmp/dokku-update*.deb dokku-update*.deb diff --git a/package.mk b/package.mk new file mode 100644 index 000000000..2b9a3dc01 --- /dev/null +++ b/package.mk @@ -0,0 +1,35 @@ +/tmp/build-dokku/var/lib/dokku/GIT_REV: + mkdir -p /tmp/build-dokku + mkdir -p /tmp/build-dokku/usr/share/bash-completion/completions + mkdir -p /tmp/build-dokku/usr/bin + mkdir -p /tmp/build-dokku/usr/share/doc/dokku + mkdir -p /tmp/build-dokku/usr/share/dokku/contrib + mkdir -p /tmp/build-dokku/usr/share/lintian/overrides + mkdir -p /tmp/build-dokku/usr/share/man/man1 + mkdir -p /tmp/build-dokku/var/lib/dokku/core-plugins/available + + cp dokku /tmp/build-dokku/usr/bin + cp LICENSE /tmp/build-dokku/usr/share/doc/dokku/copyright + cp contrib/bash-completion /tmp/build-dokku/usr/share/bash-completion/completions/dokku + find . -name ".DS_Store" -depth -exec rm {} \; + $(MAKE) go-build + cp common.mk /tmp/build-dokku/var/lib/dokku/core-plugins/common.mk + cp -r plugins/* /tmp/build-dokku/var/lib/dokku/core-plugins/available + find plugins/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | while read plugin; do cd /tmp/build-dokku/var/lib/dokku/core-plugins/available/$$plugin && if [ -e Makefile ]; then $(MAKE) src-clean; fi; done + find plugins/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | while read plugin; do touch /tmp/build-dokku/var/lib/dokku/core-plugins/available/$$plugin/.core; done + rm /tmp/build-dokku/var/lib/dokku/core-plugins/common.mk + $(MAKE) help2man + $(MAKE) addman + cp /usr/local/share/man/man1/dokku.1 /tmp/build-dokku/usr/share/man/man1/dokku.1 + gzip -9 /tmp/build-dokku/usr/share/man/man1/dokku.1 + cp contrib/dokku-installer.py /tmp/build-dokku/usr/share/dokku/contrib +ifeq ($(DOKKU_VERSION),master) + git describe --tags > /tmp/build-dokku/var/lib/dokku/VERSION +else + echo $(DOKKU_VERSION) > /tmp/build-dokku/var/lib/dokku/VERSION +endif +ifdef DOKKU_GIT_REV + echo "$(DOKKU_GIT_REV)" > /tmp/build-dokku/var/lib/dokku/GIT_REV +else + git rev-parse HEAD > /tmp/build-dokku/var/lib/dokku/GIT_REV +endif diff --git a/plugins/resource/Makefile b/plugins/resource/Makefile index 582d8d44b..6859e0283 100644 --- a/plugins/resource/Makefile +++ b/plugins/resource/Makefile @@ -35,4 +35,4 @@ triggers/%: src/triggers/*/%.go triggers-copy: cp triggers/* . - ln -s docker-args-process-deploy docker-args-process-run + ln -sf docker-args-process-deploy docker-args-process-run diff --git a/rpm.mk b/rpm.mk index 9bb455a60..ddb7fe073 100644 --- a/rpm.mk +++ b/rpm.mk @@ -46,51 +46,17 @@ rpm-herokuish: --license 'MIT License' \ . -rpm-dokku: - rm -rf /tmp/tmp /tmp/build $(BUILD_DIRECTORY)/dokku_*_$(RPM_ARCHITECTURE).rpm - mkdir -p /tmp/tmp /tmp/build +rpm-dokku: /tmp/build-dokku/var/lib/dokku/GIT_REV + rm -f $(BUILD_DIRECTORY)/dokku_*_$(RPM_ARCHITECTURE).rpm - mkdir -p /tmp/build/usr/share/bash-completion/completions - mkdir -p /tmp/build/usr/bin - mkdir -p /tmp/build/usr/share/doc/dokku - mkdir -p /tmp/build/usr/share/dokku/contrib - mkdir -p /tmp/build/usr/share/lintian/overrides - mkdir -p /tmp/build/usr/share/man/man1 - mkdir -p /tmp/build/var/lib/dokku/core-plugins/available - - cp dokku /tmp/build/usr/bin - cp LICENSE /tmp/build/usr/share/doc/dokku/copyright - cp contrib/bash-completion /tmp/build/usr/share/bash-completion/completions/dokku - find . -name ".DS_Store" -depth -exec rm {} \; - $(MAKE) go-build - cp common.mk /tmp/build/var/lib/dokku/core-plugins/common.mk - cp -r plugins/* /tmp/build/var/lib/dokku/core-plugins/available - find plugins/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | while read plugin; do cd /tmp/build/var/lib/dokku/core-plugins/available/$$plugin && if [ -e Makefile ]; then $(MAKE) src-clean; fi; done - find plugins/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n' | while read plugin; do touch /tmp/build/var/lib/dokku/core-plugins/available/$$plugin/.core; done - rm /tmp/build/var/lib/dokku/core-plugins/common.mk - $(MAKE) help2man - $(MAKE) addman - cp /usr/local/share/man/man1/dokku.1 /tmp/build/usr/share/man/man1/dokku.1 - gzip -9 /tmp/build/usr/share/man/man1/dokku.1 - cp contrib/dokku-installer.py /tmp/build/usr/share/dokku/contrib -ifeq ($(DOKKU_VERSION),master) - git describe --tags > /tmp/build/var/lib/dokku/VERSION -else - echo $(DOKKU_VERSION) > /tmp/build/var/lib/dokku/VERSION -endif - cat /tmp/build/var/lib/dokku/VERSION | cut -d '-' -f 1 | cut -d 'v' -f 2 > /tmp/build/var/lib/dokku/STABLE_VERSION + cat /tmp/build-dokku/var/lib/dokku/VERSION | cut -d '-' -f 1 | cut -d 'v' -f 2 > /tmp/build-dokku/var/lib/dokku/STABLE_VERSION ifneq (,$(findstring false,$(IS_RELEASE))) - sed -i.bak -e "s/^/`date +%s`-/" /tmp/build/var/lib/dokku/STABLE_VERSION && rm /tmp/build/var/lib/dokku/STABLE_VERSION.bak -endif -ifdef DOKKU_GIT_REV - echo "$(DOKKU_GIT_REV)" > /tmp/build/var/lib/dokku/GIT_REV -else - git rev-parse HEAD > /tmp/build/var/lib/dokku/GIT_REV + sed -i.bak -e "s/^/`date +%s`-/" /tmp/build-dokku/var/lib/dokku/STABLE_VERSION && rm /tmp/build-dokku/var/lib/dokku/STABLE_VERSION.bak endif @echo "-> Creating rpm package" - VERSION=$$(cat /tmp/build/var/lib/dokku/STABLE_VERSION); \ - sudo fpm -t rpm -s dir -C /tmp/build -n dokku \ + VERSION=$$(cat /tmp/build-dokku/var/lib/dokku/STABLE_VERSION); \ + sudo fpm -t rpm -s dir -C /tmp/build-dokku -n dokku \ --version "$$VERSION" \ --architecture $(RPM_ARCHITECTURE) \ --package "$(BUILD_DIRECTORY)/$(DOKKU_RPM_PACKAGE_NAME)" \ From 71be7026d79e3ac7ef75644c850009715c2d006a Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 12:41:48 -0400 Subject: [PATCH 029/163] fix: correct GetResourceValue errors --- plugins/resource/resource.go | 30 ++++++++++++++++++++++++------ plugins/resource/subcommands.go | 18 ------------------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/plugins/resource/resource.go b/plugins/resource/resource.go index 0d8e0187f..cddb2cf9e 100644 --- a/plugins/resource/resource.go +++ b/plugins/resource/resource.go @@ -67,23 +67,24 @@ func ReportSingleApp(appName, infoFlag string) { common.LogFail(fmt.Sprintf("Invalid flag passed, valid flags: %s", strings.Join(strkeys, ", "))) } -func GetResourceValue(appName string, procType string, resourceType string, prefix string) (string, error) { +// Fetches a single value for a given app/process/request/key combination +func GetResourceValue(appName string, processType string, requestType string, key string) (string, error) { resources, err := common.PropertyGetAll("resource", appName) if err != nil { return "", err } + defaultValue := "" for key, value := range resources { - parts := strings.SplitN(strings.TrimPrefix(key, prefix), ".", 2) - if parts[0] != resourceType { - return "", err + if key == propertyKey("_default_", requestType, key) { + defaultValue = value } - if parts[1] == prefix { + if key == propertyKey(processType, requestType, key) { return value, nil } } - return "", errors.New("No value found") + return defaultValue, nil } func times(str string, n int) (out string) { @@ -97,3 +98,20 @@ func times(str string, n int) (out string) { func Right(str string, length int, pad string) string { return str + times(pad, length-len(str)) } + +func propertyKey(processType string, requestType string, key string) string { + return fmt.Sprintf("%v.%v.%v", processType, requestType, key) +} + +func getAppName(args []string) (string, error) { + if len(args) < 1 { + return "", errors.New("Please specify an app to run the command on") + } + + appName := args[0] + if err := common.VerifyAppName(appName); err != nil { + return "", err + } + + return appName, nil +} diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index 09a5dab73..2c0ced583 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -1,7 +1,6 @@ package resource import ( - "errors" "fmt" "github.com/dokku/dokku/plugins/common" ) @@ -169,20 +168,3 @@ func reportRequestType(appName string, processType string, requestType string) { } return } - -func propertyKey(processType string, requestType string, key string) string { - return fmt.Sprintf("%v.%v.%v", processType, requestType, key) -} - -func getAppName(args []string) (string, error) { - if len(args) < 1 { - return "", errors.New("Please specify an app to run the command on") - } - - appName := args[0] - if err := common.VerifyAppName(appName); err != nil { - return "", err - } - - return appName, nil -} From 0af8b2421d2585fff7b54d93fb27ee20e23b9ffd Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 12:42:25 -0400 Subject: [PATCH 030/163] fix: correct range call --- plugins/resource/subcommands.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index 2c0ced583..fcb90be0d 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -62,7 +62,7 @@ func clearByRequestType(appName string, processType string, requestType string) if err != nil { return err } - for key, _ := range resources { + for key := range resources { err := common.PropertyDelete("resource", appName, key) if err != nil { return err From d7c7511ea9fe4678cbef05c837b16b24e3d0b529 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 12:52:30 -0400 Subject: [PATCH 031/163] fix: correct comment on GetResourceValue --- plugins/resource/resource.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/resource/resource.go b/plugins/resource/resource.go index cddb2cf9e..fc6e17372 100644 --- a/plugins/resource/resource.go +++ b/plugins/resource/resource.go @@ -67,7 +67,7 @@ func ReportSingleApp(appName, infoFlag string) { common.LogFail(fmt.Sprintf("Invalid flag passed, valid flags: %s", strings.Join(strkeys, ", "))) } -// Fetches a single value for a given app/process/request/key combination +// GetResourceValue fetches a single value for a given app/process/request/key combination func GetResourceValue(appName string, processType string, requestType string, key string) (string, error) { resources, err := common.PropertyGetAll("resource", appName) if err != nil { From ff945c64fea700c796c2fc20a9d6ccd3f0209c72 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 13:18:28 -0400 Subject: [PATCH 032/163] fix: correct local test running --- tests.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests.mk b/tests.mk index 6638e6da6..d3b932782 100644 --- a/tests.mk +++ b/tests.mk @@ -71,7 +71,7 @@ endif ifneq ($(wildcard /etc/ssh/sshd_config),) ifeq ($(shell grep 22333 /etc/ssh/sshd_config),) sed --in-place "s:^Port 22:Port 22 \\nPort 22333:g" /etc/ssh/sshd_config - restart ssh + service ssh restart endif endif From 6cc6d5a33f8ed5e3d55f485cbb28dcdac200428b Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 13:22:40 -0400 Subject: [PATCH 033/163] fix: correct port application during local tests --- tests.mk | 1 + 1 file changed, 1 insertion(+) diff --git a/tests.mk b/tests.mk index d3b932782..16a6fb2fd 100644 --- a/tests.mk +++ b/tests.mk @@ -71,6 +71,7 @@ endif ifneq ($(wildcard /etc/ssh/sshd_config),) ifeq ($(shell grep 22333 /etc/ssh/sshd_config),) sed --in-place "s:^Port 22:Port 22 \\nPort 22333:g" /etc/ssh/sshd_config + sed --in-place "s:^#Port 22$:Port 22 \\nPort 22333:g" /etc/ssh/sshd_config service ssh restart endif endif From beef464cc19fe43500da20fb8e8a28309c7c3340 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 13:42:01 -0400 Subject: [PATCH 034/163] fix: only uncomment port for sshd-config --- tests.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests.mk b/tests.mk index 16a6fb2fd..c42dc22b3 100644 --- a/tests.mk +++ b/tests.mk @@ -69,11 +69,11 @@ else ifeq ($(shell grep dokku.me /root/.ssh/config),) endif ifneq ($(wildcard /etc/ssh/sshd_config),) + sed --in-place "s/^#Port 22$\/Port 22/g" /etc/ssh/sshd_config ifeq ($(shell grep 22333 /etc/ssh/sshd_config),) sed --in-place "s:^Port 22:Port 22 \\nPort 22333:g" /etc/ssh/sshd_config - sed --in-place "s:^#Port 22$:Port 22 \\nPort 22333:g" /etc/ssh/sshd_config - service ssh restart endif + service ssh restart endif @echo "-----> Installing SSH public key..." From 4a6d3c25a4ad4b8dfa786c7c34de9c7445950460 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 13:55:23 -0400 Subject: [PATCH 035/163] fix: do not crash when clearing properties --- plugins/resource/subcommands.go | 10 ++-------- tests/unit/20_resource.bats | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index fcb90be0d..568f0a769 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -63,10 +63,7 @@ func clearByRequestType(appName string, processType string, requestType string) return err } for key := range resources { - err := common.PropertyDelete("resource", appName, key) - if err != nil { - return err - } + common.PropertyDelete("resource", appName, key) } } else { resources := []string{ @@ -80,10 +77,7 @@ func clearByRequestType(appName string, processType string, requestType string) for _, key := range resources { property := propertyKey(processType, requestType, key) - err := common.PropertyDelete("resource", appName, property) - if err != nil { - return err - } + common.PropertyDelete("resource", appName, property) } } return nil diff --git a/tests/unit/20_resource.bats b/tests/unit/20_resource.bats index 372fad236..38b8729c8 100644 --- a/tests/unit/20_resource.bats +++ b/tests/unit/20_resource.bats @@ -135,3 +135,27 @@ teardown() { echo "status: $status" assert_output "0" } + +@test "(docker-options) resource:limit-clear" { + run /bin/bash -c "dokku resource:limit-clear $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku resource:limit-clear --process-type web $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success +} + +@test "(docker-options) resource:reserve-clear" { + run /bin/bash -c "dokku resource:reserve-clear $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + + run /bin/bash -c "dokku resource:reserve-clear --process-type web $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success +} From c41753c74777e17d66e04642f80ba397ede5816c Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 14:17:16 -0400 Subject: [PATCH 036/163] fix: clearing resources for web doesn't actually clear for everything --- tests/unit/20_resource.bats | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/unit/20_resource.bats b/tests/unit/20_resource.bats index 38b8729c8..26eaac973 100644 --- a/tests/unit/20_resource.bats +++ b/tests/unit/20_resource.bats @@ -66,6 +66,18 @@ teardown() { echo "status: $status" assert_success + dokku ps:rebuild "$TEST_APP" + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "536870912" + + run /bin/bash -c "dokku resource:limit-clear $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + dokku ps:rebuild "$TEST_APP" CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.Memory}}' $CID" @@ -128,6 +140,18 @@ teardown() { echo "status: $status" assert_success + dokku ps:rebuild "$TEST_APP" + CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) + run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" + echo "output: $output" + echo "status: $status" + assert_output "536870912" + + run /bin/bash -c "dokku resource:reserve-clear $TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + dokku ps:rebuild "$TEST_APP" CID=$(< $DOKKU_ROOT/$TEST_APP/CONTAINER.web.1) run /bin/bash -c "docker inspect --format '{{.HostConfig.MemoryReservation}}' $CID" From a4114bb29d3b8468e7497992353ed781067159d9 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 14:30:50 -0400 Subject: [PATCH 037/163] fix: clearing values has no returnable errors --- plugins/resource/subcommands.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index 568f0a769..82134fdbc 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -22,7 +22,8 @@ func CommandLimitClear(args []string, processType string) error { return err } - return clearByRequestType(appName, processType, "limit") + clearByRequestType(appName, processType, "limit") + return nil } // CommandReserve implements resource:reserve @@ -42,10 +43,11 @@ func CommandReserveClear(args []string, processType string) error { return err } - return clearByRequestType(appName, processType, "reserve") + clearByRequestType(appName, processType, "reserve") + return nil } -func clearByRequestType(appName string, processType string, requestType string) error { +func clearByRequestType(appName string, processType string, requestType string) { noun := "limits" if requestType == "reserve" { noun = "reservation" @@ -60,7 +62,7 @@ func clearByRequestType(appName string, processType string, requestType string) if processType == "" { resources, err := common.PropertyGetAll("resource", appName) if err != nil { - return err + return } for key := range resources { common.PropertyDelete("resource", appName, key) @@ -80,7 +82,6 @@ func clearByRequestType(appName string, processType string, requestType string) common.PropertyDelete("resource", appName, property) } } - return nil } func setRequestType(appName string, processType string, r Resource, requestType string) error { From b24697443a69bd819a58ad894630ef667c6d5428 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 15:18:46 -0400 Subject: [PATCH 038/163] fix: correct the name --- tests/unit/20_resource.bats | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/20_resource.bats b/tests/unit/20_resource.bats index 26eaac973..a8f92cc89 100644 --- a/tests/unit/20_resource.bats +++ b/tests/unit/20_resource.bats @@ -12,7 +12,7 @@ teardown() { global_teardown } -@test "(docker-options) resource:limit" { +@test "(resource) resource:limit" { run /bin/bash -c "dokku resource:limit $TEST_APP" echo "output: $output" echo "status: $status" @@ -86,7 +86,7 @@ teardown() { assert_output "0" } -@test "(docker-options) resource:reserve" { +@test "(resource) resource:reserve" { run /bin/bash -c "dokku resource:reserve $TEST_APP" echo "output: $output" echo "status: $status" @@ -160,7 +160,7 @@ teardown() { assert_output "0" } -@test "(docker-options) resource:limit-clear" { +@test "(resource) resource:limit-clear" { run /bin/bash -c "dokku resource:limit-clear $TEST_APP" echo "output: $output" echo "status: $status" @@ -172,7 +172,7 @@ teardown() { assert_success } -@test "(docker-options) resource:reserve-clear" { +@test "(resource) resource:reserve-clear" { run /bin/bash -c "dokku resource:reserve-clear $TEST_APP" echo "output: $output" echo "status: $status" From a4c30c2ba908f7231245fd6c83047009880365f3 Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 16:36:30 -0400 Subject: [PATCH 039/163] chore: standardize variable naming --- plugins/network/network.go | 20 ++++++------- .../network-compute-ports.go | 4 +-- .../network-get-ipaddr/network-get-ipaddr.go | 4 +-- .../network-get-port/network-get-port.go | 4 +-- .../network-write-ipaddr.go | 4 +-- .../network-write-port/network-write-port.go | 4 +-- plugins/resource/resource.go | 10 +++---- .../docker-args-process-deploy.go | 4 +-- .../resource-get-property.go | 8 +++--- plugins/resource/subcommands.go | 28 +++++++++---------- 10 files changed, 45 insertions(+), 45 deletions(-) diff --git a/plugins/network/network.go b/plugins/network/network.go index af44450d3..55a32c629 100644 --- a/plugins/network/network.go +++ b/plugins/network/network.go @@ -52,7 +52,7 @@ func BuildConfig(appName string) { if len(procParts) != 2 { continue } - procType := procParts[0] + processType := procParts[0] procCount, err := strconv.Atoi(procParts[1]) if err != nil { continue @@ -62,25 +62,25 @@ func BuildConfig(appName string) { for containerIndex < procCount { containerIndex++ containerIndexString := strconv.Itoa(containerIndex) - containerIDFile := fmt.Sprintf("%v/CONTAINER.%v.%v", appRoot, procType, containerIndex) + containerIDFile := fmt.Sprintf("%v/CONTAINER.%v.%v", appRoot, processType, containerIndex) containerID := common.ReadFirstLine(containerIDFile) if containerID == "" || !common.ContainerIsRunning(containerID) { continue } - ipAddress := GetContainerIpaddress(appName, procType, containerID) - port := GetContainerPort(appName, procType, isHerokuishContainer, containerID) + ipAddress := GetContainerIpaddress(appName, processType, containerID) + port := GetContainerPort(appName, processType, isHerokuishContainer, containerID) if ipAddress != "" { - _, err := sh.Command("plugn", "trigger", "network-write-ipaddr", appName, procType, containerIndexString, ipAddress).Output() + _, err := sh.Command("plugn", "trigger", "network-write-ipaddr", appName, processType, containerIndexString, ipAddress).Output() if err != nil { common.LogWarn(err.Error()) } } if port != "" { - _, err := sh.Command("plugn", "trigger", "network-write-port", appName, procType, containerIndexString, port).Output() + _, err := sh.Command("plugn", "trigger", "network-write-port", appName, processType, containerIndexString, port).Output() if err != nil { common.LogWarn(err.Error()) } @@ -90,8 +90,8 @@ func BuildConfig(appName string) { } // GetContainerIpaddress returns the ipaddr for a given app container -func GetContainerIpaddress(appName, procType, containerID string) (ipAddr string) { - if procType != "web" { +func GetContainerIpaddress(appName, processType, containerID string) (ipAddr string) { + if processType != "web" { return } @@ -109,8 +109,8 @@ func GetContainerIpaddress(appName, procType, containerID string) (ipAddr string } // GetContainerPort returns the port for a given app container -func GetContainerPort(appName, procType string, isHerokuishContainer bool, containerID string) (port string) { - if procType != "web" { +func GetContainerPort(appName, processType string, isHerokuishContainer bool, containerID string) (port string) { + if processType != "web" { return } diff --git a/plugins/network/src/triggers/network-compute-ports/network-compute-ports.go b/plugins/network/src/triggers/network-compute-ports/network-compute-ports.go index e83fc3620..b981c59b0 100644 --- a/plugins/network/src/triggers/network-compute-ports/network-compute-ports.go +++ b/plugins/network/src/triggers/network-compute-ports/network-compute-ports.go @@ -15,10 +15,10 @@ import ( func main() { flag.Parse() appName := flag.Arg(0) - procType := flag.Arg(1) + processType := flag.Arg(1) isHerokuishContainer := common.ToBool(flag.Arg(2)) - if procType != "web" { + if processType != "web" { return } diff --git a/plugins/network/src/triggers/network-get-ipaddr/network-get-ipaddr.go b/plugins/network/src/triggers/network-get-ipaddr/network-get-ipaddr.go index cfdb3bbfd..237526b64 100644 --- a/plugins/network/src/triggers/network-get-ipaddr/network-get-ipaddr.go +++ b/plugins/network/src/triggers/network-get-ipaddr/network-get-ipaddr.go @@ -12,9 +12,9 @@ import ( func main() { flag.Parse() appName := flag.Arg(0) - procType := flag.Arg(1) + processType := flag.Arg(1) containerID := flag.Arg(2) - ipAddress := network.GetContainerIpaddress(appName, procType, containerID) + ipAddress := network.GetContainerIpaddress(appName, processType, containerID) fmt.Fprintln(os.Stdout, ipAddress) } diff --git a/plugins/network/src/triggers/network-get-port/network-get-port.go b/plugins/network/src/triggers/network-get-port/network-get-port.go index 801bc88ff..389265da7 100644 --- a/plugins/network/src/triggers/network-get-port/network-get-port.go +++ b/plugins/network/src/triggers/network-get-port/network-get-port.go @@ -13,10 +13,10 @@ import ( func main() { flag.Parse() appName := flag.Arg(0) - procType := flag.Arg(1) + processType := flag.Arg(1) isHerokuishContainer := common.ToBool(flag.Arg(2)) containerID := flag.Arg(3) - port := network.GetContainerPort(appName, procType, isHerokuishContainer, containerID) + port := network.GetContainerPort(appName, processType, isHerokuishContainer, containerID) fmt.Fprintln(os.Stdout, port) } diff --git a/plugins/network/src/triggers/network-write-ipaddr/network-write-ipaddr.go b/plugins/network/src/triggers/network-write-ipaddr/network-write-ipaddr.go index b76ceb3af..3cf618120 100644 --- a/plugins/network/src/triggers/network-write-ipaddr/network-write-ipaddr.go +++ b/plugins/network/src/triggers/network-write-ipaddr/network-write-ipaddr.go @@ -13,7 +13,7 @@ import ( func main() { flag.Parse() appName := flag.Arg(0) - procType := flag.Arg(1) + processType := flag.Arg(1) containerIndex := flag.Arg(2) ip := flag.Arg(3) @@ -26,7 +26,7 @@ func main() { } appRoot := strings.Join([]string{common.MustGetEnv("DOKKU_ROOT"), appName}, "/") - filename := fmt.Sprintf("%v/IP.%v.%v", appRoot, procType, containerIndex) + filename := fmt.Sprintf("%v/IP.%v.%v", appRoot, processType, containerIndex) f, err := os.Create(filename) if err != nil { common.LogFail(err.Error()) diff --git a/plugins/network/src/triggers/network-write-port/network-write-port.go b/plugins/network/src/triggers/network-write-port/network-write-port.go index 2a8d45dff..e263cd786 100644 --- a/plugins/network/src/triggers/network-write-port/network-write-port.go +++ b/plugins/network/src/triggers/network-write-port/network-write-port.go @@ -13,7 +13,7 @@ import ( func main() { flag.Parse() appName := flag.Arg(0) - procType := flag.Arg(1) + processType := flag.Arg(1) containerIndex := flag.Arg(2) port := flag.Arg(3) @@ -26,7 +26,7 @@ func main() { } appRoot := strings.Join([]string{common.MustGetEnv("DOKKU_ROOT"), appName}, "/") - filename := fmt.Sprintf("%v/PORT.%v.%v", appRoot, procType, containerIndex) + filename := fmt.Sprintf("%v/PORT.%v.%v", appRoot, processType, containerIndex) f, err := os.Create(filename) if err != nil { common.LogFail(err.Error()) diff --git a/plugins/resource/resource.go b/plugins/resource/resource.go index fc6e17372..bd299e58c 100644 --- a/plugins/resource/resource.go +++ b/plugins/resource/resource.go @@ -68,7 +68,7 @@ func ReportSingleApp(appName, infoFlag string) { } // GetResourceValue fetches a single value for a given app/process/request/key combination -func GetResourceValue(appName string, processType string, requestType string, key string) (string, error) { +func GetResourceValue(appName string, processType string, resourceType string, key string) (string, error) { resources, err := common.PropertyGetAll("resource", appName) if err != nil { return "", err @@ -76,10 +76,10 @@ func GetResourceValue(appName string, processType string, requestType string, ke defaultValue := "" for key, value := range resources { - if key == propertyKey("_default_", requestType, key) { + if key == propertyKey("_default_", resourceType, key) { defaultValue = value } - if key == propertyKey(processType, requestType, key) { + if key == propertyKey(processType, resourceType, key) { return value, nil } } @@ -99,8 +99,8 @@ func Right(str string, length int, pad string) string { return str + times(pad, length-len(str)) } -func propertyKey(processType string, requestType string, key string) string { - return fmt.Sprintf("%v.%v.%v", processType, requestType, key) +func propertyKey(processType string, resourceType string, key string) string { + return fmt.Sprintf("%v.%v.%v", processType, resourceType, key) } func getAppName(args []string) (string, error) { diff --git a/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go b/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go index 5b92d85df..76dec47e6 100644 --- a/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go +++ b/plugins/resource/src/triggers/docker-args-process-deploy/docker-args-process-deploy.go @@ -19,7 +19,7 @@ func main() { flag.Parse() appName := flag.Arg(0) - procType := flag.Arg(3) + processType := flag.Arg(3) resources, err := common.PropertyGetAll("resource", appName) if err != nil { @@ -38,7 +38,7 @@ func main() { validReservations := map[string]bool{ "memory": true, } - validPrefixes := []string{"_default_.", fmt.Sprintf("%s.", procType)} + validPrefixes := []string{"_default_.", fmt.Sprintf("%s.", processType)} for _, validPrefix := range validPrefixes { for key, value := range resources { if !strings.HasPrefix(key, validPrefix) { diff --git a/plugins/resource/src/triggers/resource-get-property/resource-get-property.go b/plugins/resource/src/triggers/resource-get-property/resource-get-property.go index d8b0759ba..53d22effc 100644 --- a/plugins/resource/src/triggers/resource-get-property/resource-get-property.go +++ b/plugins/resource/src/triggers/resource-get-property/resource-get-property.go @@ -9,15 +9,15 @@ import ( "github.com/dokku/dokku/plugins/resource" ) -// writes the resource property to stdout for a given app container +// writes the resource key to stdout for a given app container func main() { flag.Parse() appName := flag.Arg(0) - procType := flag.Arg(1) + processType := flag.Arg(1) resourceType := flag.Arg(1) - property := flag.Arg(1) + key := flag.Arg(1) - value, err := resource.GetResourceValue(appName, procType, resourceType, property) + value, err := resource.GetResourceValue(appName, processType, resourceType, key) if err != nil { common.LogFail(err.Error()) } diff --git a/plugins/resource/subcommands.go b/plugins/resource/subcommands.go index 82134fdbc..f93a4672a 100644 --- a/plugins/resource/subcommands.go +++ b/plugins/resource/subcommands.go @@ -12,7 +12,7 @@ func CommandLimit(args []string, processType string, r Resource) error { return err } - return setRequestType(appName, processType, r, "limit") + return setResourceType(appName, processType, r, "limit") } // CommandLimitClear implements resource:limit-clear @@ -22,7 +22,7 @@ func CommandLimitClear(args []string, processType string) error { return err } - clearByRequestType(appName, processType, "limit") + clearByResourceType(appName, processType, "limit") return nil } @@ -33,7 +33,7 @@ func CommandReserve(args []string, processType string, r Resource) error { return err } - return setRequestType(appName, processType, r, "reserve") + return setResourceType(appName, processType, r, "reserve") } // CommandReserveClear implements resource:reserve-clear @@ -43,13 +43,13 @@ func CommandReserveClear(args []string, processType string) error { return err } - clearByRequestType(appName, processType, "reserve") + clearByResourceType(appName, processType, "reserve") return nil } -func clearByRequestType(appName string, processType string, requestType string) { +func clearByResourceType(appName string, processType string, resourceType string) { noun := "limits" - if requestType == "reserve" { + if resourceType == "reserve" { noun = "reservation" } @@ -78,13 +78,13 @@ func clearByRequestType(appName string, processType string, requestType string) } for _, key := range resources { - property := propertyKey(processType, requestType, key) + property := propertyKey(processType, resourceType, key) common.PropertyDelete("resource", appName, property) } } } -func setRequestType(appName string, processType string, r Resource, requestType string) error { +func setResourceType(appName string, processType string, r Resource, resourceType string) error { if len(processType) == 0 { processType = "_default_" } @@ -106,12 +106,12 @@ func setRequestType(appName string, processType string, r Resource, requestType } if !hasValues { - reportRequestType(appName, processType, requestType) + reportResourceType(appName, processType, resourceType) return nil } noun := "limits" - if requestType == "reserve" { + if resourceType == "reserve" { noun = "reservation" } message := fmt.Sprintf("Setting resource %v for %v", noun, appName) @@ -125,7 +125,7 @@ func setRequestType(appName string, processType string, r Resource, requestType common.LogVerbose(fmt.Sprintf("%v: %v", key, value)) } - property := propertyKey(processType, requestType, key) + property := propertyKey(processType, resourceType, key) err := common.PropertyWrite("resource", appName, property, value) if err != nil { return err @@ -135,9 +135,9 @@ func setRequestType(appName string, processType string, r Resource, requestType return nil } -func reportRequestType(appName string, processType string, requestType string) { +func reportResourceType(appName string, processType string, resourceType string) { noun := "limits" - if requestType == "reserve" { + if resourceType == "reserve" { noun = "reservation" } @@ -157,7 +157,7 @@ func reportRequestType(appName string, processType string, requestType string) { } for _, key := range resources { - property := propertyKey(processType, requestType, key) + property := propertyKey(processType, resourceType, key) value := common.PropertyGet("resource", appName, property) common.LogVerbose(fmt.Sprintf("%v: %v", key, value)) } From 642c293d5aa4ec62ade40b085a0f57d075e204ae Mon Sep 17 00:00:00 2001 From: Jose Diaz-Gonzalez Date: Mon, 8 Apr 2019 18:03:29 -0400 Subject: [PATCH 040/163] Release 0.15.0 # History ## 0.15.0 Install/update via the bootstrap script: ```shell wget https://raw.githubusercontent.com/dokku/dokku/v0.15.0/bootstrap.sh sudo DOKKU_TAG=v0.15.0 bash bootstrap.sh ``` ### Bug Fixes - #3479: @josegonzalez Turn off logging for nginx validate configuration - #3470: @josegonzalez fix: correct the argument for get_release_cmd ### New Features - #3469: @josegonzalez Resource management - #3466: @josegonzalez Quieter builds - #3467: @josegonzalez feat: update golang in use for binary building - #3465: @josegonzalez Vagrant VM Enhancements - #3464: @josegonzalez Upgrade to go-procfile-util version 0.6.0 - #3463: @josegonzalez Implement version flags - #3462: @josegonzalez Upgrade procfile-util - #3449: @josegonzalez Only override the `WORKDIR` in copy_from_image if the image is `gliderlabs/herokuish` based - #3461: @josegonzalez Allow skipping aws releases for plugins - #3459: @josegonzalez Upgrade to herokuish:0.4.9 - #3413: @josegonzalez Implement buildpacks plugin ### Documentation - #3482: @multikatt rails-database -> railsdatabase - #3476: @lazyatom Add chrome plugin to documentation - #3468: @josegonzalez Doc cleanup ### Other - #3471: @josegonzalez chore: drop plugn package building --- HISTORY.md | 38 ++++++++++++++++ README.md | 4 +- contrib/dokku-installer.py | 2 +- contrib/release-dokku | 2 +- debian/control | 2 +- docs/advanced-usage/plugin-management.md | 50 +++++++++++----------- docs/assets/favicons/browserconfig.xml | 8 ++-- docs/assets/favicons/manifest.json | 12 +++--- docs/assets/style.css | 4 +- docs/assets/versions.json | 25 +++++------ docs/development/release-process.md | 2 +- docs/getting-started/installation.md | 4 +- docs/home.html | 40 ++++++++--------- docs/template.html | 36 ++++++++-------- plugins/00_dokku-standard/plugin.toml | 2 +- plugins/20_events/plugin.toml | 2 +- plugins/app-json/plugin.toml | 2 +- plugins/apps/plugin.toml | 2 +- plugins/build-env/plugin.toml | 2 +- plugins/buildpacks/plugin.toml | 2 +- plugins/certs/plugin.toml | 2 +- plugins/checks/plugin.toml | 2 +- plugins/common/plugin.toml | 2 +- plugins/config/plugin.toml | 2 +- plugins/docker-options/plugin.toml | 2 +- plugins/domains/plugin.toml | 2 +- plugins/enter/plugin.toml | 2 +- plugins/git/plugin.toml | 2 +- plugins/logs/plugin.toml | 2 +- plugins/network/plugin.toml | 2 +- plugins/nginx-vhosts/plugin.toml | 2 +- plugins/plugin/plugin.toml | 2 +- plugins/proxy/plugin.toml | 2 +- plugins/ps/plugin.toml | 2 +- plugins/repo/plugin.toml | 2 +- plugins/resource/plugin.toml | 2 +- plugins/scheduler-docker-local/plugin.toml | 2 +- plugins/shell/plugin.toml | 2 +- plugins/ssh-keys/plugin.toml | 2 +- plugins/storage/plugin.toml | 2 +- plugins/tags/plugin.toml | 2 +- plugins/tar/plugin.toml | 2 +- 42 files changed, 162 insertions(+), 123 deletions(-) diff --git a/HISTORY.md b/HISTORY.md index 0dcc7c5d1..b1153a018 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,43 @@ # History +## 0.15.0 + +Install/update via the bootstrap script: + +```shell +wget https://raw.githubusercontent.com/dokku/dokku/v0.15.0/bootstrap.sh +sudo DOKKU_TAG=v0.15.0 bash bootstrap.sh +``` + +### Bug Fixes + +- #3479: @josegonzalez Turn off logging for nginx validate configuration +- #3470: @josegonzalez fix: correct the argument for get_release_cmd + +### New Features + +- #3469: @josegonzalez Resource management +- #3466: @josegonzalez Quieter builds +- #3467: @josegonzalez feat: update golang in use for binary building +- #3465: @josegonzalez Vagrant VM Enhancements +- #3464: @josegonzalez Upgrade to go-procfile-util version 0.6.0 +- #3463: @josegonzalez Implement version flags +- #3462: @josegonzalez Upgrade procfile-util +- #3449: @josegonzalez Only override the `WORKDIR` in copy_from_image if the image is `gliderlabs/herokuish` based +- #3461: @josegonzalez Allow skipping aws releases for plugins +- #3459: @josegonzalez Upgrade to herokuish:0.4.9 +- #3413: @josegonzalez Implement buildpacks plugin + +### Documentation + +- #3482: @multikatt rails-database -> railsdatabase +- #3476: @lazyatom Add chrome plugin to documentation +- #3468: @josegonzalez Doc cleanup + +### Other + +- #3471: @josegonzalez chore: drop plugn package building + ## 0.14.6 Install/update via the bootstrap script: diff --git a/README.md b/README.md index 02e3f97b0..03223d64b 100644 --- a/README.md +++ b/README.md @@ -98,8 +98,8 @@ A fresh VM running any of the following operating systems: To install the latest stable release, run the following commands as a user who has access to `sudo`: ```shell -wget https://raw.githubusercontent.com/dokku/dokku/v0.14.6/bootstrap.sh -sudo DOKKU_TAG=v0.14.6 bash bootstrap.sh +wget https://raw.githubusercontent.com/dokku/dokku/v0.15.0/bootstrap.sh +sudo DOKKU_TAG=v0.15.0 bash bootstrap.sh ``` You can then proceed to the ip address or domain name associated with your server to complete the web-based installation. diff --git a/contrib/dokku-installer.py b/contrib/dokku-installer.py index afd791b67..8fbd4818c 100755 --- a/contrib/dokku-installer.py +++ b/contrib/dokku-installer.py @@ -10,7 +10,7 @@ import subprocess import sys import threading -VERSION = 'v0.14.6' +VERSION = 'v0.15.0' hostname = '' try: diff --git a/contrib/release-dokku b/contrib/release-dokku index fcb6be588..f88f01a92 100755 --- a/contrib/release-dokku +++ b/contrib/release-dokku @@ -219,7 +219,7 @@ fn-repo-update() { if [[ "$RELEASE" == 'patch' ]]; then fn-replace-version "$CURRENT_VERSION" "$NEXT_VERSION" docs/assets/versions.json else - versions=$(python -c 'import json,sys;d=json.load(sys.stdin);d["max-versions"].append("'"${NEXT_VERSION}"'"); print json.dumps(d, indent=2, sort_keys=True)' docs/assets/versions.json git add docs/assets/versions.json fi diff --git a/debian/control b/debian/control index 31f00b7d9..e2b70f4c9 100644 --- a/debian/control +++ b/debian/control @@ -1,5 +1,5 @@ Package: dokku -Version: 0.14.6 +Version: 0.15.0 Section: web Priority: optional Architecture: amd64 diff --git a/docs/advanced-usage/plugin-management.md b/docs/advanced-usage/plugin-management.md index 636b26c07..883794abe 100644 --- a/docs/advanced-usage/plugin-management.md +++ b/docs/advanced-usage/plugin-management.md @@ -31,31 +31,31 @@ dokku plugin ``` plugn: dev - 00_dokku-standard 0.14.6 enabled dokku core standard plugin - 20_events 0.14.6 enabled dokku core events logging plugin - apps 0.14.6 enabled dokku core apps plugin - build-env 0.14.6 enabled dokku core build-env plugin - certs 0.14.6 enabled dokku core certificate management plugin - checks 0.14.6 enabled dokku core checks plugin - common 0.14.6 enabled dokku core common plugin - config 0.14.6 enabled dokku core config plugin - docker-options 0.14.6 enabled dokku core docker-options plugin - domains 0.14.6 enabled dokku core domains plugin - enter 0.14.6 enabled dokku core enter plugin - git 0.14.6 enabled dokku core git plugin - logs 0.14.6 enabled dokku core logs plugin - named-containers 0.14.6 enabled dokku core named containers plugin - network 0.14.6 enabled dokku core network plugin - nginx-vhosts 0.14.6 enabled dokku core nginx-vhosts plugin - plugin 0.14.6 enabled dokku core plugin plugin - proxy 0.14.6 enabled dokku core proxy plugin - ps 0.14.6 enabled dokku core ps plugin - repo 0.14.6 enabled dokku core repo plugin - shell 0.14.6 enabled dokku core shell plugin - ssh-keys 0.14.6 enabled dokku core ssh-keys plugin - storage 0.14.6 enabled dokku core storage plugin - tags 0.14.6 enabled dokku core tags plugin - tar 0.14.6 enabled dokku core tar plugin + 00_dokku-standard 0.15.0 enabled dokku core standard plugin + 20_events 0.15.0 enabled dokku core events logging plugin + apps 0.15.0 enabled dokku core apps plugin + build-env 0.15.0 enabled dokku core build-env plugin + certs 0.15.0 enabled dokku core certificate management plugin + checks 0.15.0 enabled dokku core checks plugin + common 0.15.0 enabled dokku core common plugin + config 0.15.0 enabled dokku core config plugin + docker-options 0.15.0 enabled dokku core docker-options plugin + domains 0.15.0 enabled dokku core domains plugin + enter 0.15.0 enabled dokku core enter plugin + git 0.15.0 enabled dokku core git plugin + logs 0.15.0 enabled dokku core logs plugin + named-containers 0.15.0 enabled dokku core named containers plugin + network 0.15.0 enabled dokku core network plugin + nginx-vhosts 0.15.0 enabled dokku core nginx-vhosts plugin + plugin 0.15.0 enabled dokku core plugin plugin + proxy 0.15.0 enabled dokku core proxy plugin + ps 0.15.0 enabled dokku core ps plugin + repo 0.15.0 enabled dokku core repo plugin + shell 0.15.0 enabled dokku core shell plugin + ssh-keys 0.15.0 enabled dokku core ssh-keys plugin + storage 0.15.0 enabled dokku core storage plugin + tags 0.15.0 enabled dokku core tags plugin + tar 0.15.0 enabled dokku core tar plugin ``` Installing a plugin is easy as well using the `plugin:install` command. This command will also trigger the `install` pluginhook on all existing plugins. diff --git a/docs/assets/favicons/browserconfig.xml b/docs/assets/favicons/browserconfig.xml index b1641bf1c..10846ab7d 100644 --- a/docs/assets/favicons/browserconfig.xml +++ b/docs/assets/favicons/browserconfig.xml @@ -2,10 +2,10 @@ - - - - + + + + #da532c diff --git a/docs/assets/favicons/manifest.json b/docs/assets/favicons/manifest.json index aa374f086..9730cc718 100644 --- a/docs/assets/favicons/manifest.json +++ b/docs/assets/favicons/manifest.json @@ -2,37 +2,37 @@ "name": "Dokku", "icons": [ { - "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.14.6\/docs\/assets\/favicons\/android-chrome-36x36.png", + "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.15.0\/docs\/assets\/favicons\/android-chrome-36x36.png", "sizes": "36x36", "type": "image\/png", "density": "0.75" }, { - "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.14.6\/docs\/assets\/favicons\/android-chrome-48x48.png", + "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.15.0\/docs\/assets\/favicons\/android-chrome-48x48.png", "sizes": "48x48", "type": "image\/png", "density": "1.0" }, { - "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.14.6\/docs\/assets\/favicons\/android-chrome-72x72.png", + "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.15.0\/docs\/assets\/favicons\/android-chrome-72x72.png", "sizes": "72x72", "type": "image\/png", "density": "1.5" }, { - "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.14.6\/docs\/assets\/favicons\/android-chrome-96x96.png", + "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.15.0\/docs\/assets\/favicons\/android-chrome-96x96.png", "sizes": "96x96", "type": "image\/png", "density": "2.0" }, { - "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.14.6\/docs\/assets\/favicons\/android-chrome-144x144.png", + "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.15.0\/docs\/assets\/favicons\/android-chrome-144x144.png", "sizes": "144x144", "type": "image\/png", "density": "3.0" }, { - "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.14.6\/docs\/assets\/favicons\/android-chrome-192x192.png", + "src": "https:\/\/cdn.rawgit.com\/progrium\/dokku\/v0.15.0\/docs\/assets\/favicons\/android-chrome-192x192.png", "sizes": "192x192", "type": "image\/png", "density": "4.0" diff --git a/docs/assets/style.css b/docs/assets/style.css index b85b033af..0fcff8835 100644 --- a/docs/assets/style.css +++ b/docs/assets/style.css @@ -39,13 +39,13 @@ h1 { font-size: 1.6em; } .header .navbar-brand a { - background-image: url(https://cdn.jsdelivr.net/gh/dokku/dokku@v0.14.6/docs/assets/dokku.png); + background-image: url(https://cdn.jsdelivr.net/gh/dokku/dokku@v0.15.0/docs/assets/dokku.png); text-indent: 40px; } .blurb { color: #424242; background-color: #ededed; - background-image: url(https://cdn.jsdelivr.net/gh/dokku/dokku@v0.14.6/docs/assets/gplaypattern.png); + background-image: url(https://cdn.jsdelivr.net/gh/dokku/dokku@v0.15.0/docs/assets/gplaypattern.png); padding: 45px 0; text-align: center; } diff --git a/docs/assets/versions.json b/docs/assets/versions.json index 9cdd57b90..d9210e3d7 100644 --- a/docs/assets/versions.json +++ b/docs/assets/versions.json @@ -1,16 +1,17 @@ { "max-versions": [ - "0.3.26", - "0.4.14", - "0.5.8", - "0.6.5", - "0.7.2", - "0.8.2", - "0.9.4", - "0.10.5", - "0.11.6", - "0.12.13", - "0.13.4", - "0.14.6" + "0.3.26", + "0.4.14", + "0.5.8", + "0.6.5", + "0.7.2", + "0.8.2", + "0.9.4", + "0.10.5", + "0.11.6", + "0.12.13", + "0.13.4", + "0.14.6", + "0.15.0" ] } diff --git a/docs/development/release-process.md b/docs/development/release-process.md index 443ee1ba9..187f0547b 100644 --- a/docs/development/release-process.md +++ b/docs/development/release-process.md @@ -29,7 +29,7 @@ The workflow looks like this: ```shell # having dokku-arch in ../dokku-arch vagrant up build-arch -# wait for "==> build-arch: ==> Finished making: dokku 0.14.6-2 (Mon Feb 22 23:20:37 CET 2016)" +# wait for "==> build-arch: ==> Finished making: dokku 0.15.0-2 (Mon Feb 22 23:20:37 CET 2016)" cd ../dokku-arch git add PKGBUILD .SRCINFO git commit -m 'Update to dokku 0.9.9' diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 4b73d51fe..8b3cd55d6 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -21,8 +21,8 @@ To install the latest stable version of Dokku, you can run the following shell c ```shell # for debian systems, installs Dokku via apt-get -wget https://raw.githubusercontent.com/dokku/dokku/v0.14.6/bootstrap.sh; -sudo DOKKU_TAG=v0.14.6 bash bootstrap.sh +wget https://raw.githubusercontent.com/dokku/dokku/v0.15.0/bootstrap.sh; +sudo DOKKU_TAG=v0.15.0 bash bootstrap.sh ``` The installation process takes about 5-10 minutes, depending upon internet connection speed. diff --git a/docs/home.html b/docs/home.html index 5325f1e7b..a9d0c9266 100644 --- a/docs/home.html +++ b/docs/home.html @@ -10,30 +10,30 @@ Dokku - The smallest PaaS implementation you've ever seen - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - + + - + + + + + + +
+

We're sorry, but something went wrong.

+

If you are the application owner check the logs for more information.

+
+ + diff --git a/plugins/nginx-vhosts/templates/nginx.conf.sigil b/plugins/nginx-vhosts/templates/nginx.conf.sigil index 360ba4a6a..e67ba75e3 100644 --- a/plugins/nginx-vhosts/templates/nginx.conf.sigil +++ b/plugins/nginx-vhosts/templates/nginx.conf.sigil @@ -105,11 +105,17 @@ server { internal; } - error_page 500 501 502 503 504 505 506 507 508 509 510 511 /500-error.html; + error_page 500 501 503 504 505 506 507 508 509 510 511 /500-error.html; location /500-error.html { root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; internal; } + + error_page 502 /502-error.html; + location /502-error.html { + root {{ $.DOKKU_LIB_ROOT }}/data/nginx-vhosts/dokku-errors; + internal; + } } {{ end }}{{ end }} From 75036dfa2752e6e3fa3d808a4bb105b8732a0fdc Mon Sep 17 00:00:00 2001 From: Alexy Mikhailichenko Date: Mon, 1 Jul 2019 14:24:36 -0400 Subject: [PATCH 156/163] Remove console calls --- plugins/nginx-vhosts/templates/502-error.html | 1 - 1 file changed, 1 deletion(-) diff --git a/plugins/nginx-vhosts/templates/502-error.html b/plugins/nginx-vhosts/templates/502-error.html index 0390baaa8..6b6693cee 100644 --- a/plugins/nginx-vhosts/templates/502-error.html +++ b/plugins/nginx-vhosts/templates/502-error.html @@ -43,7 +43,6 @@ retry_current *= 2; } - console.dir('Trying', window.location.href, 'in', retry_current, 'seconds...'); setTimeout(check_gateway, retry_current * 1000); } }; From a496ae987af4fcbb36fc5c202ce78d5a2961ac13 Mon Sep 17 00:00:00 2001 From: Alexy Mikhailichenko Date: Mon, 1 Jul 2019 17:10:45 -0400 Subject: [PATCH 157/163] Remove console calls --- plugins/nginx-vhosts/templates/502-error.html | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugins/nginx-vhosts/templates/502-error.html b/plugins/nginx-vhosts/templates/502-error.html index 6b6693cee..fba8b3e04 100644 --- a/plugins/nginx-vhosts/templates/502-error.html +++ b/plugins/nginx-vhosts/templates/502-error.html @@ -34,8 +34,6 @@ request.onload = function () { - console.dir(window.location.href, 'returned', request.status); - if (request.status >= 200 && request.status < 400) { window.location.reload(true); } else { From c99f92e9f2cf86fadc748d85efd219e7174e1532 Mon Sep 17 00:00:00 2001 From: Alexy Mikhailichenko Date: Mon, 1 Jul 2019 17:17:14 -0400 Subject: [PATCH 158/163] Change 500 to 502 --- plugins/nginx-vhosts/templates/502-error.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/nginx-vhosts/templates/502-error.html b/plugins/nginx-vhosts/templates/502-error.html index fba8b3e04..45640345d 100644 --- a/plugins/nginx-vhosts/templates/502-error.html +++ b/plugins/nginx-vhosts/templates/502-error.html @@ -1,7 +1,7 @@ - We're sorry, but something went wrong (500) + We're sorry, but something went wrong (502)