Skip to content

Commit

Permalink
fix(executor): windows output artifacts. Fixes #4082 (#4083)
Browse files Browse the repository at this point in the history
  • Loading branch information
mweibel committed Sep 23, 2020
1 parent 7c92b3a commit 764b56b
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 30 deletions.
3 changes: 2 additions & 1 deletion Dockerfile.windows
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ RUN iex ((new-object net.webclient).DownloadString('https://chocolatey.org/insta

# install golang, dep and other tools
RUN choco install golang --version=$env:GOLANG_VERSION ; \
choco install make dep docker-cli git.portable
choco install make dep docker-cli git.portable 7zip.portable

####################################################################################################
# argoexec-base
Expand All @@ -41,6 +41,7 @@ RUN mkdir C:\app && \

COPY --from=builder C:/ProgramData/chocolatey/lib/docker-cli/tools/docker.exe C:/app/docker.exe
COPY --from=builder C:/tools/git C:/app/git
COPY --from=builder C:/ProgramData/chocolatey/lib/7zip.portable/tools/7z-extra/x64/7za.exe C:/app/7za.exe

# add binaries to path
USER Administrator
Expand Down
1 change: 1 addition & 0 deletions USERS.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,4 @@ Currently, the following organizations are **officially** using Argo Workflows:
1. [Tulip](https://tulip.com/)
1. [Wavefront](https://www.wavefront.com/)
1. [Wellcome Trust](https://wellcome.ac.uk/)
1. [Helio](https://helio.exchange)
24 changes: 19 additions & 5 deletions workflow/common/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"net/http"
"os/exec"
"regexp"
"runtime"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -404,20 +405,33 @@ func Replace(fstTmpl *fasttemplate.Template, replaceMap map[string]string, allow
}

// RunCommand is a convenience function to run/log a command and log the stderr upon failure
func RunCommand(name string, arg ...string) error {
func RunCommand(name string, arg ...string) ([]byte, error) {
cmd := exec.Command(name, arg...)
cmdStr := strings.Join(cmd.Args, " ")
log.Info(cmdStr)
_, err := cmd.Output()
out, err := cmd.Output()
if err != nil {
if exErr, ok := err.(*exec.ExitError); ok {
errOutput := string(exErr.Stderr)
log.Errorf("`%s` failed: %s", cmdStr, errOutput)
return errors.InternalError(strings.TrimSpace(errOutput))
return nil, errors.InternalError(strings.TrimSpace(errOutput))
}
return errors.InternalWrapError(err)
return nil, errors.InternalWrapError(err)
}
return nil
return out, nil
}

// RunShellCommand is a convenience function to use RunCommand for shell executions. It's os-specific
// and runs `cmd` in windows.
func RunShellCommand(arg ...string) ([]byte, error) {
name := "sh"
shellFlag := "-c"
if runtime.GOOS == "windows" {
name = "cmd"
shellFlag = "/c"
}
arg = append([]string{shellFlag}, arg...)
return RunCommand(name, arg...)
}

const patchRetries = 5
Expand Down
57 changes: 34 additions & 23 deletions workflow/executor/docker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"io/ioutil"
"os"
"os/exec"
"runtime"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -36,34 +37,18 @@ func (d *DockerExecutor) GetFileContents(containerID string, sourcePath string)
// content from the tar archive and output into stdout. In this way, we do not need to
// create and copy the content into a file from the wait container.
dockerCpCmd := fmt.Sprintf("docker cp -a %s:%s - | tar -ax -O", containerID, sourcePath)
cmd := exec.Command("sh", "-c", dockerCpCmd)
log.Info(cmd.Args)
out, err := cmd.Output()
out, err := common.RunShellCommand(dockerCpCmd)
if err != nil {
if exErr, ok := err.(*exec.ExitError); ok {
log.Errorf("`%s` stderr:\n%s", cmd.Args, string(exErr.Stderr))
}
return "", errors.InternalWrapError(err)
return "", err
}
return string(out), nil
}

func (d *DockerExecutor) CopyFile(containerID string, sourcePath string, destPath string, compressionLevel int) error {
log.Infof("Archiving %s:%s to %s", containerID, sourcePath, destPath)
var levelFlag string
switch compressionLevel {
case gzip.NoCompression:
// best we can do - if we skip gzip it's a different file
levelFlag = "-1"
case gzip.DefaultCompression:
// use cmd default
levelFlag = ""
default:
// -1 through -9 (or error)
levelFlag = "-" + strconv.Itoa(compressionLevel)
}
dockerCpCmd := fmt.Sprintf("docker cp -a %s:%s - | gzip %s > %s", containerID, sourcePath, levelFlag, destPath)
err := common.RunCommand("sh", "-c", dockerCpCmd)

dockerCpCmd := getDockerCpCmd(containerID, sourcePath, compressionLevel, destPath)
_, err := common.RunShellCommand(dockerCpCmd)
if err != nil {
return err
}
Expand Down Expand Up @@ -182,15 +167,16 @@ func (d *DockerExecutor) WaitInit() error {

// Wait for the container to complete
func (d *DockerExecutor) Wait(containerID string) error {
return common.RunCommand("docker", "wait", containerID)
_, err := common.RunCommand("docker", "wait", containerID)
return err
}

// killContainers kills a list of containerIDs first with a SIGTERM then with a SIGKILL after a grace period
func (d *DockerExecutor) Kill(containerIDs []string) error {
killArgs := append([]string{"kill", "--signal", "TERM"}, containerIDs...)
// docker kill will return with an error if a container has terminated already, which is not an error in this case.
// We therefore ignore any error. docker wait that follows will re-raise any other error with the container.
err := common.RunCommand("docker", killArgs...)
_, err := common.RunCommand("docker", killArgs...)
if err != nil {
log.Warningf("Ignored error from 'docker kill --signal TERM': %s", err)
}
Expand Down Expand Up @@ -219,3 +205,28 @@ func (d *DockerExecutor) Kill(containerIDs []string) error {
log.Infof("Containers %s killed successfully", containerIDs)
return nil
}

// getDockerCpCmd uses os-specific code to run `docker cp` and gzip/7zip to copy gzipped data from another
// container.
func getDockerCpCmd(containerID, sourcePath string, compressionLevel int, destPath string) string {
gzipCmd := "gzip %s > %s"
levelFlagParam := "-"
if runtime.GOOS == "windows" {
gzipCmd = "7za.exe a -tgzip -si %s %s"
levelFlagParam = "-mx"
}

var levelFlag string
switch compressionLevel {
case gzip.NoCompression:
// best we can do - if we skip gzip it's a different file
levelFlag = levelFlagParam + "1"
case gzip.DefaultCompression:
// use cmd default
levelFlag = ""
default:
// -1 through -9 (or error)
levelFlag = levelFlagParam + strconv.Itoa(compressionLevel)
}
return fmt.Sprintf("docker cp -a %s:%s - | %s", containerID, sourcePath, fmt.Sprintf(gzipCmd, levelFlag, destPath))
}
2 changes: 1 addition & 1 deletion workflow/executor/executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -854,7 +854,7 @@ func untar(tarPath string, destPath string) error {
if err != nil {
return errors.InternalWrapError(err)
}
err = common.RunCommand("tar", "-xf", tarPath, "-C", tmpDir)
_, err = common.RunCommand("tar", "-xf", tarPath, "-C", tmpDir)
if err != nil {
return err
}
Expand Down

0 comments on commit 764b56b

Please sign in to comment.