--- a/docker/build.go Wed Oct 11 09:22:28 2017 -0500
+++ b/docker/build.go Wed Oct 11 19:39:23 2017 -0500
@@ -47,7 +47,7 @@
func (b *Build) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.Environment)
+ fullEnv := environment.Merge(env, st.GetEnv()) files, err := st.MapSlice(b.Files, fullEnv)
--- a/docker/export.go Wed Oct 11 09:22:28 2017 -0500
+++ b/docker/export.go Wed Oct 11 19:39:23 2017 -0500
@@ -84,7 +84,7 @@
func (e *Export) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.Environment)
+ fullEnv := environment.Merge(env, st.GetEnv()) files, err := st.MapSlice(e.Files, fullEnv)
--- a/docker/import.go Wed Oct 11 09:22:28 2017 -0500
+++ b/docker/import.go Wed Oct 11 19:39:23 2017 -0500
@@ -33,7 +33,7 @@
const importTemplate = `cp {{.source}} {{.workspaceID}}:/workspace/{{.destination}}`
func (i *Import) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.Environment)
+ fullEnv := environment.Merge(env, st.GetEnv()) files, err := st.MapSlice(i.Files, fullEnv)
--- a/docker/login.go Wed Oct 11 09:22:28 2017 -0500
+++ b/docker/login.go Wed Oct 11 19:39:23 2017 -0500
@@ -34,7 +34,7 @@
const loginTemplate = `login -u {{.username}} -p {{.password}}{{if .server}} {{.server}}{{end}}`
func (l *Login) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.Environment)
+ fullEnv := environment.Merge(env, st.GetEnv()) username, err := environment.Mapper(l.Username, fullEnv)
--- a/docker/logout.go Wed Oct 11 09:22:28 2017 -0500
+++ b/docker/logout.go Wed Oct 11 19:39:23 2017 -0500
@@ -32,7 +32,7 @@
const logoutTemplate = `logout{{if .server}} {{.server}}{{end}}`
func (l *Logout) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.Environment)
+ fullEnv := environment.Merge(env, st.GetEnv()) server, err := environment.Mapper(l.Server, fullEnv)
--- a/docker/pull.go Wed Oct 11 09:22:28 2017 -0500
+++ b/docker/pull.go Wed Oct 11 19:39:23 2017 -0500
@@ -34,7 +34,7 @@
const pullTemplate = `pull {{.image}}`
func (p *Pull) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.Environment)
+ fullEnv := environment.Merge(env, st.GetEnv()) images, err := st.MapSlice(p.Images, fullEnv)
--- a/docker/push.go Wed Oct 11 09:22:28 2017 -0500
+++ b/docker/push.go Wed Oct 11 19:39:23 2017 -0500
@@ -34,7 +34,7 @@
const pushTemplate = `push {{.image}}`
func (p *Push) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.Environment)
+ fullEnv := environment.Merge(env, st.GetEnv()) images, err := st.MapSlice(p.Images, fullEnv)
--- a/docker/remove.go Wed Oct 11 09:22:28 2017 -0500
+++ b/docker/remove.go Wed Oct 11 19:39:23 2017 -0500
@@ -37,7 +37,7 @@
const removeTemplate = `rmi {{.image}}`
func (r *Remove) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.Environment)
+ fullEnv := environment.Merge(env, st.GetEnv()) images, err := st.MapSlice(r.Images, fullEnv)
--- a/docker/run.go Wed Oct 11 09:22:28 2017 -0500
+++ b/docker/run.go Wed Oct 11 19:39:23 2017 -0500
@@ -143,7 +143,7 @@
func (r *Run) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
fullEnv := environment.Merge(env, r.Environment)
- fullEnv = environment.Merge(fullEnv, st.Environment)
+ fullEnv = environment.Merge(fullEnv, st.GetEnv()) user, err := user.Current()
--- a/docker/tag.go Wed Oct 11 09:22:28 2017 -0500
+++ b/docker/tag.go Wed Oct 11 19:39:23 2017 -0500
@@ -35,7 +35,7 @@
const tagTemplate = `tag {{.source}} {{.destination}}`
func (t *Tag) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.Environment)
+ fullEnv := environment.Merge(env, st.GetEnv()) source, err := environment.Mapper(t.Source, fullEnv)
--- a/environment/mapper_test.go Wed Oct 11 09:22:28 2017 -0500
+++ b/environment/mapper_test.go Wed Oct 11 19:39:23 2017 -0500
@@ -62,11 +62,11 @@
result, err := Mapper("${FOO}", []string{"FOOBAR"})
- Expect(result).To(Equal("$FOO"))
+ Expect(result).To(Equal("${FOO}")) result, err = Mapper("${BARBAZ}", []string{"BAR"})
- Expect(result).To(Equal("$BARBAZ"))
+ Expect(result).To(Equal("${BARBAZ}")) func (e *environmentSuite) TestRecursiveMatch(t sweet.T) {
--- a/intrinsic/clean.go Wed Oct 11 09:22:28 2017 -0500
+++ b/intrinsic/clean.go Wed Oct 11 19:39:23 2017 -0500
@@ -68,7 +68,7 @@
func (c *Clean) Execute(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv := environment.Merge(env, st.Environment)
+ fullEnv := environment.Merge(env, st.GetEnv()) --- a/intrinsic/extend_test.go Wed Oct 11 09:22:28 2017 -0500
+++ b/intrinsic/extend_test.go Wed Oct 11 19:39:23 2017 -0500
@@ -30,11 +30,13 @@
- st = &state.State{Environment: []string{"bar=bonk", "quux=honk"}}
+ st.MergeEnv([]string{"bar=bonk", "quux=honk"}) task.executeFn = func(name string, logger *gomol.LogAdapter, env []string, st *state.State) error {
- fullEnv = environment.Merge(environment.Merge(env, []string{"x=1", "foo=quux"}), st.Environment)
+ fullEnv = environment.Merge(environment.Merge(env, []string{"x=1", "foo=quux"}), st.GetEnv()) @@ -48,8 +50,8 @@
Expect(fullEnv).To(ConsistOf("x=1", "foo=bar", "bar=bonk", "quux=honk"))
- Expect(st.Environment).To(HaveLen(2))
- Expect(st.Environment).To(ConsistOf("bar=bonk", "quux=honk"))
+ Expect(st.GetEnv()).To(HaveLen(2)) + Expect(st.GetEnv()).To(ConsistOf("bar=bonk", "quux=honk")) func (s *intrinsicSuite) TestDependencies(t sweet.T) {
--- a/main.go Wed Oct 11 09:22:28 2017 -0500
+++ b/main.go Wed Oct 11 19:39:23 2017 -0500
@@ -136,10 +136,10 @@
st.ForceSequential = *forceSequential
st.EnableSSHAgent = enableSSHAgent
st.TaskTimeout = *taskTimeout
- st.Environment = environment.Merge(defEnv, *env)
st.DockerConfig = *dockerConfig
st.CPUShares = *cpuShares
+ st.MergeEnv(environment.Merge(defEnv, *env)) if err := st.Valid(); err != nil {
--- a/state/state.go Wed Oct 11 09:22:28 2017 -0500
+++ b/state/state.go Wed Oct 11 19:39:23 2017 -0500
@@ -39,12 +39,22 @@
TaskTimeout time.Duration
+ // Guard the environment around accessors so that the base + // environment can be modified while allowing all "wrapped" + // environments to get the same updates. + // Additional environment variables that are applied from + // the wrapper around the base env. This is applied on demand + // to the base environment so that we don't keep around a // States have the ability to "wrap" another one without
// changing the underlying state. This is used by the
// extends intrinsic in order to modify the stat without
@@ -61,18 +71,17 @@
// multiple goroutines, so this needs to be guarded via
detachedContainers map[string]struct{}
detachedContainers: map[string]struct{}{},
- mutex: &sync.RWMutex{},
func (st *State) Valid() error {
- if st.parent == nil && (st.detachedContainers == nil || st.mutex == nil) {
+ if st.parent == nil && st.detachedContainers == nil { return fmt.Errorf("state must be constructed via New")
@@ -85,10 +94,48 @@
+// GetEnv gets the fullEnv with the wrapped environments applied. +func (st *State) GetEnv() []string { + // fmt.Printf("I/O > %#v %#v\n", st.innerEnv, st.outerEnv) + // fmt.Printf("I/O > %#v %#v\n", st.parent.innerEnv, st.parent.outerEnv) + // fmt.Printf("I/O > %#v %#v\n", st.parent.parent.innerEnv, st.parent.parent.outerEnv) + return st.mergeRecursive(st.getInnerEnv()) +// getInnerEnv retrieves the original state environment (without wrapping). +func (st *State) getInnerEnv() []string { + return st.parent.getInnerEnv() +// mergeRecursive applies the wrapped environments recursively. +func (st *State) mergeRecursive(env []string) []string { + // Merge recursively and then apply the wrapped environment so that + // the wrapping takes the highest precedence. + return environment.Merge(st.outerEnv, st.parent.mergeRecursive(env)) +// MergeEnv adds additional environment arguments to the original state environment. +func (st *State) MergeEnv(env []string) { + st.parent.MergeEnv(env) + st.innerEnv = environment.Merge(st.innerEnv, env) // MapSlice calls SliceMapper on the given environment, but also checks to
// see if the variable in the env parameter can be expanded into a list.
-func (st *State) MapSlice(env, fullEnv []string) ([]string, error) {
+func (st *State) MapSlice(slice, fullEnv []string) ([]string, error) { // Protect ourselves against a weird infinite expansion. This can
// happen if something occurs like X => A,$Y; Y => B,$X. This is
@@ -103,13 +150,13 @@
// If we haven't made a change, return the final result.
+ return environment.SliceMapper(next, fullEnv) - return nil, fmt.Errorf("hit limit while expanding '%s'", env)
+ return nil, fmt.Errorf("hit limit while expanding '%s'", slice) func (st *State) expandSlice(env, fullEnv []string) ([]string, error) {
@@ -199,10 +246,6 @@
// the same as ignoring the wrapped values and using the underlying state. This stack
// is used to map a slice within an extended task.
func (st *State) WrapWithExpandableEnv(env, expandable []string, delimiter string) *State {
- // Merge the environment into this map, but do NOT override anything that
- // is currently in the state's environment - this has higher precedence.
- env = environment.Merge(env, st.Environment)
@@ -210,10 +253,10 @@
ForceSequential: st.ForceSequential,
EnableSSHAgent: st.EnableSSHAgent,
TaskTimeout: st.TaskTimeout,
DockerConfig: st.DockerConfig,
expandableDelimiter: delimiter,
--- a/state/state_test.go Wed Oct 11 09:22:28 2017 -0500
+++ b/state/state_test.go Wed Oct 11 19:39:23 2017 -0500
@@ -37,11 +37,23 @@
+func (s *stateSuite) TestWrappedEnvironment(t sweet.T) { + st1.MergeEnv([]string{"Y=0"}) + st2 := st1.WrapWithExpandableEnv([]string{"X=1", "Y=1"}, []string{}, ";") + st3 := st2.WrapWithExpandableEnv([]string{"X=2", "Y=2"}, []string{}, ";") + st4 := st3.WrapWithExpandableEnv([]string{"X=3", "Y=3", "Z=3"}, []string{}, ";") + // Things in the "base" env are not override, but things + // that are wrapped take the highest precedence + Expect(st4.GetEnv()).To(ConsistOf([]string{"X=1", "Y=0", "Z=3"})) func (s *stateSuite) TestMapSlice(t sweet.T) {
- st1.Environment = []string{"FOO=BAR"}
+ st1.MergeEnv([]string{"FOO=BAR"}) - Expect(mapEnv(st1, "$X")).To(ConsistOf([]string{"$X"}))
+ Expect(mapEnv(st1, "$X")).To(ConsistOf([]string{"${X}"})) Expect(mapEnv(st1, "$FOO")).To(ConsistOf([]string{"BAR"}))
st2 := st1.WrapWithExpandableEnv([]string{"X=A;B;C"}, []string{"X"}, ";")
@@ -55,8 +67,8 @@
func (s *stateSuite) TestMapSliceComplex(t sweet.T) {
- st.Environment = []string{"FOO=BAR"}
+ st.MergeEnv([]string{"FOO=BAR"}) st = st.WrapWithExpandableEnv([]string{"X=A;B;C", "Y=D;E;F"}, []string{"X", "Y"}, ";")
@@ -80,27 +92,27 @@
func (s *stateSuite) TestMapSliceIterative(t sweet.T) {
- st.Environment = []string{"FOO=BAR"}
+ st.MergeEnv([]string{"FOO=BAR"}) st = st.WrapWithExpandableEnv([]string{"X=A;x$Y", "Y=B;y$Z", "Z=C;D;E"}, []string{"X", "Y", "Z"}, ";")
Expect(mapEnv(st, "$X")).To(ConsistOf([]string{"A", "xB", "xyC", "xyD", "xyE"}))
func (s *stateSuite) TestMapSliceIndirect(t sweet.T) {
- st.Environment = []string{"FOO=BAR"}
+ st.MergeEnv([]string{"FOO=BAR"}) st = st.WrapWithExpandableEnv([]string{"X=$Y", "Y=A;$Z;C", "Z=B;$W", "W=D"}, []string{"X", "Y", "Z"}, ";")
Expect(mapEnv(st, "$X")).To(ConsistOf([]string{"A", "B", "C", "D"}))
func (s *stateSuite) TestMapSliceRecursive(t sweet.T) {
- st.Environment = []string{"FOO=BAR"}
+ st.MergeEnv([]string{"FOO=BAR"}) st = st.WrapWithExpandableEnv([]string{"X=A;x$Y", "Y=B;y$Z", "Z=C;D;$X"}, []string{"X", "Y", "Z"}, ";")
- _, err := st.MapSlice([]string{"$X"}, st.Environment)
+ _, err := st.MapSlice([]string{"$X"}, st.GetEnv()) Expect(err).NotTo(BeNil())
Expect(err.Error()).To(ContainSubstring("hit limit"))
@@ -135,7 +147,7 @@
func (s *stateSuite) TestWrapParent(t sweet.T) {
st1.WrapWithExpandableEnv(nil, nil, "")
Expect(st1.parent).To(BeNil())
@@ -144,14 +156,14 @@
func (s *stateSuite) TestWrapWithExpandableEnvMap(t sweet.T) {
- st1.Environment = []string{"FOO=BAR", "BAR=BAZ"}
- Expect(st1.Environment).To(HaveLen(2))
- Expect(st1.Environment).To(ConsistOf([]string{"FOO=BAR", "BAR=BAZ"}))
+ st1.MergeEnv([]string{"FOO=BAR", "BAR=BAZ"}) + Expect(st1.GetEnv()).To(HaveLen(2)) + Expect(st1.GetEnv()).To(ConsistOf([]string{"FOO=BAR", "BAR=BAZ"})) st2 := st1.WrapWithExpandableEnv([]string{"FOO=BONK", "BAZ=BONK"}, nil, "")
- Expect(st2.Environment).To(HaveLen(3))
- Expect(st2.Environment).To(ConsistOf([]string{"FOO=BAR", "BAR=BAZ", "BAZ=BONK"}))
+ Expect(st2.GetEnv()).To(HaveLen(3)) + Expect(st2.GetEnv()).To(ConsistOf([]string{"FOO=BAR", "BAR=BAZ", "BAZ=BONK"})) func (s *stateSuite) TestDetached(t sweet.T) {
@@ -173,7 +185,7 @@
func mapEnv(st *State, val string) []string {
- mapped, err := st.MapSlice([]string{val}, st.Environment)
+ mapped, err := st.MapSlice([]string{val}, st.GetEnv())