[config] refactor package structure
This commit is contained in:
@@ -7,9 +7,6 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
"github.com/dokku/dokku/plugins/config/src/configenv"
|
||||
|
||||
"github.com/dokku/dokku/plugins/config"
|
||||
columnize "github.com/ryanuber/columnize"
|
||||
)
|
||||
@@ -43,25 +40,9 @@ func main() {
|
||||
global := args.Bool("global", false, "--global: use the global environment")
|
||||
shell := args.Bool("shell", false, "--shell: in a single-line for usage in command-line utilities [deprecated]")
|
||||
export := args.Bool("export", false, "--export: print the env as eval-compatible exports [deprecated]")
|
||||
merged := args.Bool("merged", false, "--merged: display the app's envionment merged with the global environment")
|
||||
args.Parse(os.Args[2:])
|
||||
appName, _ := config.GetCommonArgs(*global, args.Args())
|
||||
env := config.GetConfig(appName, false)
|
||||
|
||||
if *shell && *export {
|
||||
common.LogFail("Only one of --shell and --export can be given")
|
||||
}
|
||||
if *shell {
|
||||
fmt.Print(env.Export(configenv.Shell))
|
||||
} else if *export {
|
||||
fmt.Println(env.Export(configenv.Exports))
|
||||
} else {
|
||||
contextName := "global"
|
||||
if appName != "" {
|
||||
contextName = appName
|
||||
}
|
||||
common.LogInfo2(contextName + " config vars")
|
||||
fmt.Println(env.Export(configenv.Pretty))
|
||||
}
|
||||
config.CommandShow(args.Args(), *global, *shell, *export, *merged)
|
||||
case "config:help":
|
||||
case "help":
|
||||
usage()
|
||||
|
||||
@@ -1,261 +0,0 @@
|
||||
package configenv
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"archive/tar"
|
||||
|
||||
"os"
|
||||
|
||||
common "github.com/dokku/dokku/plugins/common"
|
||||
godotenv "github.com/joho/godotenv"
|
||||
"github.com/ryanuber/columnize"
|
||||
)
|
||||
|
||||
//ExportFormat types of possible exports
|
||||
type ExportFormat int
|
||||
|
||||
const (
|
||||
//Exports format: Sourceable exports
|
||||
Exports ExportFormat = iota
|
||||
//Envfile format: dotenv file
|
||||
Envfile
|
||||
//DockerArgs format: --env args for docker
|
||||
DockerArgs
|
||||
//Shell format: env arguments for shell
|
||||
Shell
|
||||
//Pretty format: pretty-printed in columns
|
||||
Pretty
|
||||
)
|
||||
|
||||
//Env is a representation for global or app environment
|
||||
type Env struct {
|
||||
name string
|
||||
filename string
|
||||
env map[string]string
|
||||
}
|
||||
|
||||
//NewFromString creates an env from the given ENVFILE contents representation
|
||||
func NewFromString(rep string) (env *Env, err error) {
|
||||
envMap, err := godotenv.Unmarshal(rep)
|
||||
env = &Env{
|
||||
"<unknown>",
|
||||
"",
|
||||
envMap,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//LoadApp loads an environment for the given app
|
||||
func LoadApp(appName string) (env *Env, err error) {
|
||||
appfile, err := getAppFile(appName)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return loadFromFile(appName, appfile)
|
||||
}
|
||||
|
||||
//LoadGlobal loads the global environmen
|
||||
func LoadGlobal() (*Env, error) {
|
||||
return loadFromFile("global", getGlobalFile())
|
||||
}
|
||||
|
||||
//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 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"
|
||||
}
|
||||
|
||||
//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() (keys []string) {
|
||||
keys = make([]string, 0, len(e.env))
|
||||
for k := range e.env {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
return
|
||||
}
|
||||
|
||||
//Len return the number of items in this environment
|
||||
func (e *Env) Len() int {
|
||||
return len(e.env)
|
||||
}
|
||||
|
||||
//Map return the Env as a map
|
||||
func (e *Env) Map() map[string]string {
|
||||
return e.env
|
||||
}
|
||||
|
||||
func (e *Env) String() string {
|
||||
return e.EnvfileString()
|
||||
}
|
||||
|
||||
//Merge merges the given environment on top of the reciever
|
||||
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
|
||||
func (e *Env) Write() error {
|
||||
if e.filename == "" {
|
||||
return errors.New("this Env was created unbound to a file")
|
||||
}
|
||||
return godotenv.Write(e.Map(), e.filename)
|
||||
}
|
||||
|
||||
//Export the Env in the given format
|
||||
func (e *Env) Export(format ExportFormat) string {
|
||||
switch format {
|
||||
case Exports:
|
||||
return e.ExportfileString()
|
||||
case Envfile:
|
||||
return e.EnvfileString()
|
||||
case DockerArgs:
|
||||
return e.DockerArgsString()
|
||||
case Shell:
|
||||
return e.ShellString()
|
||||
case Pretty:
|
||||
return PrettyPrintEnvEntries("", e.Map())
|
||||
default:
|
||||
common.LogFail(fmt.Sprintf("Unknown export format: %v", format))
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
//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
|
||||
func (e *Env) ExportfileString() string {
|
||||
return e.stringWithPrefixAndSeparator("export ", "\n", true)
|
||||
}
|
||||
|
||||
//DockerArgsString gets the contents of this Env in the form -env=KEY=VALUE --env...
|
||||
func (e *Env) DockerArgsString() string {
|
||||
return e.stringWithPrefixAndSeparator("--env=", " ", true)
|
||||
}
|
||||
|
||||
//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("", " ", true)
|
||||
}
|
||||
|
||||
//ExportBundle writes a tarfile of the environmnet 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 {
|
||||
tarfile := tar.NewWriter(dest)
|
||||
defer tarfile.Close()
|
||||
|
||||
for _, k := range e.Keys() {
|
||||
val, _ := e.Get(k)
|
||||
valbin := []byte(val)
|
||||
|
||||
header := &tar.Header{
|
||||
Name: k,
|
||||
Mode: 0600,
|
||||
Size: int64(len(valbin)),
|
||||
}
|
||||
tarfile.WriteHeader(header)
|
||||
tarfile.Write(valbin)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//SingleQuoteEscape escapes the value as if it were shell-quoted in single quotes
|
||||
func SingleQuoteEscape(value string) string { // so that 'esc'apped' -> 'esc'\''aped'
|
||||
return strings.Replace(value, "'", "'\\''", -1)
|
||||
}
|
||||
|
||||
//PrettyPrintEnvEntries in columns
|
||||
func PrettyPrintEnvEntries(prefix string, entries map[string]string) (representation string) {
|
||||
colConfig := columnize.DefaultConfig()
|
||||
colConfig.Prefix = prefix
|
||||
colConfig.Delim = "\x00"
|
||||
lines := make([]string, 0, len(entries))
|
||||
for k, v := range entries {
|
||||
lines = append(lines, fmt.Sprintf("%s:\x00%s", k, v))
|
||||
}
|
||||
return columnize.Format(lines, colConfig)
|
||||
}
|
||||
|
||||
func loadFromFile(name string, filename string) (env *Env, err error) {
|
||||
envMap := make(map[string]string)
|
||||
if _, err := os.Stat(filename); err == nil {
|
||||
envMap, err = godotenv.Read(filename)
|
||||
}
|
||||
|
||||
env = &Env{
|
||||
name,
|
||||
filename,
|
||||
envMap,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//stringWithPrefixAndSeparator makes a string of the environment
|
||||
// with the given prefix and separator for each entry
|
||||
func (e *Env) stringWithPrefixAndSeparator(prefix string, separator string, allowNewlines bool) string {
|
||||
keys := e.Keys()
|
||||
entries := make([]string, len(keys))
|
||||
for i, k := range keys {
|
||||
v := SingleQuoteEscape(e.env[k])
|
||||
if !allowNewlines {
|
||||
v = strings.Replace(v, "\n", "'$'\\n''", -1)
|
||||
}
|
||||
entries[i] = fmt.Sprintf("%s%s='%s'", prefix, k, v)
|
||||
}
|
||||
return strings.Join(entries, separator)
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package configenv
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestExportfileRoundtrip(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
env, err := NewFromString("HI='ho'\nFoo='Bar'\n\nBaz='BOFF'")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(env.Map()).To(Equal(pairs("Baz", "BOFF", "Foo", "Bar", "HI", "ho")))
|
||||
Expect(env.String()).To(Equal("Baz=\"BOFF\"\nFoo=\"Bar\"\nHI=\"ho\""))
|
||||
|
||||
env, err = NewFromString(`export HI="h\no"`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(env.Map()).To(Equal(pairs("HI", "h\no")))
|
||||
Expect(env.String()).To(Equal(`HI="h\no"`))
|
||||
|
||||
env, err = NewFromString("HI='ho'\nFOO=\"'\\nBAR=''\"")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(env.Map()).To(Equal(pairs("HI", "ho", "FOO", "'\nBAR=''")))
|
||||
Expect(env.String()).To(Equal("FOO=\"'\\nBAR=''\"\nHI=\"ho\""))
|
||||
|
||||
env, err = NewFromString("FOO='bar' ")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(env.Map()).To(Equal(pairs("FOO", "bar")))
|
||||
Expect(env.String()).To(Equal(`FOO="bar"`))
|
||||
|
||||
}
|
||||
|
||||
func TestExportfileErrors(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
|
||||
_, err := NewFromString("F\nOO='bar'") //keys cannot have embedded newlines
|
||||
Expect(err).To(HaveOccurred())
|
||||
}
|
||||
|
||||
func TestMerge(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
e, _ := NewFromString("FOO='bar'")
|
||||
e2, _ := NewFromString("BAR='baz'")
|
||||
e.Merge(e2)
|
||||
Expect(e.Map()).To(Equal(pairs("BAR", "baz", "FOO", "bar")))
|
||||
}
|
||||
|
||||
func TestArrayExport(t *testing.T) {
|
||||
RegisterTestingT(t)
|
||||
e, _ := NewFromString("BAR='BAZ'\nFOO='b'ar '")
|
||||
Expect(e.EnvfileString()).To(Equal("BAR=\"BAZ\"\nFOO=\"b'ar \""))
|
||||
Expect(e.DockerArgsString()).To(Equal("--env=BAR='BAZ' --env=FOO='b'\\''ar '"))
|
||||
Expect(e.ShellString()).To(Equal("BAR='BAZ' FOO='b'\\''ar '"))
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -2,10 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
"github.com/dokku/dokku/plugins/config"
|
||||
)
|
||||
|
||||
@@ -14,11 +12,5 @@ func main() {
|
||||
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:])
|
||||
|
||||
appName, trailingArgs := config.GetCommonArgs(*global, args.Args())
|
||||
if len(trailingArgs) > 0 {
|
||||
common.LogFail(fmt.Sprintf("Trailing argument(s): %v", trailingArgs))
|
||||
}
|
||||
config := config.GetConfig(appName, *merged)
|
||||
config.ExportBundle(os.Stdout)
|
||||
config.CommandBundle(args.Args(), *global, *merged)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
common "github.com/dokku/dokku/plugins/common"
|
||||
"github.com/dokku/dokku/plugins/config"
|
||||
"github.com/dokku/dokku/plugins/config/src/configenv"
|
||||
)
|
||||
|
||||
// print the environment to stdout
|
||||
@@ -19,28 +16,5 @@ func main() {
|
||||
merged := args.Bool("merged", false, "--merged: merge app environment and global environment")
|
||||
format := args.String("format", "exports", "--format: [ exports | envfile | docker-args | shell ] which format to export as)")
|
||||
args.Parse(os.Args[2:])
|
||||
|
||||
appName, trailingArgs := config.GetCommonArgs(*global, args.Args())
|
||||
if len(trailingArgs) > 0 {
|
||||
common.LogFail(fmt.Sprintf("Trailing argument(s): %v", trailingArgs))
|
||||
}
|
||||
|
||||
env := config.GetConfig(appName, *merged)
|
||||
exportType := configenv.Exports
|
||||
suffix := "\n"
|
||||
switch *format {
|
||||
case "exports":
|
||||
exportType = configenv.Exports
|
||||
case "envfile":
|
||||
exportType = configenv.Envfile
|
||||
case "docker-args":
|
||||
exportType = configenv.DockerArgs
|
||||
case "shell":
|
||||
exportType = configenv.Shell
|
||||
suffix = " "
|
||||
default:
|
||||
common.LogFail(fmt.Sprintf("Unknown export format: %v", *format))
|
||||
}
|
||||
exported := env.Export(exportType)
|
||||
fmt.Print(exported + suffix)
|
||||
config.CommandExport(args.Args(), *global, *merged, *format)
|
||||
}
|
||||
|
||||
@@ -2,12 +2,9 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
common "github.com/dokku/dokku/plugins/common"
|
||||
"github.com/dokku/dokku/plugins/config"
|
||||
"github.com/dokku/dokku/plugins/config/src/configenv"
|
||||
)
|
||||
|
||||
// get the given entries from the specified environment
|
||||
@@ -16,22 +13,5 @@ func main() {
|
||||
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:])
|
||||
|
||||
appName, keys := config.GetCommonArgs(*global, args.Args())
|
||||
if len(keys) > 1 {
|
||||
common.LogFail(fmt.Sprintf("Unexpected argument(s): %v", keys[1:]))
|
||||
}
|
||||
if len(keys) == 0 {
|
||||
common.LogFail("Expected: key")
|
||||
}
|
||||
if !config.HasKey(appName, keys[0]) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
value := config.GetWithDefault(appName, keys[0], "")
|
||||
if *quoted {
|
||||
fmt.Printf("'%s'", configenv.SingleQuoteEscape(value))
|
||||
} else {
|
||||
fmt.Printf("%s", value)
|
||||
}
|
||||
config.CommandGet(args.Args(), *global, *quoted)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,8 @@ package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
"github.com/dokku/dokku/plugins/config"
|
||||
)
|
||||
|
||||
@@ -14,13 +12,5 @@ func main() {
|
||||
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:])
|
||||
|
||||
appName, trailingArgs := config.GetCommonArgs(*global, args.Args())
|
||||
if len(trailingArgs) > 0 {
|
||||
common.LogFail(fmt.Sprintf("Trailing argument(s): %v", trailingArgs))
|
||||
}
|
||||
config := config.GetConfig(appName, *merged)
|
||||
for _, k := range config.Keys() {
|
||||
fmt.Println(k)
|
||||
}
|
||||
config.CommandKeys(args.Args(), *global, *merged)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
common "github.com/dokku/dokku/plugins/common"
|
||||
config "github.com/dokku/dokku/plugins/config"
|
||||
"github.com/dokku/dokku/plugins/config"
|
||||
)
|
||||
|
||||
// set the given entries from the specified environment
|
||||
@@ -18,23 +14,5 @@ func main() {
|
||||
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:])
|
||||
appName, pairs := config.GetCommonArgs(*global, args.Args())
|
||||
|
||||
updated := make(map[string]string)
|
||||
for _, e := range pairs {
|
||||
parts := strings.SplitN(e, "=", 2)
|
||||
if len(parts) == 1 {
|
||||
common.LogFail("Invalid env pair: " + e)
|
||||
}
|
||||
key, value := parts[0], parts[1]
|
||||
if *encoded {
|
||||
decoded, err := base64.StdEncoding.DecodeString(value)
|
||||
if err != nil {
|
||||
common.LogFail(fmt.Sprintf("%s for key '%s'", err.Error(), key))
|
||||
}
|
||||
value = string(decoded)
|
||||
}
|
||||
updated[key] = value
|
||||
}
|
||||
config.SetMany(appName, updated, !*noRestart)
|
||||
config.CommandSet(args.Args(), *global, *noRestart, *encoded)
|
||||
}
|
||||
|
||||
@@ -12,8 +12,6 @@ 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:])
|
||||
appName, keys := config.GetCommonArgs(*global, args.Args())
|
||||
config.UnsetMany(appName, keys, !*noRestart)
|
||||
config.CommandUnset(args.Args(), *global, *noRestart)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user