Merge pull request #5183 from dokku/user-access-to-app

Filter apps when verifying app names
This commit is contained in:
Jose Diaz-Gonzalez
2022-05-29 12:50:44 -04:00
committed by GitHub
13 changed files with 331 additions and 142 deletions

View File

@@ -581,6 +581,11 @@ func VerifyAppName(appName string) error {
return &AppDoesNotExist{appName} return &AppDoesNotExist{appName}
} }
apps, _ := filterApps([]string{appName})
if len(apps) != 1 {
return &AppDoesNotExist{appName}
}
return nil return nil
} }

View File

@@ -20,6 +20,10 @@ var (
testEnvLine2 = "export testKey=TESTING" testEnvLine2 = "export testKey=TESTING"
) )
func setupTests() (err error) {
return os.Setenv("PLUGIN_ENABLED_PATH", "/var/lib/dokku/plugins/enabled")
}
func setupTestApp() (err error) { func setupTestApp() (err error) {
Expect(os.MkdirAll(testAppDir, 0644)).To(Succeed()) Expect(os.MkdirAll(testAppDir, 0644)).To(Succeed())
b := []byte(testEnvLine + "\n") b := []byte(testEnvLine + "\n")
@@ -48,32 +52,37 @@ func teardownTestApp2() {
func TestCommonGetEnv(t *testing.T) { func TestCommonGetEnv(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(MustGetEnv("DOKKU_ROOT")).To(Equal("/home/dokku")) Expect(MustGetEnv("DOKKU_ROOT")).To(Equal("/home/dokku"))
} }
func TestCommonGetAppImageRepo(t *testing.T) { func TestCommonGetAppImageRepo(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(GetAppImageRepo("testapp")).To(Equal("dokku/testapp")) Expect(GetAppImageRepo("testapp")).To(Equal("dokku/testapp"))
} }
func TestCommonVerifyImageInvalid(t *testing.T) { func TestCommonVerifyImageInvalid(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(VerifyImage("testapp")).To(Equal(false)) Expect(VerifyImage("testapp")).To(Equal(false))
} }
func TestCommonVerifyAppNameInvalid(t *testing.T) { func TestCommonVerifyAppNameInvalid(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
err := VerifyAppName("1994testApp") Expect(setupTests()).To(Succeed())
Expect(err).To(HaveOccurred()) Expect(VerifyAppName("1994testApp")).To(HaveOccurred())
} }
func TestCommonVerifyAppName(t *testing.T) { func TestCommonVerifyAppName(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(setupTestApp()).To(Succeed()) Expect(setupTestApp()).To(Succeed())
Expect(VerifyAppName(testAppName)).To(Succeed()) Expect(VerifyAppName(testAppName)).To(Succeed())
teardownTestApp() teardownTestApp()
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(setupTestApp2()).To(Succeed()) Expect(setupTestApp2()).To(Succeed())
Expect(VerifyAppName(testAppName2)).To(Succeed()) Expect(VerifyAppName(testAppName2)).To(Succeed())
teardownTestApp2() teardownTestApp2()
@@ -81,13 +90,14 @@ func TestCommonVerifyAppName(t *testing.T) {
func TestCommonDokkuAppsError(t *testing.T) { func TestCommonDokkuAppsError(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
_, err := DokkuApps() _, err := DokkuApps()
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
} }
func TestCommonDokkuApps(t *testing.T) { func TestCommonDokkuApps(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
os.Setenv("PLUGIN_ENABLED_PATH", "/var/lib/dokku/plugins/enabled") Expect(setupTests()).To(Succeed())
Expect(setupTestApp()).To(Succeed()) Expect(setupTestApp()).To(Succeed())
apps, err := DokkuApps() apps, err := DokkuApps()
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
@@ -98,6 +108,7 @@ func TestCommonDokkuApps(t *testing.T) {
func TestCommonStripInlineComments(t *testing.T) { func TestCommonStripInlineComments(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
text := StripInlineComments(strings.Join([]string{testEnvLine, "# testing comment"}, " ")) text := StripInlineComments(strings.Join([]string{testEnvLine, "# testing comment"}, " "))
Expect(text).To(Equal(testEnvLine)) Expect(text).To(Equal(testEnvLine))
} }

View File

@@ -250,23 +250,12 @@ is_valid_app_name_old() {
verify_app_name() { verify_app_name() {
declare desc="verify app name format and app existence" declare desc="verify app name format and app existence"
declare APP="$1" declare APP="$1"
local VALID_NEW=false
local VALID_OLD=false if "$PLUGIN_CORE_AVAILABLE_PATH/common/common" --quiet verify-app-name "$APP"; then
if fn-is-valid-app-name "$APP" 2>/dev/null; then return 0
VALID_NEW=true
fi fi
if fn-is-valid-app-name-old "$APP" 2>/dev/null; then DOKKU_FAIL_EXIT_CODE=20 dokku_log_fail "App $APP does not exist"
VALID_OLD=true
fi
if [[ "$VALID_NEW" == "false" ]] && [[ "$VALID_OLD" == "false" ]]; then
dokku_log_fail "App name must begin with lowercase alphanumeric character, and cannot include uppercase characters, colons, or underscores"
fi
[[ ! -d "$DOKKU_ROOT/$APP" ]] && DOKKU_FAIL_EXIT_CODE=20 dokku_log_fail "App $APP does not exist"
return 0
} }
verify_image() { verify_image() {

View File

@@ -61,6 +61,19 @@ func LogFailWithError(err error) {
os.Exit(1) os.Exit(1)
} }
// LogFailWithErrorQuiet is the failure log formatter (with quiet option)
// prints text to stderr and exits with the specified exit code
// The error message is not printed if DOKKU_QUIET_OUTPUT has any value
func LogFailWithErrorQuiet(err error) {
if os.Getenv("DOKKU_QUIET_OUTPUT") == "" {
fmt.Fprintln(os.Stderr, fmt.Sprintf(" ! %s", err.Error()))
}
if errExit, ok := err.(ErrWithExitCode); ok {
os.Exit(errExit.ExitCode())
}
os.Exit(1)
}
// LogFailQuiet is the failure log formatter (with quiet option) // LogFailQuiet is the failure log formatter (with quiet option)
// prints text to stderr and exits with status 1 // prints text to stderr and exits with status 1
func LogFailQuiet(text string) { func LogFailQuiet(text string) {

View File

@@ -38,11 +38,14 @@ func main() {
appName = "--global" appName = "--global"
} }
fmt.Print(common.GetAppScheduler(appName)) fmt.Print(common.GetAppScheduler(appName))
case "verify-app-name":
appName := flag.Arg(1)
err = common.VerifyAppName(appName)
default: default:
err = fmt.Errorf("Invalid common command call: %v", cmd) err = fmt.Errorf("Invalid common command call: %v", cmd)
} }
if err != nil { if err != nil {
common.LogFailQuiet(err.Error()) common.LogFailWithErrorQuiet(err)
} }
} }

View File

@@ -1,6 +1,12 @@
SUBCOMMANDS = subcommands/bundle subcommands/clear subcommands/export subcommands/get subcommands/keys subcommands/show subcommands/set subcommands/unset 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
BUILD = commands subcommands triggers BUILD = commands config_sub subcommands triggers
PLUGIN_NAME = config PLUGIN_NAME = config
clean-config_sub:
rm -rf config_sub
config_sub: clean-config_sub **/**/config_sub.go
go build -ldflags="-s -w" $(GO_ARGS) -o config_sub src/config_sub/config_sub.go
include ../../common.mk include ../../common.mk

View File

@@ -18,6 +18,10 @@ var (
globalConfigFile = strings.Join([]string{dokkuRoot, "ENV"}, "/") globalConfigFile = strings.Join([]string{dokkuRoot, "ENV"}, "/")
) )
func setupTests() (err error) {
return os.Setenv("PLUGIN_ENABLED_PATH", "/var/lib/dokku/plugins/enabled")
}
func setupTestApp() (err error) { func setupTestApp() (err error) {
Expect(os.MkdirAll(testAppDir, 0766)).To(Succeed()) Expect(os.MkdirAll(testAppDir, 0766)).To(Succeed())
b := []byte("export testKey=TESTING\n") b := []byte("export testKey=TESTING\n")
@@ -38,6 +42,7 @@ func teardownTestApp() {
func TestConfigGetWithDefault(t *testing.T) { func TestConfigGetWithDefault(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(setupTestApp()).To(Succeed()) Expect(setupTestApp()).To(Succeed())
Expect(GetWithDefault(testAppName, "unknownKey", "UNKNOWN")).To(Equal("UNKNOWN")) Expect(GetWithDefault(testAppName, "unknownKey", "UNKNOWN")).To(Equal("UNKNOWN"))
Expect(GetWithDefault(testAppName, "testKey", "testKey")).To(Equal("TESTING")) Expect(GetWithDefault(testAppName, "testKey", "testKey")).To(Equal("TESTING"))
@@ -47,6 +52,7 @@ func TestConfigGetWithDefault(t *testing.T) {
func TestConfigGet(t *testing.T) { func TestConfigGet(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(setupTestApp()).To(Succeed()) Expect(setupTestApp()).To(Succeed())
defer teardownTestApp() defer teardownTestApp()
@@ -59,45 +65,48 @@ func TestConfigGet(t *testing.T) {
func TestConfigSetMany(t *testing.T) { func TestConfigSetMany(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(setupTestApp()).To(Succeed()) Expect(setupTestApp()).To(Succeed())
defer teardownTestApp() defer teardownTestApp()
expectValue(testAppName, "testKey", "TESTING") expectValue(testAppName, "testKey", "TESTING")
vals := map[string]string{"testKey": "updated", "testKey2": "new"} vals := []string{"testKey=updated", "testKey2=new"}
Expect(SetMany(testAppName, vals, false)).To(Succeed()) Expect(CommandSet(testAppName, vals, false, true, false)).To(Succeed())
expectValue(testAppName, "testKey", "updated") expectValue(testAppName, "testKey", "updated")
expectValue(testAppName, "testKey2", "new") expectValue(testAppName, "testKey2", "new")
vals = map[string]string{"testKey": "updated_global", "testKey2": "new_global"} vals = []string{"testKey=updated_global", "testKey2=new_global"}
Expect(SetMany("", vals, false)).To(Succeed()) Expect(CommandSet("", vals, true, true, false)).To(Succeed())
expectValue("", "testKey", "updated_global") expectValue("", "testKey", "updated_global")
expectValue("", "testKey2", "new_global") expectValue("", "testKey2", "new_global")
expectValue("", "globalKey", "GLOBAL_VALUE") expectValue("", "globalKey", "GLOBAL_VALUE")
expectValue(testAppName, "testKey", "updated") expectValue(testAppName, "testKey", "updated")
expectValue(testAppName, "testKey2", "new") expectValue(testAppName, "testKey2", "new")
Expect(SetMany(testAppName+"does_not_exist", vals, false)).ToNot(Succeed()) Expect(CommandSet(testAppName+"does_not_exist", vals, false, true, false)).ToNot(Succeed())
} }
func TestConfigUnsetAll(t *testing.T) { func TestConfigUnsetAll(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(setupTestApp()).To(Succeed()) Expect(setupTestApp()).To(Succeed())
defer teardownTestApp() defer teardownTestApp()
expectValue(testAppName, "testKey", "TESTING") expectValue(testAppName, "testKey", "TESTING")
expectValue("", "testKey", "GLOBAL_TESTING") expectValue("", "testKey", "GLOBAL_TESTING")
Expect(UnsetAll(testAppName, false)).To(Succeed()) Expect(CommandClear(testAppName, false, true)).To(Succeed())
expectNoValue(testAppName, "testKey") expectNoValue(testAppName, "testKey")
expectNoValue(testAppName, "noKey") expectNoValue(testAppName, "noKey")
expectNoValue(testAppName, "globalKey") expectNoValue(testAppName, "globalKey")
Expect(UnsetAll(testAppName+"does-not-exist", false)).ToNot(Succeed()) Expect(CommandClear(testAppName+"does-not-exist", false, true)).ToNot(Succeed())
} }
func TestConfigUnsetMany(t *testing.T) { func TestConfigUnsetMany(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(setupTestApp()).To(Succeed()) Expect(setupTestApp()).To(Succeed())
defer teardownTestApp() defer teardownTestApp()
@@ -105,19 +114,20 @@ func TestConfigUnsetMany(t *testing.T) {
expectValue("", "testKey", "GLOBAL_TESTING") expectValue("", "testKey", "GLOBAL_TESTING")
keys := []string{"testKey", "noKey"} keys := []string{"testKey", "noKey"}
Expect(UnsetMany(testAppName, keys, false)).To(Succeed()) Expect(CommandUnset(testAppName, keys, false, true)).To(Succeed())
expectNoValue(testAppName, "testKey") expectNoValue(testAppName, "testKey")
expectValue("", "testKey", "GLOBAL_TESTING") expectValue("", "testKey", "GLOBAL_TESTING")
Expect(UnsetMany(testAppName, keys, false)).To(Succeed()) Expect(CommandUnset(testAppName, keys, false, true)).To(Succeed())
expectNoValue(testAppName, "testKey") expectNoValue(testAppName, "testKey")
expectNoValue(testAppName, "globalKey") expectNoValue(testAppName, "globalKey")
Expect(UnsetMany(testAppName+"does-not-exist", keys, false)).ToNot(Succeed()) Expect(CommandUnset(testAppName+"does-not-exist", keys, false, true)).ToNot(Succeed())
} }
func TestEnvironmentLoading(t *testing.T) { func TestEnvironmentLoading(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(setupTestApp()).To(Succeed()) Expect(setupTestApp()).To(Succeed())
defer teardownTestApp() defer teardownTestApp()
@@ -142,6 +152,7 @@ func TestEnvironmentLoading(t *testing.T) {
func TestInvalidKeys(t *testing.T) { func TestInvalidKeys(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(setupTestApp()).To(Succeed()) Expect(setupTestApp()).To(Succeed())
defer teardownTestApp() defer teardownTestApp()
@@ -158,6 +169,7 @@ func TestInvalidKeys(t *testing.T) {
func TestInvalidEnvOnDisk(t *testing.T) { func TestInvalidEnvOnDisk(t *testing.T) {
RegisterTestingT(t) RegisterTestingT(t)
Expect(setupTests()).To(Succeed())
Expect(setupTestApp()).To(Succeed()) Expect(setupTestApp()).To(Succeed())
defer teardownTestApp() defer teardownTestApp()

View File

@@ -60,10 +60,6 @@ func newEnvFromString(rep string) (env *Env, err error) {
//LoadAppEnv loads an environment for the given app //LoadAppEnv loads an environment for the given app
func LoadAppEnv(appName string) (env *Env, err error) { func LoadAppEnv(appName string) (env *Env, err error) {
err = common.VerifyAppName(appName)
if err != nil {
return
}
appfile, err := getAppFile(appName) appfile, err := getAppFile(appName)
if err != nil { if err != nil {
return return
@@ -357,10 +353,6 @@ func loadFromFile(name string, filename string) (env *Env, err error) {
} }
func getAppFile(appName string) (string, error) { 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 return filepath.Join(common.MustGetEnv("DOKKU_ROOT"), appName, "ENV"), nil
} }

View File

@@ -7,7 +7,7 @@ config_sub() {
declare desc="executes a config subcommand" declare desc="executes a config subcommand"
local name="$1" local name="$1"
shift shift
"$PLUGIN_AVAILABLE_PATH/config/subcommands/$name" "config:$name" "$@" "$PLUGIN_AVAILABLE_PATH/config/config_sub" "$name" "$@"
} }
config_export() { config_export() {

View File

@@ -1,13 +1,16 @@
package config package config
import "fmt" import (
"encoding/base64"
"errors"
"fmt"
"os"
"strings"
func export(appName string, global bool, merged bool, format string) error { "github.com/dokku/dokku/plugins/common"
appName, err := getAppNameOrGlobal(appName, global) )
if err != nil {
return err
}
func export(appName string, merged bool, format string) error {
env := getEnvironment(appName, merged) env := getEnvironment(appName, merged)
exportType := ExportFormatExports exportType := ExportFormatExports
suffix := "\n" suffix := "\n"
@@ -37,3 +40,113 @@ func export(appName string, global bool, merged bool, format string) error {
fmt.Print(exported + suffix) fmt.Print(exported + suffix)
return nil return nil
} }
// SubBundle implements the logic for config:bundle without app name validation
func SubBundle(appName string, merged bool) error {
env := getEnvironment(appName, merged)
return env.ExportBundle(os.Stdout)
}
// SubClear implements the logic for config:clear without app name validation
func SubClear(appName string, noRestart bool) error {
return UnsetAll(appName, !noRestart)
}
// SubExport implements the logic for config:export without app name validation
func SubExport(appName string, merged bool, format string) error {
return export(appName, merged, format)
}
// SubGet implements the logic for config:get without app name validation
func SubGet(appName string, keys []string, quoted bool) error {
if len(keys) == 0 {
return errors.New("Expected: key")
}
if len(keys) != 1 {
return fmt.Errorf("Unexpected argument(s): %v", keys[1:])
}
value, ok := Get(appName, keys[0])
if !ok {
os.Exit(1)
return nil
}
if quoted {
fmt.Printf("'%s'\n", singleQuoteEscape(value))
} else {
fmt.Printf("%s\n", value)
}
return nil
}
// SubKeys implements the logic for config:keys without app name validation
func SubKeys(appName string, merged bool) error {
env := getEnvironment(appName, merged)
for _, k := range env.Keys() {
fmt.Println(k)
}
return nil
}
// SubSet implements the logic for config:set without app name validation
func SubSet(appName string, pairs []string, noRestart bool, encoded bool) error {
if len(pairs) == 0 {
return errors.New("At least one env pair must be given")
}
updated := make(map[string]string)
for _, e := range pairs {
parts := strings.SplitN(e, "=", 2)
if len(parts) == 1 {
return fmt.Errorf("Invalid env pair: %v", e)
}
key, value := parts[0], parts[1]
if encoded {
decoded, err := base64.StdEncoding.DecodeString(value)
if err != nil {
return fmt.Errorf("%s for key '%s'", err.Error(), key)
}
value = string(decoded)
}
updated[key] = value
}
return SetMany(appName, updated, !noRestart)
}
// SubShow implements the logic for config:show without app name validation
func SubShow(appName string, merged bool, shell bool, export bool) error {
env := getEnvironment(appName, merged)
if shell && export {
return errors.New("Only one of --shell and --export can be given")
}
if shell {
common.LogWarn("Deprecated: Use 'config:export --format shell' instead")
fmt.Print(env.Export(ExportFormatShell))
} else if export {
common.LogWarn("Deprecated: Use 'config:export --format exports' instead")
fmt.Println(env.Export(ExportFormatExports))
} else {
contextName := "global"
if appName != "" {
contextName = appName
}
common.LogInfo2Quiet(contextName + " env vars")
fmt.Println(env.Export(ExportFormatPretty))
}
return nil
}
// SubUnset implements the logic for config:unset without app name validation
func SubUnset(appName string, keys []string, noRestart bool) error {
if len(keys) == 0 {
return fmt.Errorf("At least one key must be given")
}
return UnsetMany(appName, keys, !noRestart)
}

View File

@@ -0,0 +1,112 @@
package main
import (
"fmt"
"os"
"github.com/dokku/dokku/plugins/common"
"github.com/dokku/dokku/plugins/config"
flag "github.com/spf13/pflag"
)
func getKeys(args []string, global bool) []string {
keys := args
if !global && len(keys) > 0 {
keys = keys[1:]
}
return keys
}
// main entrypoint to all subcommands
func main() {
action := os.Args[1]
var err error
appName := "--global"
switch action {
case "bundle":
args := flag.NewFlagSet("bundle", flag.ExitOnError)
global := args.Bool("global", false, "--global: use the global environment")
merged := args.Bool("merged", false, "--merged: merge app environment and global environment")
args.Parse(os.Args[2:])
if !*global {
appName = args.Arg(0)
}
err = config.SubBundle(appName, *merged)
case "clear":
args := flag.NewFlagSet("clear", 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:])
if !*global {
appName = args.Arg(0)
}
err = config.SubClear(appName, *noRestart)
case "export":
args := flag.NewFlagSet("export", flag.ExitOnError)
global := args.Bool("global", false, "--global: use the global environment")
merged := args.Bool("merged", false, "--merged: merge app environment and global environment")
format := args.String("format", "exports", "--format: [ docker-args | docker-args-keys | exports | envfile | json | json-list | pack-keys | pretty | shell ] which format to export as)")
args.Parse(os.Args[2:])
if !*global {
appName = args.Arg(0)
}
err = config.SubExport(appName, *merged, *format)
case "get":
args := flag.NewFlagSet("get", flag.ExitOnError)
global := args.Bool("global", false, "--global: use the global environment")
quoted := args.Bool("quoted", false, "--quoted: get the value quoted")
args.Parse(os.Args[2:])
if !*global {
appName = args.Arg(0)
}
keys := getKeys(args.Args(), *global)
err = config.SubGet(appName, keys, *quoted)
case "keys":
args := flag.NewFlagSet("keys", flag.ExitOnError)
global := args.Bool("global", false, "--global: use the global environment")
merged := args.Bool("merged", false, "--merged: merge app environment and global environment")
args.Parse(os.Args[2:])
if !*global {
appName = args.Arg(0)
}
err = config.SubKeys(appName, *merged)
case "show":
args := flag.NewFlagSet("show", flag.ExitOnError)
global := args.Bool("global", false, "--global: use the global environment")
merged := args.Bool("merged", false, "--merged: display the app's environment merged with the global environment")
args.Parse(os.Args[2:])
if !*global {
appName = args.Arg(0)
}
err = config.SubShow(appName, *merged, false, false)
case "set":
args := flag.NewFlagSet("set", flag.ExitOnError)
global := args.Bool("global", false, "--global: use the global environment")
encoded := args.Bool("encoded", false, "--encoded: interpret VALUEs as base64")
noRestart := args.Bool("no-restart", false, "--no-restart: no restart")
args.Parse(os.Args[2:])
if !*global {
appName = args.Arg(0)
}
pairs := getKeys(args.Args(), *global)
err = config.SubSet(appName, pairs, *noRestart, *encoded)
case "unset":
args := flag.NewFlagSet("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:])
if !*global {
appName = args.Arg(0)
}
keys := getKeys(args.Args(), *global)
err = config.SubUnset(appName, keys, *noRestart)
default:
err = fmt.Errorf("Invalid plugin config_sub call: %s", action)
}
if err != nil {
common.LogFailWithError(err)
}
}

View File

@@ -1,156 +1,83 @@
package config package config
import ( // CommandBundle creates a tarball of a .env.d directory
"encoding/base64" // containing env vars for the app
"errors"
"fmt"
"os"
"strings"
"github.com/dokku/dokku/plugins/common"
)
// CommandBundle implements config:bundle
func CommandBundle(appName string, global bool, merged bool) error { func CommandBundle(appName string, global bool, merged bool) error {
appName, err := getAppNameOrGlobal(appName, global) appName, err := getAppNameOrGlobal(appName, global)
if err != nil { if err != nil {
return err return err
} }
env := getEnvironment(appName, merged) return SubBundle(appName, merged)
return env.ExportBundle(os.Stdout)
} }
// CommandClear implements config:clear // CommandClear unsets all environment variables in use
func CommandClear(appName string, global bool, noRestart bool) error { func CommandClear(appName string, global bool, noRestart bool) error {
appName, err := getAppNameOrGlobal(appName, global) appName, err := getAppNameOrGlobal(appName, global)
if err != nil { if err != nil {
return err return err
} }
return UnsetAll(appName, !noRestart) return SubClear(appName, noRestart)
} }
// CommandExport implements config:export // CommandExport outputs all env vars (merged or not, global or not)
// in the specified format for consumption by other tools
func CommandExport(appName string, global bool, merged bool, format string) error { func CommandExport(appName string, global bool, merged bool, format string) error {
return export(appName, global, merged, format) appName, err := getAppNameOrGlobal(appName, global)
if err != nil {
return err
}
return SubExport(appName, merged, format)
} }
// CommandGet implements config:get // CommandGet gets the value for the specified environment variable
func CommandGet(appName string, keys []string, global bool, quoted bool) error { func CommandGet(appName string, keys []string, global bool, quoted bool) error {
appName, err := getAppNameOrGlobal(appName, global) appName, err := getAppNameOrGlobal(appName, global)
if err != nil { if err != nil {
return err return err
} }
if len(keys) == 0 { return SubGet(appName, keys, quoted)
return errors.New("Expected: key")
}
if len(keys) != 1 {
return fmt.Errorf("Unexpected argument(s): %v", keys[1:])
}
value, ok := Get(appName, keys[0])
if !ok {
os.Exit(1)
return nil
}
if quoted {
fmt.Printf("'%s'\n", singleQuoteEscape(value))
} else {
fmt.Printf("%s\n", value)
}
return nil
} }
// CommandKeys implements config:keys // CommandKeys shows the keys set for the specified environment
func CommandKeys(appName string, global bool, merged bool) error { func CommandKeys(appName string, global bool, merged bool) error {
appName, err := getAppNameOrGlobal(appName, global) appName, err := getAppNameOrGlobal(appName, global)
if err != nil { if err != nil {
return err return err
} }
env := getEnvironment(appName, merged) return SubKeys(appName, merged)
for _, k := range env.Keys() {
fmt.Println(k)
}
return nil
} }
// CommandSet implements config:set // CommandSet sets one or more environment variable pairs
func CommandSet(appName string, pairs []string, global bool, noRestart bool, encoded bool) error { func CommandSet(appName string, pairs []string, global bool, noRestart bool, encoded bool) error {
appName, err := getAppNameOrGlobal(appName, global) appName, err := getAppNameOrGlobal(appName, global)
if err != nil { if err != nil {
return err return err
} }
if len(pairs) == 0 { return SubSet(appName, pairs, noRestart, encoded)
return errors.New("At least one env pair must be given")
}
updated := make(map[string]string)
for _, e := range pairs {
parts := strings.SplitN(e, "=", 2)
if len(parts) == 1 {
return fmt.Errorf("Invalid env pair: %v", e)
}
key, value := parts[0], parts[1]
if encoded {
decoded, err := base64.StdEncoding.DecodeString(value)
if err != nil {
return fmt.Errorf("%s for key '%s'", err.Error(), key)
}
value = string(decoded)
}
updated[key] = value
}
return SetMany(appName, updated, !noRestart)
} }
// CommandShow implements config:show // CommandShow pretty-prints the specified environment vaiables
func CommandShow(appName string, global bool, merged bool, shell bool, export bool) error { func CommandShow(appName string, global bool, merged bool, shell bool, export bool) error {
appName, err := getAppNameOrGlobal(appName, global) appName, err := getAppNameOrGlobal(appName, global)
if err != nil { if err != nil {
return err return err
} }
env := getEnvironment(appName, merged) return SubShow(appName, merged, shell, export)
if shell && export {
return errors.New("Only one of --shell and --export can be given")
}
if shell {
common.LogWarn("Deprecated: Use 'config:export --format shell' instead")
fmt.Print(env.Export(ExportFormatShell))
} else if export {
common.LogWarn("Deprecated: Use 'config:export --format exports' instead")
fmt.Println(env.Export(ExportFormatExports))
} else {
contextName := "global"
if appName != "" {
contextName = appName
}
common.LogInfo2Quiet(contextName + " env vars")
fmt.Println(env.Export(ExportFormatPretty))
}
return nil
} }
// CommandUnset implements config:unset // CommandUnset unsets one or more keys in a specified environment
func CommandUnset(appName string, keys []string, global bool, noRestart bool) error { func CommandUnset(appName string, keys []string, global bool, noRestart bool) error {
appName, err := getAppNameOrGlobal(appName, global) appName, err := getAppNameOrGlobal(appName, global)
if err != nil { if err != nil {
return err return err
} }
if len(keys) == 0 { return SubUnset(appName, keys, noRestart)
return fmt.Errorf("At least one key must be given")
}
return UnsetMany(appName, keys, !noRestart)
} }

View File

@@ -16,7 +16,13 @@ func TriggerConfigExport(appName string, global string, merged string, format st
if err != nil { if err != nil {
return err return err
} }
return export(appName, g, m, format)
appName, err = getAppNameOrGlobal(appName, g)
if err != nil {
return err
}
return export(appName, m, format)
} }
// TriggerConfigGet returns an app config value by key // TriggerConfigGet returns an app config value by key