grim/convey

closing merged branch
inject
2017-10-15, Gary Kramlich
346ffe15612e
closing merged branch
/*
* Convey
* Copyright 2016-2017 Gary Kramlich <grim@reaperworld.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package docker
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
"github.com/aphistic/gomol"
"bitbucket.org/rw_grim/convey/environment"
"bitbucket.org/rw_grim/convey/state"
"bitbucket.org/rw_grim/convey/tasks"
"bitbucket.org/rw_grim/convey/yaml"
)
type Environment struct {
File string `yaml:"from-file"`
Files yaml.StringOrSlice `yaml:"from-files"`
Prefix string `yaml:"prefix"`
}
func (e *Environment) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
fullEnv := environment.Merge(env, st.GetEnv())
prefix, err := environment.Mapper(e.Prefix, fullEnv)
if err != nil {
return err
}
files, err := st.MapSlice(e.Files, fullEnv)
if err != nil {
return err
}
// Create a temp directory we can export files from the current
// container into. We can't read directly from the volume, and
// we don't want to require reading only environment files on
// the host.
tmpDir, err := ioutil.TempDir("", "convey-environment-")
if err != nil {
return err
}
// Remove temp dir on app exit
cleanupFn := st.CleanupList.Add(func() { os.RemoveAll(tmpDir) })
// Call cleanup function on defer, which may instead be called
// if the cleanup thread traps a signal.
defer cleanupFn()
for _, file := range files {
// Export the file into the temp directory and maintain the
// structure of the file (for ease of error messages, so we
// get the file a/b/c/env instead of env).
dest := filepath.Clean(filepath.Join(tmpDir, file))
if err = exportFile(name, st.Workspace.Name(), file, dest, st); err != nil {
return err
}
// Process the entries of the file and apply them to the
// state's base environment immediately.
entries, err := processFile(dest, file, prefix)
if err != nil {
return err
}
st.MergeEnv(entries)
}
return nil
}
func processFile(path, name, prefix string) ([]string, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read environment file '%s'", name)
}
entries := []string{}
for _, line := range strings.Split(string(data), "\n") {
// Allow blank lines
if len(strings.TrimSpace(line)) == 0 {
continue
}
// Each non-empty line requires the form key=val. Split the
// key and the val, then uppercase the key and add the prefix.
// We don't care what form val takes, it will be treated as a
// string (does not need to be quoted).
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("malformed entry in environments file '%s'", line)
}
var (
key = strings.TrimSpace(parts[0])
val = strings.TrimSpace(parts[1])
)
if len(key) == 0 {
return nil, fmt.Errorf("malformed entry in environments file '%s'", line)
}
entries = append(entries, fmt.Sprintf("%s%s=%s", prefix, strings.ToUpper(key), val))
}
return entries, nil
}
func (e *Environment) New() tasks.Task {
return &Environment{}
}
func (e *Environment) Valid() error {
if e.File != "" {
e.Files = append([]string{e.File}, e.Files...)
}
if len(e.Files) == 0 {
return errNoFilesEnvironment
}
return nil
}