--- a/docker/build.go Wed Oct 11 20:57:52 2017 -0500
+++ b/docker/build.go Wed Oct 11 22:00:46 2017 -0500
@@ -59,7 +59,9 @@
- defer os.RemoveAll(tmpDir)
+ // Remove the dir on defer OR on signal exit (first one to happen) + defer st.CleanupList.Add(func() { os.RemoveAll(tmpDir) })() dockerfile, err := environment.Mapper(b.Dockerfile, fullEnv)
--- a/docker/network.go Wed Oct 11 20:57:52 2017 -0500
+++ b/docker/network.go Wed Oct 11 22:00:46 2017 -0500
@@ -33,8 +33,9 @@
- networkCreateTemplate = `network create {{.Name}}`
- networkDestroyTemplate = `network rm {{.Name}}`
+ networkCreateTemplate = `network create {{.Name}}` + networkDisconnectTemplate = `network disconnect --force {{.Name}} {{.ContainerName}}` + networkDestroyTemplate = `network rm {{.Name}}` func NewNetwork(st *state.State) (*Network, error) {
@@ -63,6 +64,23 @@
func (network *Network) Destroy() error {
+ for _, name := range network.state.GetRunning() { + params := map[string]interface{}{ + network.logger.Infof("disconnecting container %s from network %s", name, network.Name()) + err := Docker("disconnect container", networkDisconnectTemplate, params, network.state) + network.logger.Warningf("failed to disconnect container %s from network %s", name, network.Name()) + network.logger.Infof("disconnected container %s from network %s", name, network.Name()) params := map[string]interface{}{
--- a/docker/run.go Wed Oct 11 20:57:52 2017 -0500
+++ b/docker/run.go Wed Oct 11 22:00:46 2017 -0500
@@ -28,6 +28,7 @@
"github.com/aphistic/gomol"
+ "bitbucket.org/rw_grim/convey/cleanup" "bitbucket.org/rw_grim/convey/environment"
"bitbucket.org/rw_grim/convey/normalize"
"bitbucket.org/rw_grim/convey/state"
@@ -64,6 +65,7 @@
const runTemplate = `run --rm
+{{if .Name}} --name {{.Name}}{{end}} {{if .UID}} -e UID={{.UID}}{{end}}
{{if .GID}} -e GID={{.GID}}{{end}}
@@ -89,37 +91,13 @@
{{.Image}}{{if .Command}} {{.Command}}{{end}}`
-// buildScript will create a shell script for the given commands
-func (r *Run) buildScript(st *state.State, fullEnv []string) (string, string, string, error) {
+// writeScript will write a shell script to the given tempfile for the given commands +func (r *Run) writeScript(st *state.State, script *os.File, fullEnv []string) (string, string, string, error) { - // figure out the current working directory
- // now get the absolute path
- absPwd, err := filepath.Abs(pwd)
- // create the temp file to write the script to
- script, err := ioutil.TempFile(absPwd, "convey-script-")
- // set scriptFile to the name of the temp file
- scriptFile := script.Name()
- // set the run command argument to the script file
- commandArg := scriptFile
// Scripts must retain order, so don't use st.MapSlice to
// expand things (which results in a non-deterministically
// ordered slice of the expanded input). It also doesn't
@@ -130,6 +108,12 @@
+ // set scriptFile to the name of the temp file + scriptFile := script.Name() + // set the run command argument to the script file + commandArg := scriptFile // write the script to the file
script.WriteString(strings.Join(scripts, "\n"))
@@ -177,13 +161,31 @@
// if we're using a script defined in the yaml, create it and override
- scriptFile, entryPoint, commandArg, err = r.buildScript(st, fullEnv)
+ // figure out the current working directory + // now get the absolute path + absPwd, err := filepath.Abs(pwd) - // remove the file when the function exits
- defer os.Remove(scriptFile)
+ // create the temp file to write the script to + script, err := ioutil.TempFile(absPwd, "convey-script-") + // Remove the script on defer OR on signal exit (first one to happen) + defer st.CleanupList.Add(func() { os.Remove(script.Name()) })() + scriptFile, entryPoint, commandArg, err = r.writeScript(st, script, fullEnv) taskLabel := normalize.Normalize(
@@ -225,6 +227,9 @@
+ runID := cleanup.ShortID() + logger.Infof("running container with id %s", runID) // build the dict for the template
params := map[string]interface{}{
@@ -247,6 +252,7 @@
"WorkspacePath": workspacePath,
"WorkspaceMount": workspaceMount,
@@ -308,6 +314,14 @@
+ // Mark running so we can detach this container form the network + // when we've got a signal to shutdown. An active network cannot + // be removed, so we need to track things to kill along with it. + // This is no longer a problem once the subprocess returns. + defer st.UnmarkRunning(runID) return Docker(name, runTemplate, params, st)
--- a/main.go Wed Oct 11 20:57:52 2017 -0500
+++ b/main.go Wed Oct 11 22:00:46 2017 -0500
@@ -25,6 +25,7 @@
"github.com/alecthomas/kingpin"
"github.com/aphistic/gomol"
+ "bitbucket.org/rw_grim/convey/cleanup" "bitbucket.org/rw_grim/convey/config"
"bitbucket.org/rw_grim/convey/environment"
"bitbucket.org/rw_grim/convey/loaders/bitbucket"
@@ -54,7 +55,7 @@
listTasks = app.Flag("list-tasks", "List the supported tasks").Short('L').Default("false").Bool()
listMetaPlans = app.Flag("list-meta-plans", "List the meta plans that are available").Short('M').Default("false").Bool()
listPlans = app.Flag("list-plans", "List the plans that are available").Short('P').Default("false").Bool()
- memory = app.Flag("memory", "The ammount of memor to give the run task").Short('m').String()
+ memory = app.Flag("memory", "The amount of memory to give the run task").Short('m').String() showConfig = app.Flag("show-config", "Show a dump of the config file").Short('C').Hidden().Default("false").Bool()
sshAgent = app.Flag("ssh-agent", "A shortcut for --ssh-identity=*").Default("false").Bool()
sshIdentities = app.Flag("ssh-identity", "Enable ssh-agent for the given identities").Strings()
@@ -131,7 +132,10 @@
+ cleanupList := cleanup.NewList() + st.CleanupList = cleanupList st.ForceSequential = *forceSequential
st.EnableSSHAgent = enableSSHAgent
--- a/plans/plans.go Wed Oct 11 20:57:52 2017 -0500
+++ b/plans/plans.go Wed Oct 11 22:00:46 2017 -0500
@@ -94,10 +94,11 @@
func (p *Plan) Execute(path string, tasks map[string]tasks.Task, env []string, st *state.State) error {
logger := logging.NewAdapter(path)
planEnv := environment.Merge(env, p.Environment)
- defer p.teardown(logger, st)
+ // Teardown plan on defer OR on signal exit (first one to happen) + defer st.CleanupList.Add(func() { p.teardown(logger, st) })() if err := p.setup(logger, st); err != nil {
--- a/state/state.go Wed Oct 11 20:57:52 2017 -0500
+++ b/state/state.go Wed Oct 11 22:00:46 2017 -0500
@@ -24,6 +24,7 @@
+ "bitbucket.org/rw_grim/convey/cleanup" "bitbucket.org/rw_grim/convey/environment"
"bitbucket.org/rw_grim/convey/network"
"bitbucket.org/rw_grim/convey/workspace"
@@ -32,6 +33,7 @@
const ExpansionLimit = 100
+ CleanupList *cleanup.List Workspace workspace.Workspace
@@ -56,16 +58,18 @@
expandableDelimiter string
- // This list is a stash of container names which are run
- // in detached mode. Appending to this may happen from
- // multiple goroutines, so this needs to be guarded via
+ // This list is a stash of container names which are either + // currently running or running in detached mode. Appending + // to this may happen from multiple goroutines, so this needs + // to be guarded via mutex. + runningContainers map[string]struct{} detachedContainers map[string]struct{}
+ runningContainers: map[string]struct{}{}, detachedContainers: map[string]struct{}{},
@@ -204,6 +208,7 @@
env = environment.Merge(env, st.Environment)
+ CleanupList: st.CleanupList, KeepWorkspace: st.KeepWorkspace,
@@ -220,6 +225,44 @@
+func (st *State) MarkRunning(name string) { + st.parent.MarkRunning(name) + defer st.mutex.Unlock() + st.runningContainers[name] = struct{}{} +func (st *State) UnmarkRunning(name string) { + st.parent.UnmarkRunning(name) + defer st.mutex.Unlock() + delete(st.runningContainers, name) +func (st *State) GetRunning() []string { + return st.parent.GetDetached() + defer st.mutex.RUnlock() + for key := range st.runningContainers { + names = append(names, key) // MarkDetached will add the given container name into the list
// of containers running in detached mode which must be shut down
// at the end of the plan. This falls through directly to the root