Skip to content

Commit 2bb0880

Browse files
authored
Revert use of PTY for docker compose invocation in Windows (#1885)
In #1801 we introduced a virtual tty to capture docker compose output as it is generated in normal consoles. The library we use for that doesn't work on Windows, breaking stack management and tests depending on it. While we find an alternative, remove the use of PTY in Windows, effectively reverting #1801 on this platform. Add also a test to check that elastic-package stack keeps working on Windows.
1 parent 3f99503 commit 2bb0880

File tree

6 files changed

+188
-57
lines changed

6 files changed

+188
-57
lines changed

.buildkite/pipeline.yml

+14-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ env:
66
KIND_VERSION: 'v0.20.0'
77
K8S_VERSION: 'v1.29.0'
88
LINUX_AGENT_IMAGE: "golang:${GO_VERSION}"
9-
WINDOWS_AGENT_IMAGE: "family/ci-windows-2022"
9+
# Using this one instead of family/core-windows-2022 because of conflicts with the pre-installed docker.
10+
WINDOWS_AGENT_IMAGE: "family/platform-ingest-beats-windows-2022"
1011
IMAGE_UBUNTU_X86_64: "family/core-ubuntu-2204"
1112

1213
steps:
@@ -48,8 +49,20 @@ steps:
4849
allow_failure: false
4950
- step: unit-tests-linux
5051
allow_failure: false
52+
53+
- label: ":windows: Run stack tests"
54+
key: stack-tests-windows
55+
command: ".buildkite/scripts/stack_tests_windows.ps1"
56+
agents:
57+
provider: "gcp"
58+
image: "${WINDOWS_AGENT_IMAGE}"
59+
depends_on:
60+
- step: check-static
61+
allow_failure: false
5162
- step: unit-tests-windows
5263
allow_failure: false
64+
artifact_paths:
65+
- 'C:\ProgramData\chocolatey\logs\chocolatey.log'
5366

5467
- wait: ~
5568
continue_on_failure: true
+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
$ErrorActionPreference = "Stop" # set -e
2+
3+
# Forcing to checkout again all the files with a correct autocrlf.
4+
# Doing this here because we cannot set git clone options before.
5+
function fixCRLF {
6+
Write-Host "-- Fixing CRLF in git checkout --"
7+
git config core.autocrlf input
8+
git rm --quiet --cached -r .
9+
git reset --quiet --hard
10+
}
11+
12+
function withGolang($version) {
13+
# Avoid conflicts with previous installations.
14+
Remove-Item env:GOROOT
15+
16+
Write-Host "-- Install golang $version --"
17+
choco install -y golang --version $version
18+
setupChocolateyPath
19+
go version
20+
go env
21+
}
22+
23+
function withDocker($version) {
24+
Write-Host "-- Install Docker $version --"
25+
choco install -y Containers Microsoft-Hyper-V --source windowsfeatures
26+
choco install -y docker-engine --version $version
27+
choco install -y docker-cli --version $version
28+
setupChocolateyPath
29+
}
30+
31+
function withDockerCompose($version) {
32+
Write-Host "-- Install Docker Compose $version --"
33+
choco install -y docker-compose --version $version
34+
setupChocolateyPath
35+
}
36+
37+
function setupChocolateyPath() {
38+
$env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.."
39+
Import-Module "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
40+
refreshenv
41+
}
42+
43+
44+
fixCRLF
45+
46+
withGolang $env:GO_VERSION
47+
withDocker $env:DOCKER_VERSION
48+
withDockerCompose $env:DOCKER_COMPOSE_VERSION
49+
50+
Write-Host "--- Docker Info"
51+
docker info
52+
53+
echo "--- Downloading Go modules"
54+
go mod download -x
55+
56+
echo "--- Running stack tests"
57+
$ErrorActionPreference = "Continue" # set +e
58+
59+
# TODO: stack status checks that we can call docker-compose, but we should try a stack up.
60+
# stack up doesn't work because we didn't manage to enable the linux engine, and we don't have Windows native images.
61+
go run . stack status
62+
63+
$EXITCODE=$LASTEXITCODE
64+
$ErrorActionPreference = "Stop"
65+
66+
Exit $EXITCODE

.buildkite/scripts/unit_tests_windows.ps1

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ function fixCRLF {
1010
}
1111

1212
function withGolang($version) {
13+
# Avoid conflicts with previous installations.
14+
Remove-Item env:GOROOT
15+
1316
Write-Host "-- Install golang $version --"
1417
choco install -y golang --version $version
1518
$env:ChocolateyInstall = Convert-Path "$((Get-Command choco).Path)\..\.."
@@ -36,11 +39,9 @@ withGolang $env:GO_VERSION
3639

3740

3841
echo "--- Downloading Go modules"
39-
go version
4042
go mod download -x
4143

4244
echo "--- Running unit tests"
43-
go version
4445
$ErrorActionPreference = "Continue" # set +e
4546
go run gotest.tools/gotestsum --junitfile "$(PWD)/TEST-unit-windows.xml" -- -count=1 ./...
4647
$EXITCODE=$LASTEXITCODE

internal/compose/compose.go

+1-54
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,11 @@ import (
1414
"os"
1515
"os/exec"
1616
"regexp"
17-
"runtime"
1817
"strconv"
1918
"strings"
20-
"sync"
2119
"time"
2220

2321
"github.com/Masterminds/semver/v3"
24-
"github.com/creack/pty"
2522

2623
"gopkg.in/yaml.v3"
2724

@@ -476,56 +473,6 @@ type dockerComposeOptions struct {
476473
stdout io.Writer
477474
}
478475

479-
func (p *Project) runDockerComposeCmd(ctx context.Context, opts dockerComposeOptions) error {
480-
name, args := p.dockerComposeBaseCommand()
481-
args = append(args, opts.args...)
482-
483-
cmd := exec.CommandContext(ctx, name, args...)
484-
cmd.Cancel = func() error {
485-
if runtime.GOOS == "windows" {
486-
// Interrupt is not implemented in Windows.
487-
return cmd.Process.Kill()
488-
}
489-
return cmd.Process.Signal(os.Interrupt)
490-
}
491-
cmd.Env = append(os.Environ(), opts.env...)
492-
493-
ptty, tty, err := pty.Open()
494-
if err != nil {
495-
return fmt.Errorf("failed to open pseudo-tty to capture stderr: %w", err)
496-
}
497-
498-
var errBuffer bytes.Buffer
499-
cmd.Stderr = tty
500-
var stderr io.Writer = &errBuffer
501-
if logger.IsDebugMode() {
502-
cmd.Stdout = os.Stdout
503-
stderr = io.MultiWriter(&errBuffer, os.Stderr)
504-
}
505-
if opts.stdout != nil {
506-
cmd.Stdout = opts.stdout
507-
}
508-
509-
var wg sync.WaitGroup
510-
wg.Add(1)
511-
go func() {
512-
defer wg.Done()
513-
io.Copy(stderr, ptty)
514-
}()
515-
516-
logger.Debugf("running command: %s", cmd)
517-
err = cmd.Run()
518-
ptty.Close()
519-
tty.Close()
520-
wg.Wait()
521-
if err != nil {
522-
if msg := cleanComposeError(errBuffer.String()); len(msg) > 0 {
523-
return fmt.Errorf("%w: %s", err, msg)
524-
}
525-
}
526-
return err
527-
}
528-
529476
const daemonResponse = `Error response from daemon:`
530477

531478
// This regexp must match prefixes like WARN[0000], which may include escape sequences for colored letters
@@ -564,7 +511,7 @@ func (p *Project) dockerComposeStandaloneRequired() bool {
564511
if err == nil {
565512
return false
566513
} else {
567-
logger.Debugf("docker compose subcommand failed: %w: %s", err, output)
514+
logger.Debugf("docker compose subcommand failed: %v: %s", err, output)
568515
}
569516

570517
return true

internal/compose/compose_other.go

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
//go:build !windows
6+
7+
package compose
8+
9+
import (
10+
"bytes"
11+
"context"
12+
"fmt"
13+
"io"
14+
"os"
15+
"os/exec"
16+
"sync"
17+
18+
"github.com/creack/pty"
19+
20+
"github.com/elastic/elastic-package/internal/logger"
21+
)
22+
23+
func (p *Project) runDockerComposeCmd(ctx context.Context, opts dockerComposeOptions) error {
24+
name, args := p.dockerComposeBaseCommand()
25+
args = append(args, opts.args...)
26+
27+
cmd := exec.CommandContext(ctx, name, args...)
28+
cmd.Cancel = func() error {
29+
return cmd.Process.Signal(os.Interrupt)
30+
}
31+
cmd.Env = append(os.Environ(), opts.env...)
32+
33+
ptty, tty, err := pty.Open()
34+
if err != nil {
35+
return fmt.Errorf("failed to open pseudo-tty to capture stderr: %w", err)
36+
}
37+
38+
var errBuffer bytes.Buffer
39+
cmd.Stderr = tty
40+
var stderr io.Writer = &errBuffer
41+
if logger.IsDebugMode() {
42+
cmd.Stdout = os.Stdout
43+
stderr = io.MultiWriter(&errBuffer, os.Stderr)
44+
}
45+
if opts.stdout != nil {
46+
cmd.Stdout = opts.stdout
47+
}
48+
49+
var wg sync.WaitGroup
50+
wg.Add(1)
51+
go func() {
52+
defer wg.Done()
53+
io.Copy(stderr, ptty)
54+
}()
55+
56+
logger.Debugf("running command: %s", cmd)
57+
err = cmd.Run()
58+
ptty.Close()
59+
tty.Close()
60+
wg.Wait()
61+
if err != nil {
62+
if msg := cleanComposeError(errBuffer.String()); len(msg) > 0 {
63+
return fmt.Errorf("%w: %s", err, msg)
64+
}
65+
}
66+
return err
67+
}

internal/compose/compose_windows.go

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
2+
// or more contributor license agreements. Licensed under the Elastic License;
3+
// you may not use this file except in compliance with the Elastic License.
4+
5+
//go:build windows
6+
7+
package compose
8+
9+
import (
10+
"context"
11+
"os"
12+
"os/exec"
13+
14+
"github.com/elastic/elastic-package/internal/logger"
15+
)
16+
17+
func (p *Project) runDockerComposeCmd(ctx context.Context, opts dockerComposeOptions) error {
18+
name, args := p.dockerComposeBaseCommand()
19+
args = append(args, opts.args...)
20+
21+
cmd := exec.CommandContext(ctx, name, args...)
22+
cmd.Cancel = func() error {
23+
// Interrupt is not implemented in Windows.
24+
return cmd.Process.Kill()
25+
}
26+
cmd.Env = append(os.Environ(), opts.env...)
27+
28+
// TODO: Use a Windows Pseudo-Console (ConPTY) to capture stderr without losing the default output.
29+
if logger.IsDebugMode() {
30+
cmd.Stdout = os.Stdout
31+
cmd.Stderr = os.Stderr
32+
}
33+
if opts.stdout != nil {
34+
cmd.Stdout = opts.stdout
35+
}
36+
return cmd.Run()
37+
}

0 commit comments

Comments
 (0)