diff --git a/plugins/builder-herokuish/post-app-clone-setup b/plugins/builder-herokuish/post-app-clone-setup deleted file mode 100755 index 3abb15417..000000000 --- a/plugins/builder-herokuish/post-app-clone-setup +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail -[[ $DOKKU_TRACE ]] && set -x -source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" - -trigger-builder-herokuish-post-app-clone-setup() { - declare desc="builder-herokuish post-app-clone-setup plugin trigger" - declare trigger="post-app-clone-setup" - declare OLD_APP="$1" NEW_APP="$2" - - local NEW_CACHE_DIR="$DOKKU_ROOT/$NEW_APP/cache" - local NEW_CACHE_HOST_DIR="$DOKKU_HOST_ROOT/$NEW_APP/cache" - - pushd "$DOKKU_ROOT/$OLD_APP/." >/dev/null - find ./* \( -name ".cache" -o -name "cache" \) -prune -o -print | cpio -pdmu --quiet "$DOKKU_ROOT/$NEW_APP" - popd &>/dev/null || pushd "/tmp" >/dev/null - - if [[ -d "$NEW_CACHE_DIR" ]] && ! rmdir "$NEW_CACHE_DIR"; then - local DOCKER_RUN_LABEL_ARGS="--label=com.dokku.app-name=$NEW_APP" - "$DOCKER_BIN" container run "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS --rm --volume "$NEW_CACHE_HOST_DIR:/cache" "dokku/$OLD_APP" chmod 777 -R /cache - fi - rm -rf "$NEW_CACHE_DIR" -} - -trigger-builder-herokuish-post-app-clone-setup "$@" diff --git a/plugins/builder-herokuish/post-app-rename-setup b/plugins/builder-herokuish/post-app-rename-setup deleted file mode 100755 index 26c5a6dbb..000000000 --- a/plugins/builder-herokuish/post-app-rename-setup +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env bash -set -eo pipefail -[[ $DOKKU_TRACE ]] && set -x -source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions" -source "$PLUGIN_CORE_AVAILABLE_PATH/common/property-functions" - -trigger-builder-herokuish-post-app-clone-setup() { - declare desc="builder-herokuish post-app-clone-setup plugin trigger" - declare trigger="post-app-clone-setup" - declare OLD_APP="$1" NEW_APP="$2" - - local OLD_CACHE_DIR="$DOKKU_ROOT/$OLD_APP/cache" - local OLD_CACHE_HOST_DIR="$DOKKU_HOST_ROOT/$OLD_APP/cache" - - if [[ -d "$OLD_CACHE_DIR" ]] && ! rmdir "$OLD_CACHE_DIR" &>/dev/null; then - local DOCKER_RUN_LABEL_ARGS="--label=com.dokku.app-name=$NEW_APP" - "$DOCKER_BIN" container run "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS --rm --volume "$OLD_CACHE_HOST_DIR:/cache" "dokku/$OLD_APP" chmod 777 -R /cache - fi - rm -rf "$OLD_CACHE_DIR" - - pushd "$DOKKU_ROOT/$OLD_APP/." >/dev/null - find ./* \( -name ".cache" -o -name "cache" \) -prune -o -print | cpio -pdmu --quiet "$DOKKU_ROOT/$NEW_APP" - popd &>/dev/null || pushd "/tmp" >/dev/null -} - -trigger-builder-herokuish-post-app-clone-setup "$@" diff --git a/plugins/config/.gitignore b/plugins/config/.gitignore index b79352e77..3b7e2f512 100644 --- a/plugins/config/.gitignore +++ b/plugins/config/.gitignore @@ -4,3 +4,4 @@ /triggers /config-* /config_sub +/post-* diff --git a/plugins/config/Makefile b/plugins/config/Makefile index e38a8f62d..86ffcb36b 100644 --- a/plugins/config/Makefile +++ b/plugins/config/Makefile @@ -1,6 +1,6 @@ GOARCH ?= amd64 SUBCOMMANDS = subcommands/bundle subcommands/clear subcommands/export subcommands/get subcommands/keys subcommands/show subcommands/set subcommands/unset -TRIGGERS = triggers/config-export triggers/config-get triggers/config-get-global +TRIGGERS = triggers/config-export triggers/config-get triggers/config-get-global triggers/post-app-clone-setup triggers/post-app-rename-setup BUILD = commands config_sub subcommands triggers PLUGIN_NAME = config diff --git a/plugins/config/environment.go b/plugins/config/environment.go index 2f6ddf071..877939e5f 100644 --- a/plugins/config/environment.go +++ b/plugins/config/environment.go @@ -16,7 +16,7 @@ import ( "github.com/ryanuber/columnize" ) -//ExportFormat types of possible exports +// ExportFormat types of possible exports type ExportFormat int const ( @@ -40,14 +40,14 @@ const ( ExportFormatPackArgKeys ) -//Env is a representation for global or app environment +// Env is a representation for global or app environment type Env struct { name string filename string env map[string]string } -//newEnvFromString creates an env from the given ENVFILE contents representation +// newEnvFromString creates an env from the given ENVFILE contents representation func newEnvFromString(rep string) (env *Env, err error) { envMap, err := godotenv.Unmarshal(rep) env = &Env{ @@ -58,7 +58,7 @@ func newEnvFromString(rep string) (env *Env, err error) { return } -//LoadAppEnv loads an environment for the given app +// LoadAppEnv loads an environment for the given app func LoadAppEnv(appName string) (env *Env, err error) { appfile, err := getAppFile(appName) if err != nil { @@ -67,7 +67,7 @@ func LoadAppEnv(appName string) (env *Env, err error) { return loadFromFile(appName, appfile) } -//LoadMergedAppEnv loads an app environment merged with the global environment +// LoadMergedAppEnv loads an app environment merged with the global environment func LoadMergedAppEnv(appName string) (env *Env, err error) { env, err = LoadAppEnv(appName) if err != nil { @@ -83,7 +83,7 @@ func LoadMergedAppEnv(appName string) (env *Env, err error) { return global, err } -//LoadGlobalEnv loads the global environment +// LoadGlobalEnv loads the global environment func LoadGlobalEnv() (*Env, error) { return loadFromFile("", getGlobalFile()) } @@ -93,13 +93,13 @@ func (e *Env) Filename() string { return e.filename } -//Get an environment variable +// Get an environment variable func (e *Env) Get(key string) (value string, ok bool) { value, ok = e.env[key] return } -//GetDefault an environment variable or a default if it doesn't exist +// GetDefault an environment variable or a default if it doesn't exist func (e *Env) GetDefault(key string, defaultValue string) string { v, ok := e.env[key] if !ok { @@ -108,8 +108,8 @@ func (e *Env) GetDefault(key string, defaultValue string) string { return v } -//GetBoolDefault gets the bool value of the given key with the given default -//right now that is evaluated as `value != "0"` +// 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 { @@ -118,17 +118,17 @@ func (e *Env) GetBoolDefault(key string, defaultValue bool) bool { return v != "0" } -//Set an environment variable +// Set an environment variable func (e *Env) Set(key string, value string) { e.env[key] = value } -//Unset an environment variable +// Unset an environment variable func (e *Env) Unset(key string) { delete(e.env, key) } -//Keys gets the keys in this environment +// Keys gets the keys in this environment func (e *Env) Keys() (keys []string) { keys = make([]string, 0, len(e.env)) for k := range e.env { @@ -138,12 +138,12 @@ func (e *Env) Keys() (keys []string) { return } -//Len returns the number of items in this environment +// Len returns the number of items in this environment func (e *Env) Len() int { return len(e.env) } -//Map returns the Env as a map +// Map returns the Env as a map func (e *Env) Map() map[string]string { return e.env } @@ -152,14 +152,14 @@ func (e *Env) String() string { return e.EnvfileString() } -//Merge merges the given environment on top of the receiver +// Merge merges the given environment on top of the receiver func (e *Env) Merge(other *Env) { for _, k := range other.Keys() { e.Set(k, other.GetDefault(k, "")) } } -//Write an Env back to the file it was read from as an exportfile +// 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") @@ -167,7 +167,7 @@ func (e *Env) Write() error { return godotenv.Write(e.Map(), e.filename) } -//Export the Env in the given format +// Export the Env in the given format func (e *Env) Export(format ExportFormat) string { switch format { case ExportFormatExports: @@ -194,23 +194,23 @@ func (e *Env) Export(format ExportFormat) string { } } -//EnvfileString returns the contents of this Env in dotenv format +// EnvfileString returns the contents of this Env in dotenv format func (e *Env) EnvfileString() string { rep, _ := godotenv.Marshal(e.Map()) return rep } -//ExportfileString returns the contents of this Env as bash exports +// ExportfileString returns the contents of this Env as bash exports func (e *Env) ExportfileString() string { return e.stringWithPrefixAndSeparator("export ", "\n") } -//DockerArgsString gets the contents of this Env in the form -env=KEY=VALUE --env... +// DockerArgsString gets the contents of this Env in the form -env=KEY=VALUE --env... func (e *Env) DockerArgsString() string { return e.stringWithPrefixAndSeparator("--env=", " ") } -//DockerArgsKeysString gets the contents of this Env in the form -env=KEY --env... +// DockerArgsKeysString gets the contents of this Env in the form -env=KEY --env... func (e *Env) DockerArgsKeysString() string { keys := e.Keys() entries := make([]string, len(keys)) @@ -220,7 +220,7 @@ func (e *Env) DockerArgsKeysString() string { return strings.Join(entries, " ") } -//JSONString returns the contents of this Env as a key/value json object +// JSONString returns the contents of this Env as a key/value json object func (e *Env) JSONString() string { data, err := json.Marshal(e.Map()) if err != nil { @@ -230,7 +230,7 @@ func (e *Env) JSONString() string { return string(data) } -//JSONListString returns the contents of this Env as a json list of objects containing the name and the value of the env var +// JSONListString returns the contents of this Env as a json list of objects containing the name and the value of the env var func (e *Env) JSONListString() string { var list []map[string]string for _, key := range e.Keys() { @@ -249,7 +249,7 @@ func (e *Env) JSONListString() string { return string(data) } -//PackArgKeysAsString gets the contents of this Env in the form -env KEY --env... +// PackArgKeysAsString gets the contents of this Env in the form -env KEY --env... func (e *Env) PackArgKeysAsString() string { keys := e.Keys() entries := make([]string, len(keys)) @@ -259,13 +259,13 @@ func (e *Env) PackArgKeysAsString() string { return strings.Join(entries, " ") } -//ShellString gets the contents of this Env in the form "KEY='value' KEY2='value'" +// ShellString gets the contents of this Env in the form "KEY='value' KEY2='value'" // for passing the environment in the shell func (e *Env) ShellString() string { return e.stringWithPrefixAndSeparator("", " ") } -//ExportBundle writes a tarfile of the environment to the given io.Writer. +// ExportBundle writes a tarfile of the environment to the given io.Writer. // for every environment variable there is a file with the variable's key // with its content set to the variable's value func (e *Env) ExportBundle(dest io.Writer) error { @@ -287,7 +287,7 @@ func (e *Env) ExportBundle(dest io.Writer) error { return nil } -//stringWithPrefixAndSeparator makes a string of the environment +// stringWithPrefixAndSeparator makes a string of the environment // with the given prefix and separator for each entry func (e *Env) stringWithPrefixAndSeparator(prefix string, separator string) string { keys := e.Keys() @@ -299,12 +299,12 @@ func (e *Env) stringWithPrefixAndSeparator(prefix string, separator string) stri return strings.Join(entries, separator) } -//singleQuoteEscape escapes the value as if it were shell-quoted in single quotes +// singleQuoteEscape escapes the value as if it were shell-quoted in single quotes func singleQuoteEscape(value string) string { // so that 'esc'aped' -> 'esc'\''aped' return strings.Replace(value, "'", "'\\''", -1) } -//prettyPrintEnvEntries in columns +// prettyPrintEnvEntries in columns func prettyPrintEnvEntries(prefix string, entries map[string]string) string { colConfig := columnize.DefaultConfig() colConfig.Prefix = prefix diff --git a/plugins/config/src/triggers/triggers.go b/plugins/config/src/triggers/triggers.go index eec123a48..1f8f58732 100644 --- a/plugins/config/src/triggers/triggers.go +++ b/plugins/config/src/triggers/triggers.go @@ -36,6 +36,14 @@ func main() { case "config-get-global": key := flag.Arg(0) err = config.TriggerConfigGetGlobal(key) + case "post-app-clone-setup": + oldAppName := flag.Arg(0) + newAppName := flag.Arg(1) + err = config.TriggerPostAppCloneSetup(oldAppName, newAppName) + case "post-app-rename-setup": + oldAppName := flag.Arg(0) + newAppName := flag.Arg(1) + err = config.TriggerPostAppRenameSetup(oldAppName, newAppName) default: err = fmt.Errorf("Invalid plugin trigger call: %s", trigger) } diff --git a/plugins/config/triggers.go b/plugins/config/triggers.go index 3014f6d26..af96d114c 100644 --- a/plugins/config/triggers.go +++ b/plugins/config/triggers.go @@ -44,3 +44,43 @@ func TriggerConfigGetGlobal(key string) error { return nil } + +// TriggerPostAppCloneSetup creates new buildpacks files +func TriggerPostAppCloneSetup(oldAppName string, newAppName string) error { + oldEnv, err := LoadAppEnv(oldAppName) + if err != nil { + return fmt.Errorf("Unable to load old environment: %s", err.Error()) + } + + newEnv, err := LoadAppEnv(newAppName) + if err != nil { + return fmt.Errorf("Unable to load new environment: %s", err.Error()) + } + + newEnv.Merge(oldEnv) + if err := newEnv.Write(); err != nil { + return fmt.Errorf("Unable to write new environment: %s", err.Error()) + } + + return nil +} + +// TriggerPostAppRenameSetup renames buildpacks files +func TriggerPostAppRenameSetup(oldAppName string, newAppName string) error { + oldEnv, err := LoadAppEnv(oldAppName) + if err != nil { + return fmt.Errorf("Unable to load old environment: %s", err.Error()) + } + + newEnv, err := LoadAppEnv(newAppName) + if err != nil { + return fmt.Errorf("Unable to load new environment: %s", err.Error()) + } + + newEnv.Merge(oldEnv) + if err := newEnv.Write(); err != nil { + return fmt.Errorf("Unable to write new environment: %s", err.Error()) + } + + return nil +} diff --git a/plugins/docker-options/post-app-clone-setup b/plugins/docker-options/post-app-clone-setup new file mode 100755 index 000000000..9b36b329d --- /dev/null +++ b/plugins/docker-options/post-app-clone-setup @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_AVAILABLE_PATH/docker-options/functions" + +trigger-docker-options-post-app-clone-setup() { + declare desc="copies docker options over" + declare trigger="post-app-clone-setup" + declare OLD_APP="$1" NEW_APP="$2" + + declare -a phases + phases=("build" "deploy" "run") + for phase in "${phases[@]}"; do + if [[ -f "$(fn-get-phase-file-path "$OLD_APP" "$phase")" ]]; then + cp "$(fn-get-phase-file-path "$OLD_APP" "$phase")" "$(fn-get-phase-file-path "$NEW_APP" "$phase")" + fi + done +} + +trigger-docker-options-post-app-clone-setup "$@" diff --git a/plugins/docker-options/post-app-rename-setup b/plugins/docker-options/post-app-rename-setup new file mode 100755 index 000000000..0688252a6 --- /dev/null +++ b/plugins/docker-options/post-app-rename-setup @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -eo pipefail +[[ $DOKKU_TRACE ]] && set -x +source "$PLUGIN_AVAILABLE_PATH/docker-options/functions" + +trigger-docker-options-post-app-rename-setup() { + declare desc="copies docker options over" + declare trigger="post-app-rename-setup" + declare OLD_APP="$1" NEW_APP="$2" + + declare -a phases + phases=("build" "deploy" "run") + for phase in "${phases[@]}"; do + if [[ -f "$(fn-get-phase-file-path "$OLD_APP" "$phase")" ]]; then + cp "$(fn-get-phase-file-path "$OLD_APP" "$phase")" "$(fn-get-phase-file-path "$NEW_APP" "$phase")" + fi + done +} + +trigger-docker-options-post-app-rename-setup "$@" diff --git a/plugins/domains/post-app-rename-setup b/plugins/domains/post-app-rename-setup index 7cbce6a11..3ec256a9c 100755 --- a/plugins/domains/post-app-rename-setup +++ b/plugins/domains/post-app-rename-setup @@ -7,9 +7,9 @@ trigger-domains-post-app-rename-setup() { declare trigger="post-app-rename-setup" declare OLD_APP="$1" NEW_APP="$2" - if [[ -f "$DOKKU_ROOT/$NEW_APP/VHOST" ]]; then + if [[ -f "$DOKKU_ROOT/$OLD_APP/VHOST" ]]; then while read -r VHOST || [[ -n "$VHOST" ]]; do - sed -i -e "s/$OLD_APP.$VHOST/$NEW_APP.$VHOST/g" "$DOKKU_ROOT/$NEW_APP/VHOST" + sed -e "s/$OLD_APP.$VHOST/$NEW_APP.$VHOST/g" "$DOKKU_ROOT/$OLD_APP/VHOST" >"$DOKKU_ROOT/$NEW_APP/VHOST" done <"$DOKKU_ROOT/VHOST" fi } diff --git a/tests/unit/apps_1.bats b/tests/unit/apps_1.bats index 06ef1be1e..5b85ffacd 100644 --- a/tests/unit/apps_1.bats +++ b/tests/unit/apps_1.bats @@ -178,6 +178,14 @@ teardown() { echo "output: $output" echo "status: $status" assert_success + run /bin/bash -c "dokku config:set --no-restart $TEST_APP KEY=VALUE-$TEST_APP" + echo "output: $output" + echo "status: $status" + assert_success + run /bin/bash -c "dokku docker-options:add $TEST_APP deploy --no-healthcheck" + echo "output: $output" + echo "status: $status" + assert_success run /bin/bash -c "dokku git:set $TEST_APP deploy-branch SOME_BRANCH_NAME" echo "output: $output" echo "status: $status" @@ -204,6 +212,16 @@ teardown() { echo "status: $status" assert_success assert_output "https://github.com/heroku/heroku-buildpack-ruby.git" + run /bin/bash -c "dokku config:get great-test-name KEY" + echo "output: $output" + echo "status: $status" + assert_success + assert_output "VALUE-$TEST_APP" + run /bin/bash -c "dokku docker-options:report great-test-name --docker-options-deploy" + echo "output: $output" + echo "status: $status" + assert_success + assert_output_contains "--no-healthcheck" run /bin/bash -c "dokku git:report great-test-name --git-deploy-branch" echo "output: $output" echo "status: $status"