From 294d4b1f2936981925f0852e2a9b7570aabee23c Mon Sep 17 00:00:00 2001 From: Alex Quick Date: Mon, 16 Jan 2017 23:13:32 -0500 Subject: [PATCH] [config] first cut of config rewritten in golang still to do: * Rework disk format to be okay with quotes * Fix Dockerfile deployments * Dedup code in the subcommands * Add --encode * Add deploytests for the new stuff * Restore some log messages (should I make some helpers in common?) * Restore some functions that may be used by 3d-party plugins --- plugins/config/.gitignore | 2 + plugins/config/Makefile | 32 ++ plugins/config/commands | 33 -- plugins/config/functions | 243 +------------- plugins/config/src/commands/commands.go | 66 ++++ plugins/config/src/configenv/configenv.go | 139 ++++++++ .../config/src/configenv/configenv_test.go | 42 +++ plugins/config/src/configenv/parsing.go | 119 +++++++ plugins/config/src/glide.lock | 6 + plugins/config/src/glide.yaml | 3 + .../config/src/subcommands/export/export.go | 40 +++ plugins/config/src/subcommands/get/get.go | 39 +++ plugins/config/src/subcommands/set/set.go | 61 ++++ plugins/config/src/subcommands/unset/unset.go | 54 ++++ .../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/config/subcommands/default | 11 - plugins/config/subcommands/get | 11 - plugins/config/subcommands/set | 11 - plugins/config/subcommands/unset | 11 - 23 files changed, 1187 insertions(+), 312 deletions(-) create mode 100644 plugins/config/.gitignore create mode 100644 plugins/config/Makefile delete mode 100755 plugins/config/commands create mode 100644 plugins/config/src/commands/commands.go create mode 100644 plugins/config/src/configenv/configenv.go create mode 100644 plugins/config/src/configenv/configenv_test.go create mode 100644 plugins/config/src/configenv/parsing.go create mode 100644 plugins/config/src/glide.lock create mode 100644 plugins/config/src/glide.yaml create mode 100644 plugins/config/src/subcommands/export/export.go create mode 100644 plugins/config/src/subcommands/get/get.go create mode 100644 plugins/config/src/subcommands/set/set.go create mode 100644 plugins/config/src/subcommands/unset/unset.go create mode 100644 plugins/config/src/vendor/github.com/ryanuber/columnize/.travis.yml create mode 100644 plugins/config/src/vendor/github.com/ryanuber/columnize/LICENSE create mode 100644 plugins/config/src/vendor/github.com/ryanuber/columnize/README.md create mode 100644 plugins/config/src/vendor/github.com/ryanuber/columnize/columnize.go create mode 100644 plugins/config/src/vendor/github.com/ryanuber/columnize/columnize_test.go delete mode 100755 plugins/config/subcommands/default delete mode 100755 plugins/config/subcommands/get delete mode 100755 plugins/config/subcommands/set delete mode 100755 plugins/config/subcommands/unset diff --git a/plugins/config/.gitignore b/plugins/config/.gitignore new file mode 100644 index 000000000..0f6770b1e --- /dev/null +++ b/plugins/config/.gitignore @@ -0,0 +1,2 @@ +/commands +/subcommands/* diff --git a/plugins/config/Makefile b/plugins/config/Makefile new file mode 100644 index 000000000..8718ca0bc --- /dev/null +++ b/plugins/config/Makefile @@ -0,0 +1,32 @@ +.PHONY: build-in-docker build clean + +GO_REPO_ROOT ?= /go/src/github.com/dokku/dokku +BUILD_IMAGE ?= golang:1.7.1 + +build-in-docker: clean + docker run --rm \ + -v $$PWD/../..:$(GO_REPO_ROOT) \ + -w $(GO_REPO_ROOT)/plugins/config \ + $(BUILD_IMAGE) \ + bash -c "make build" || exit $$? + +build: commands subcommands +subcommands: subcommands/export subcommands/set subcommands/get subcommands/unset + +commands: **/**/commands.go + go build -a -o commands src/commands/commands.go + +subcommands/export: **/**/**/export.go + go build -a -o subcommands/export src/subcommands/export/export.go + +subcommands/set: **/**/**/set.go + go build -a -o subcommands/set src/subcommands/set/set.go + +subcommands/get: **/**/**/get.go + go build -a -o subcommands/get src/subcommands/get/get.go + +subcommands/unset: **/**/**/unset.go + go build -a -o subcommands/unset src/subcommands/unset/unset.go + +clean: + rm -rf commands subcommands diff --git a/plugins/config/commands b/plugins/config/commands deleted file mode 100755 index 305cb27ac..000000000 --- a/plugins/config/commands +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bash -[[ " help config:help " == *" $1 "* ]] || exit "$DOKKU_NOT_IMPLEMENTED_EXIT" -set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x - -case "$1" in - help | config:help) - help_content_func () { - declare desc="return config plugin help content" - cat<|--global), Display all global or app-specific config vars - config:get (|--global) KEY, Display a global or app-specific config value - config:set (|--global) KEY1=VALUE1 [KEY2=VALUE2 ...], Set one or more config vars - config:unset (|--global) KEY1 [KEY2 ...], Unset one or more config vars -help_content - } - - if [[ $1 = "config:help" ]] ; then - echo -e 'Usage: dokku config[:COMMAND] (|--global)' - echo '' - echo 'Manage global or app-specific config vars.' - echo '' - echo 'Additional commands:' - help_content_func | sort | column -c2 -t -s, - else - help_content_func - fi - ;; - - *) - exit "$DOKKU_NOT_IMPLEMENTED_EXIT" - ;; - -esac diff --git a/plugins/config/functions b/plugins/config/functions index 908f6a7af..582d49018 100755 --- a/plugins/config/functions +++ b/plugins/config/functions @@ -6,251 +6,24 @@ config_export() { declare desc="returns export command for config variable of specified type (app/global)" local CONFIG_TYPE="$1" local APP="$2" - local ENV_FILE="$DOKKU_ROOT/$APP/ENV" - - [[ $CONFIG_TYPE == "global" ]] && local ENV_FILE="$DOKKU_ROOT/ENV" - [[ ! -f $ENV_FILE ]] && return 0 - [[ ! -s $ENV_FILE ]] && return 0 - - local VARS=$(grep -Eo "export ([a-zA-Z_][a-zA-Z0-9_]*=.*)" "$ENV_FILE" | cut -d" " -f2-) - echo "$VARS" | awk '{print "export " $0}' + if [[ $CONFIG_TYPE == "global" ]]; then + APP="--global" + fi + dokku config:export "$APP" return 0 } -get_sanitized_config_args() { - local desc="return sanitized arguments for config plugin" - local SANITIZED_ARGS=("$@") - - SANITIZED_ARGS=("${SANITIZED_ARGS[@]//--global/}") - SANITIZED_ARGS=("${SANITIZED_ARGS[@]//--no-restart/}") - SANITIZED_ARGS=("${SANITIZED_ARGS[@]//--export/}") - SANITIZED_ARGS=("${SANITIZED_ARGS[@]//--shell/}") - - echo "${SANITIZED_ARGS[@]}" -} - -config_parse_args() { - declare desc="parse config plugin args" - unset APP ENV_FILE DOKKU_CONFIG_TYPE DOKKU_CONFIG_RESTART DOKKU_CONFIG_EXPORT - - for var; do - if [[ "$var" == "--global" ]]; then - ENV_FILE="$DOKKU_ROOT/ENV" - DOKKU_CONFIG_TYPE="global" - DOKKU_CONFIG_RESTART=false - fi - if [[ "$var" == "--no-restart" ]]; then - DOKKU_CONFIG_RESTART=false - fi - if [[ "$var" == "--export" ]]; then - DOKKU_CONFIG_EXPORT=true - fi - if [[ "$var" == "--shell" ]]; then - DOKKU_CONFIG_SHELL=true - fi - done - - local SANITIZED_ARGS=($(get_sanitized_config_args "$@")) - set -- "${SANITIZED_ARGS[@]}" - - DOKKU_CONFIG_TYPE=${DOKKU_CONFIG_TYPE:="app"} - DOKKU_CONFIG_RESTART=${DOKKU_CONFIG_RESTART:=true} - - if [[ "$DOKKU_CONFIG_TYPE" == "app" ]]; then - if [[ -z $2 ]]; then - dokku_log_fail "Please specify an app to run the command on" - else - verify_app_name "$2" - APP=${APP:="$2"} - ENV_FILE=${ENV_FILE:="$DOKKU_ROOT/$APP/ENV"} - fi - fi - export APP ENV_FILE DOKKU_CONFIG_TYPE DOKKU_CONFIG_RESTART DOKKU_CONFIG_EXPORT -} - -config_create () { - declare desc="create config env file" - local ENV_FILE=$1 - [[ -f $ENV_FILE ]] || touch "$ENV_FILE" -} - -config_styled_hash () { - declare desc="internal config hash" - local vars="$1" - local prefix="$2" - local longest="" - - while read -r word; do - local KEY=$(echo "$word" | cut -d"=" -f1) - if [[ ${#KEY} -gt ${#longest} ]]; then - local longest=$KEY - fi - done <<< "$vars" - - while read -r word; do - local KEY=$(echo "$word" | cut -d"=" -f1) - local VALUE=$(echo "$word" | cut -d"=" -f2- | sed -e "s/^'//" -e "s/'$//" -e "s/\$$//g") - - local num_zeros=$((${#longest} - ${#KEY})) - local zeros=" " - while [[ $num_zeros -gt 0 ]]; do - local zeros="$zeros " - local num_zeros=$((num_zeros - 1)) - done - echo "$prefix$KEY:$zeros$VALUE" - done <<< "$vars" -} - -config_write() { - declare desc="writes config vars out to appropriate file path" - local ENV_TEMP="$1" - local ENV_FILE_TEMP="${ENV_FILE}.tmp" - - echo "$ENV_TEMP" | sed '/^$/d' | sort > "$ENV_FILE_TEMP" - if ! cmp -s "$ENV_FILE" "$ENV_FILE_TEMP"; then - cp -f "$ENV_FILE_TEMP" "$ENV_FILE" - chmod 600 "$ENV_FILE" - fi - rm -f "$ENV_FILE_TEMP" -} - -config_all() { - declare desc="print or export config vars" - [[ "$1" == "config" ]] || set -- "config" "$@" - config_parse_args "$@" - local SANITIZED_ARGS=($(get_sanitized_config_args "$@")) - set -- "${SANITIZED_ARGS[@]}" - - config_create "$ENV_FILE" - [[ $DOKKU_CONFIG_EXPORT ]] && config_export "$DOKKU_CONFIG_TYPE" "$APP" && return 0 - - [[ $APP ]] && local DOKKU_CONFIG_TYPE=$APP - [[ ! -s $ENV_FILE ]] && dokku_log_fail "no config vars for $DOKKU_CONFIG_TYPE" - - local VARS=$(grep -Eo "export ([a-zA-Z_][a-zA-Z0-9_]*=.*)" "$ENV_FILE" | cut -d" " -f2-) - - [[ $DOKKU_CONFIG_SHELL ]] && echo "$VARS" && return 0 - - dokku_log_info2_quiet "$DOKKU_CONFIG_TYPE config vars" - config_styled_hash "$VARS" -} - config_get() { declare desc="get value of given config var" - [[ "$1" == "config:get" ]] || set -- "config:get" "$@" - config_parse_args "$@" - local SANITIZED_ARGS=($(get_sanitized_config_args "$@")) - set -- "${SANITIZED_ARGS[@]}" - - if [[ -z $2 ]]; then - echo "Usage: dokku config:get APP KEY" - echo "Must specify KEY." - exit 1 - fi - - config_create "$ENV_FILE" - if [[ ! -s $ENV_FILE ]]; then - return 1 - fi - - local KEY="${*: -1}" - - grep -Eo "export ([a-zA-Z_][a-zA-Z0-9_]*=.*)" "$ENV_FILE" | grep "^export $KEY=" | cut -d"=" -f2- | sed -e "s/^'//" -e "s/'$//" + dokku config:get "$@" } config_set() { declare desc="set value of given config var" - source "$PLUGIN_AVAILABLE_PATH/ps/functions" - - [[ "$1" == "config:set" ]] || set -- "config:set" "$@" - config_parse_args "$@" - - if [[ -z "${*:3}" ]]; then - echo "Usage: dokku config:set APP KEY1=VALUE1 [KEY2=VALUE2 ...]" - echo "Whitespaces need to be escaped, i.e. KEY=\"VAL\ WITH\ SPACES\"" - echo "Must specify KEY and VALUE to set." - exit 1 - fi - - config_create "$ENV_FILE" - local ENV_ADD="" - local ENV_TEMP=$(cat "${ENV_FILE}") - local RESTART_APP=false - shift 2 - - for var; do - if [[ $var == "--global" ]] || [[ $var == "--no-restart" ]] || ([[ $APP ]] && [[ $var == "$APP" ]]); then - shift 1 - elif [[ $var != *"="* ]]; then - echo "Usage: dokku config:set APP KEY1=VALUE1 [KEY2=VALUE2 ...]" - echo "Whitespaces need to be escaped, i.e. KEY=\"VAL\ WITH\ SPACES\"" - echo "Must specify KEY and VALUE to set." - exit 1 - fi - done - - for var; do - local KEY=$(echo ${var} | cut -d"=" -f1) - local VALUE=$(echo ${var} | cut -d"=" -f2-) - - if [[ $KEY =~ [a-zA-Z_][a-zA-Z0-9_]* ]]; then - local RESTART_APP=true - local ENV_TEMP=$(echo "${ENV_TEMP}" | sed "/^export $KEY=/ d") - local ENV_TEMP="${ENV_TEMP} -export $KEY='$VALUE'" - local ENV_ADD=$(echo -e "${ENV_ADD}" | sed "/^$KEY=/ d") - local ENV_ADD="${ENV_ADD}$ -${var}" - fi - done - local ENV_ADD=$(echo "$ENV_ADD" | tail -n +2) #remove first empty line - - if [[ $RESTART_APP ]]; then - dokku_log_info1 "Setting config vars" - config_styled_hash "$ENV_ADD" " " - - config_write "$ENV_TEMP" - plugn trigger post-config-update "$APP" "set" "$@" - fi - - [[ $APP ]] && local DOKKU_APP_RESTORE=$(config_get "$APP" DOKKU_APP_RESTORE || true) - if [[ "$DOKKU_CONFIG_RESTART" == "true" ]] && [[ "$DOKKU_APP_RESTORE" != 0 ]]; then - dokku_log_info1 "Restarting app $APP" - ps_restart "$APP" - fi + dokku config:set "$@" } config_unset() { - declare desc="unset given config var" - source "$PLUGIN_AVAILABLE_PATH/ps/functions" - - [[ "$1" == "config:unset" ]] || set -- "config:unset" "$@" - config_parse_args "$@" - local SANITIZED_ARGS=($(get_sanitized_config_args "$@")) - set -- "${SANITIZED_ARGS[@]}" - - if [[ -z $2 ]]; then - echo "Usage: dokku config:unset APP KEY1 [KEY2 ...]" - echo "Must specify KEY to unset." - exit 1 - fi - - config_create "$ENV_FILE" - local ENV_TEMP=$(cat "${ENV_FILE}") - local VARS="${*:2}" - - for var in $VARS; do - dokku_log_info1 "Unsetting $var" - local ENV_TEMP=$(echo "${ENV_TEMP}" | sed "/^export $var=/ d") - - config_write "$ENV_TEMP" - done - - plugn trigger post-config-update "$APP" "unset" "$@" - - [[ $APP ]] && local DOKKU_APP_RESTORE=$(config_get "$APP" DOKKU_APP_RESTORE || true) - if [[ "$DOKKU_CONFIG_RESTART" == "true" ]] && [[ "$DOKKU_APP_RESTORE" != 0 ]]; then - dokku_log_info1 "Restarting app $APP" - ps_restart "$APP" - fi + declare desc="set value of given config var" + dokku config:unset "$@" } diff --git a/plugins/config/src/commands/commands.go b/plugins/config/src/commands/commands.go new file mode 100644 index 000000000..d0a04dbf3 --- /dev/null +++ b/plugins/config/src/commands/commands.go @@ -0,0 +1,66 @@ +package main + +import ( + "flag" + "fmt" + "os" + "strconv" + "strings" + + common "github.com/dokku/dokku/plugins/common" + configenv "github.com/dokku/dokku/plugins/config/src/configenv" + columnize "github.com/ryanuber/columnize" +) + +const ( + helpHeader = `Usage: dokku config [|--global] + +Display all global or app-specific config vars + +Additional commands:` + + helpContent = ` + config:get (|--global) KEY, Display a global or app-specific config value + config:set (|--global) [--no-restart] KEY1=VALUE1 [KEY2=VALUE2 ...], Set one or more config vars + config:unset (|--global) KEY1 [KEY2 ...], Unset one or more config vars + config:export (|--global) [--envfile], Export a global or app environment +` +) + +func main() { + flag.Usage = usage + flag.Parse() + + cmd := flag.Arg(0) + switch cmd { + case "config": + target := flag.Arg(1) + env, err := configenv.NewFromTarget(target) + if err != nil { + common.LogFail(err.Error()) + } else { + fmt.Println(env.String()) + } + case "config:help": + usage() + case "help": + fmt.Print(helpContent) + 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 = "\t" + config.Empty = "" + content := strings.Split(helpContent, "\n")[1:] + fmt.Println(helpHeader) + fmt.Println(columnize.Format(content, config)) +} diff --git a/plugins/config/src/configenv/configenv.go b/plugins/config/src/configenv/configenv.go new file mode 100644 index 000000000..d449c7bbe --- /dev/null +++ b/plugins/config/src/configenv/configenv.go @@ -0,0 +1,139 @@ +package configenv + +import ( + "errors" + "fmt" + "os" + "path/filepath" + "sort" + "strings" + + common "github.com/dokku/dokku/plugins/common" +) + +//Env is a representation for global or app environment +type Env struct { + name string + env map[string]string + filename string +} + +func (e *Env) String() string { + return e.EnvfileString() +} + +//EnvfileString returns the contents of this Env in ENVFILE format +func (e *Env) EnvfileString() string { + return e.stringWithEntryPrefix("") +} + +//ExportfileString returns the contents of this Env as bash exports +func (e *Env) ExportfileString() string { + return e.stringWithEntryPrefix("export ") +} + +func (e *Env) stringWithEntryPrefix(prefix string) string { + keys := e.Keys() + sort.Strings(keys) + entries := make([]string, len(keys)) + for i, k := range keys { + entries[i] = fmt.Sprintf("%s%s='%s'", prefix, k, escape(e.env[k])) + } + return strings.Join(entries, "\n") +} + +func escape(value string) string { //TODO: improve this + return strings.Replace(value, "'", "\\'", -1) +} + +//NewFromTarget creates an env from the given target. Tareget is either "--global" or an app name +func NewFromTarget(target string) (*Env, error) { + if target == "--global" { + return parseEnv("global", getGlobalFile()) + } + appfile, err := getAppFile(target) + if err != nil { + return nil, err + } + return parseEnv(target, appfile) +} + +//NewFromString creates an env from the given ENVFILE contents representation +func NewFromString(rep string) (*Env, error) { + return parseEnvFromReader("", "", strings.NewReader(rep)) +} + +//Set an environment variable +func (e *Env) Set(key string, value string) { + e.env[key] = value +} + +//Unset an environment variable +func (e *Env) Unset(key string) { + delete(e.env, key) +} + +//Keys gets the keys in this environment +func (e *Env) Keys() []string { + keys := make([]string, 0, len(e.env)) + for k := range e.env { + keys = append(keys, k) + } + return keys +} + +//Get an environment variable +func (e *Env) Get(key string) (string, bool) { + v, ok := e.env[key] + return v, ok +} + +//GetDefault an environment variable or a default if it doesnt exist +func (e *Env) GetDefault(key string, defaultValue string) string { + v, ok := e.env[key] + if !ok { + return defaultValue + } + return v +} + +//GetBoolDefault gets the bool value of the given key with the given default +//right now that is evaluated as `value != "0"` +func (e *Env) GetBoolDefault(key string, defaultValue bool) bool { + v, ok := e.Get(key) + if !ok { + return defaultValue + } + return v != "0" +} + +//Map return the Env as a map +func (e *Env) Map() map[string]string { + return e.env +} + +//Write an Env back to the file it was read from as an exportfile +func (e *Env) Write() error { + if e.filename == "" { + return errors.New("this Env was created unbound to a file") + } + file, err := os.Create(e.filename) + defer file.Close() + if err != nil { + return err + } + _, err = file.WriteString(e.ExportfileString()) + return err +} + +func getAppFile(appName string) (string, error) { + err := common.VerifyAppName(appName) + if err != nil { + return "", err + } + return filepath.Join(common.MustGetEnv("DOKKU_ROOT"), appName, "ENV"), nil +} + +func getGlobalFile() string { + return filepath.Join(common.MustGetEnv("DOKKU_ROOT"), "ENV") +} diff --git a/plugins/config/src/configenv/configenv_test.go b/plugins/config/src/configenv/configenv_test.go new file mode 100644 index 000000000..d0b3cbf02 --- /dev/null +++ b/plugins/config/src/configenv/configenv_test.go @@ -0,0 +1,42 @@ +package configenv + +import ( + "testing" + + . "github.com/onsi/gomega" +) + +func TestBasic(t *testing.T) { + RegisterTestingT(t) + env, err := NewFromString("HI=ho") + Expect(err).NotTo(HaveOccurred()) + Expect(env.Map()).To(Equal(pairs("HI", "ho"))) + Expect(env.String()).To(Equal("HI='ho'")) + + env, err = NewFromString("HI=h\\o\\'") + Expect(err).NotTo(HaveOccurred()) + Expect(env.Map()).To(Equal(pairs("HI", "h\\o'"))) + Expect(env.String()).To(Equal("HI='h\\o\\''")) + + env, err = NewFromString("HI=ho\nFOO=bar\n\n") + Expect(err).NotTo(HaveOccurred()) + Expect(env.Map()).To(Equal(pairs("HI", "ho", "FOO", "bar"))) + Expect(env.String()).To(Equal("FOO='bar'\nHI='ho'")) + + env, err = NewFromString("HI='ho\n'\n\nFOO='bar\\'\nbaz'") + Expect(err).NotTo(HaveOccurred()) + Expect(env.Map()).To(Equal(pairs("HI", "ho\n", "FOO", "bar'\nbaz"))) + Expect(env.String()).To(Equal("FOO='bar\\'\nbaz'\nHI='ho\n'")) +} + +func pairs(vars ...string) map[string]string { + res := map[string]string{} + var i = 0 + for i < len(vars)-1 { + key := vars[i] + value := vars[i+1] + res[key] = value + i += 2 + } + return res +} diff --git a/plugins/config/src/configenv/parsing.go b/plugins/config/src/configenv/parsing.go new file mode 100644 index 000000000..bf539156b --- /dev/null +++ b/plugins/config/src/configenv/parsing.go @@ -0,0 +1,119 @@ +package configenv + +import ( + "bufio" + "bytes" + "errors" + "io" + "os" +) + +func parseEnv(name string, filename string) (*Env, error) { + var file *os.File + var err error + if _, err := os.Stat(filename); os.IsNotExist(err) { + file, err = os.Create(filename) + } else { + file, err = os.Open(filename) + } + if err != nil { + return nil, err + } + defer file.Close() + return parseEnvFromReader(name, filename, file) +} + +func parseEnvFromReader(name string, filename string, reader io.Reader) (*Env, error) { + //The file format looks like KEY='VALUE'. Eait pair is terminated with a newline. + //All characters are valid for VALUE without escaping + //but one: the single quote and backslash. To represent a single quote emit \'. To represent + //a literal backslash emit \\. In any other case, the backslash is interpreted literally. + //KEY='value + //one' + //KEY='val\\ue\\'' etc.. + + //TODO: We might want to rework this to use shell-style quoting for single quotes + buffered := bufio.NewReader(reader) + const ( + StateKey = iota + StateValue = iota + ) + env := &Env{name, map[string]string{}, filename} + var buffer bytes.Buffer + var state = StateKey + var quoted = false + var escaped = false + var key = "" + for char, _, err := buffered.ReadRune(); err == nil; char, _, err = buffered.ReadRune() { + switch state { + case StateKey: + switch char { + case ' ': + if buffer.String() == "export" { + buffer.Truncate(0) //so we can read exportfiles too + } else { + return nil, errors.New("Env keys cannot have spaces") + } + case '=': + key = buffer.String() + buffer.Truncate(0) + state = StateValue + case '\n': + if buffer.Len() > 0 { + return nil, errors.New("Invalid newline after: " + buffer.String()) + } + default: + buffer.WriteRune(char) + } + case StateValue: + switch char { + case '\'': + if escaped { + buffer.WriteRune('\'') + escaped = false + } else { + quoted = !quoted + } + case '\n': + if escaped || quoted { + buffer.WriteRune(char) + } else { + state = StateKey + env.Set(key, buffer.String()) + buffer.Truncate(0) + key = "" + escaped = false + quoted = false + } + case '\\': + if escaped { + buffer.WriteRune(char) + escaped = false + } else { + escaped = true + } + default: + if escaped { + buffer.WriteRune('\\') + escaped = false + } + buffer.WriteRune(char) + } + } + } + if escaped { + return nil, errors.New("Unterminated escape") + } + if quoted { + return nil, errors.New("Unterminated quote") + } + + if state == StateValue { + env.Set(key, buffer.String()) + } else { + if buffer.Len() > 0 { + return nil, errors.New("Invalid trailing content: " + buffer.String()) + } + } + return env, nil +} diff --git a/plugins/config/src/glide.lock b/plugins/config/src/glide.lock new file mode 100644 index 000000000..fe4a98f07 --- /dev/null +++ b/plugins/config/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/config/src/glide.yaml b/plugins/config/src/glide.yaml new file mode 100644 index 000000000..3f7a96657 --- /dev/null +++ b/plugins/config/src/glide.yaml @@ -0,0 +1,3 @@ +package: . +import: +- package: github.com/ryanuber/columnize diff --git a/plugins/config/src/subcommands/export/export.go b/plugins/config/src/subcommands/export/export.go new file mode 100644 index 000000000..52feae877 --- /dev/null +++ b/plugins/config/src/subcommands/export/export.go @@ -0,0 +1,40 @@ +package main + +import ( + "flag" + "fmt" + "os" + + common "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/config/src/configenv" +) + +// print the environment to stdout +func main() { + args := flag.NewFlagSet("config:export", flag.ExitOnError) + global := args.Bool("global", false, "--global: use the global environment") + envfile := args.Bool("envfile", false, "--envfile: export as envfile rather than bash exports") + args.Parse(os.Args[2:]) + + appName := args.Arg(0) + if appName == "" && !*global { + common.LogFail("Please specify an app or --global") + } + + if *global { + if appName != "" { + common.LogFail("Trailing argument: " + appName) + } + appName = "--global" + } + + env, err := configenv.NewFromTarget(appName) + if err != nil { + common.LogFail(err.Error()) + } + if *envfile { + fmt.Println(env.EnvfileString()) + } else { + fmt.Println(env.ExportfileString()) + } +} diff --git a/plugins/config/src/subcommands/get/get.go b/plugins/config/src/subcommands/get/get.go new file mode 100644 index 000000000..3c57eea71 --- /dev/null +++ b/plugins/config/src/subcommands/get/get.go @@ -0,0 +1,39 @@ +package main + +import ( + "flag" + "fmt" + "os" + + common "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/config/src/configenv" +) + +// get the given entries from the specified environment +func main() { + args := flag.NewFlagSet("config:get", flag.ExitOnError) + global := args.Bool("global", false, "--global: use the global environment") + args.Parse(os.Args[2:]) + appName := args.Arg(0) + nextArg := 0 + if appName == "" && !*global { + common.LogFail("Please specify an app or --global") + } + + if *global { + appName = "--global" + } else { + nextArg = 1 + } + + env, err := configenv.NewFromTarget(appName) + if err != nil { + common.LogFail(err.Error()) + } + + if args.NArg() > nextArg+1 { + common.LogFail(fmt.Sprintf("Unexpected argument(s): %v", args.Args()[nextArg+1:])) + } + key := args.Arg(nextArg) + fmt.Printf("%s", env.GetDefault(key, "")) +} diff --git a/plugins/config/src/subcommands/set/set.go b/plugins/config/src/subcommands/set/set.go new file mode 100644 index 000000000..5e0b38dab --- /dev/null +++ b/plugins/config/src/subcommands/set/set.go @@ -0,0 +1,61 @@ +package main + +import ( + "flag" + + "strings" + + "os" + + common "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/config/src/configenv" +) + +// set the given entries from the specified environment +func main() { + args := flag.NewFlagSet("config:set", flag.ExitOnError) + global := args.Bool("global", false, "--global: use the global environment") + noRestart := args.Bool("no-restart", false, "--no-restart: no restart") + args.Parse(os.Args[2:]) + shouldRestart := !*global && !*noRestart + var nextArg = 0 + appName := args.Arg(0) + if appName == "" && !*global { + common.LogFail("Please specify an app or --global") + } + + if *global { + appName = "--global" + } else { + nextArg = 1 + } + + env, err := configenv.NewFromTarget(appName) + if err != nil { + common.LogFail(err.Error()) + } + + var changed = false + entries := args.Args()[nextArg:] + for _, e := range entries { + //log + parts := strings.SplitN(e, "=", 2) + if len(parts) == 1 { + common.LogFail("Invalid env pair: " + e) + } + env.Set(parts[0], parts[1]) + changed = true + } + + if changed { + //log + env.Write() + args := append([]string{appName, "set"}, entries...) + common.PlugnTrigger("post-config-update", args...) + } + + if shouldRestart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) { + cmd := common.NewTokenizedShellCmd("dokku", "ps:restart", appName) + cmd.Execute() + } +} diff --git a/plugins/config/src/subcommands/unset/unset.go b/plugins/config/src/subcommands/unset/unset.go new file mode 100644 index 000000000..ed72cf44b --- /dev/null +++ b/plugins/config/src/subcommands/unset/unset.go @@ -0,0 +1,54 @@ +package main + +import ( + "flag" + "os" + + common "github.com/dokku/dokku/plugins/common" + "github.com/dokku/dokku/plugins/config/src/configenv" +) + +//unset the given entries from the given environment +func main() { + args := flag.NewFlagSet("config:unset", flag.ExitOnError) + global := args.Bool("global", false, "--global: use the global environment") + noRestart := args.Bool("no-restart", false, "--no-restart: no restart") + args.Parse(os.Args[2:]) + shouldRestart := !*global && !*noRestart + var nextArg = 0 + appName := args.Arg(0) + if appName == "" && !*global { + common.LogFail("Please specify an app or --global") + } + + if *global { + appName = "--global" + } else { + nextArg = 1 + } + + env, err := configenv.NewFromTarget(appName) + if err != nil { + common.LogFail(err.Error()) + } + + var changed = false + keys := args.Args()[nextArg:] + for _, k := range keys { + //log + env.Unset(k) + changed = true + } + + if changed { + //log + env.Write() + args := append([]string{appName, "unset"}, keys...) + common.PlugnTrigger("post-config-update", args...) + } + + if shouldRestart && env.GetBoolDefault("DOKKU_APP_RESTORE", true) { + cmd := common.NewTokenizedShellCmd("dokku", "ps:restart", appName) + cmd.Execute() + } +} diff --git a/plugins/config/src/vendor/github.com/ryanuber/columnize/.travis.yml b/plugins/config/src/vendor/github.com/ryanuber/columnize/.travis.yml new file mode 100644 index 000000000..1a0bbea6c --- /dev/null +++ b/plugins/config/src/vendor/github.com/ryanuber/columnize/.travis.yml @@ -0,0 +1,3 @@ +language: go +go: + - tip diff --git a/plugins/config/src/vendor/github.com/ryanuber/columnize/LICENSE b/plugins/config/src/vendor/github.com/ryanuber/columnize/LICENSE new file mode 100644 index 000000000..b9c0e2b68 --- /dev/null +++ b/plugins/config/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/config/src/vendor/github.com/ryanuber/columnize/README.md b/plugins/config/src/vendor/github.com/ryanuber/columnize/README.md new file mode 100644 index 000000000..e47634fc6 --- /dev/null +++ b/plugins/config/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/config/src/vendor/github.com/ryanuber/columnize/columnize.go b/plugins/config/src/vendor/github.com/ryanuber/columnize/columnize.go new file mode 100644 index 000000000..915716a10 --- /dev/null +++ b/plugins/config/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/config/src/vendor/github.com/ryanuber/columnize/columnize_test.go b/plugins/config/src/vendor/github.com/ryanuber/columnize/columnize_test.go new file mode 100644 index 000000000..89dabaa38 --- /dev/null +++ b/plugins/config/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/config/subcommands/default b/plugins/config/subcommands/default deleted file mode 100755 index 2a266cc81..000000000 --- a/plugins/config/subcommands/default +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -source "$PLUGIN_AVAILABLE_PATH/config/functions" - -config_main_cmd() { - declare desc="print config vars for app via command line" - local cmd="config" - config_all "$@" -} - -config_main_cmd "$@" diff --git a/plugins/config/subcommands/get b/plugins/config/subcommands/get deleted file mode 100755 index 6a0822ab4..000000000 --- a/plugins/config/subcommands/get +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -source "$PLUGIN_AVAILABLE_PATH/config/functions" - -config_get_cmd() { - declare desc="get specified config vars from app via command line" - local cmd="config:get" - config_get "$@" -} - -config_get_cmd "$@" diff --git a/plugins/config/subcommands/set b/plugins/config/subcommands/set deleted file mode 100755 index 54253d3aa..000000000 --- a/plugins/config/subcommands/set +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -source "$PLUGIN_AVAILABLE_PATH/config/functions" - -config_set_cmd() { - declare desc="set specified config vars for app via command line" - local cmd="config:set" - config_set "$@" -} - -config_set_cmd "$@" diff --git a/plugins/config/subcommands/unset b/plugins/config/subcommands/unset deleted file mode 100755 index abb864d03..000000000 --- a/plugins/config/subcommands/unset +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x -source "$PLUGIN_AVAILABLE_PATH/config/functions" - -config_unset_cmd() { - declare desc="unset specified config vars for app via command line" - local cmd="config:unset" - config_unset "$@" -} - -config_unset_cmd "$@"