grim/convey

7fd7299d950f
Added tag v0.10.1 for changeset 6fcf770a1b72
/*
* 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 state
import (
"errors"
"fmt"
"os"
"strings"
"time"
"bitbucket.org/rw_grim/convey/environment"
"bitbucket.org/rw_grim/convey/network"
"bitbucket.org/rw_grim/convey/workspace"
)
type State struct {
Network network.Network
Workspace workspace.Workspace
KeepWorkspace bool
ForceSequential bool
EnableSSHAgent bool
TaskTimeout time.Duration
Environment []string
ExtendFrames []extendFrame
DockerConfig string
CPUShares string
Memory string
DetachedContainers []string
}
type extendFrame struct {
expandable []string
delimiter string
stashedEnv []string
}
var errEmptyExtendStack = errors.New("empty extend stack")
func (st *State) Valid() error {
if st.EnableSSHAgent {
if val := os.Getenv("SSH_AUTH_SOCK"); val == "" {
return fmt.Errorf("ssh-agent forwarding requested, but agent not running")
}
}
return nil
}
// MapSlice calls SliceMapper on the given environment, but also checks to
// see if the variable could be expanded into a list.
func (st *State) MapSlice(env, fullEnv []string) ([]string, error) {
if len(env) == 1 {
// If we only have one thing and it looks like $VAR or ${VAR}, then
// see if we have it in the list of things we can expand on the stack.
if delimiter, ok := st.getDelimiter(getName(env[0])); ok {
mapped, err := environment.Mapper(env[0], fullEnv)
if err != nil {
return nil, err
}
// Split it!
return strings.SplitN(mapped, delimiter, -1), nil
}
}
return environment.SliceMapper(env, fullEnv)
}
// GetDelimiter returns the highest (outermost extend task) delimiter registered
// with a given expandable environment variable. Returns true if found by name.
func (st *State) getDelimiter(name string) (string, bool) {
for i := len(st.ExtendFrames) - 1; i >= 0; i-- {
frame := st.ExtendFrames[i]
for _, expandable := range frame.expandable {
if expandable == name {
return frame.delimiter, true
}
}
}
return "", false
}
// PushExtendFrame will store the set of expandable environment variables
// and the current set of environments which are restored on pop. These frames
// are used to map a slice within an extended task.
func (st *State) PushExtendFrame(env, expandable []string, delimiter string) {
st.ExtendFrames = append(st.ExtendFrames, extendFrame{
expandable: expandable,
delimiter: delimiter,
stashedEnv: st.Environment,
})
// Merge the environment into this map, but do NOT override anything that
// is currently in the state's enviornment - this has higher precedence.
st.Environment = environment.Merge(env, st.Environment)
}
// PopExtendFrame will remove one frame from the top of the stack.
func (st *State) PopExtendFrame() error {
idx := len(st.ExtendFrames) - 1
if idx < 0 {
return errEmptyExtendStack
}
last := st.ExtendFrames[idx]
st.ExtendFrames = st.ExtendFrames[:idx]
st.Environment = last.stashedEnv
return nil
}
// getName returns the name of the variable `var` if the string is of the
// form $var or ${var}.
func getName(env string) string {
env = strings.TrimSpace(env)
if strings.HasPrefix(env, "$") {
return strings.Trim(env[1:], "{}")
}
return ""
}