Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Consul Connect Envoy Command on Windows #17694

Merged
merged 35 commits into from
Jul 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
0a4c136
[CONSUL-491] Support admin_access_log_path value for Windows (#71)
cocolavayen Sep 30, 2022
5e0bec2
[CONSUL-519] Implement mkfifo Alternative (#84)
joselo85 Oct 19, 2022
41c02a0
[CONSUL-542] Create OS Specific Files for Envoy Package (#88)
joselo85 Nov 9, 2022
c592e5d
[CONSUL-543] Create exec_supported.go (#89)
joselo85 Nov 9, 2022
ace9e37
[CONSUL-544] Test and Build Changes (#90)
joselo85 Nov 10, 2022
7fab79f
Implement os.DevNull
joselo85 Nov 16, 2022
6b1fea5
fix merge conflict main
absolutelightning Jun 13, 2023
4b2fc2d
using mmap instead of disk files
absolutelightning Jun 13, 2023
13e5433
Merge branch 'main' into southworks/qa-consul
absolutelightning Jun 13, 2023
906aaa2
fix import in exec-unix
absolutelightning Jun 13, 2023
2a70c2f
fix nmap open too many arguemtn
absolutelightning Jun 19, 2023
fb1fd8e
go fmt on file
absolutelightning Jun 19, 2023
45f6f2a
changelog file
absolutelightning Jun 19, 2023
aec2a20
Merge branch 'main' into southworks/qa-consul
absolutelightning Jun 19, 2023
4ab3bdb
fix go mod
absolutelightning Jun 19, 2023
aad0dab
Merge branch 'southworks/qa-consul' of ssh://github.com/hashicorp/con…
absolutelightning Jun 19, 2023
4d62ffc
Update .changelog/17694.txt
absolutelightning Jun 21, 2023
55dba95
different mmap library
absolutelightning Jun 21, 2023
6b0a813
using different library
absolutelightning Jun 28, 2023
6be9fcf
Merge branch 'southworks/qa-consul' of ssh://github.com/hashicorp/con…
absolutelightning Jun 28, 2023
27073a4
fix Map funciton call
absolutelightning Jun 28, 2023
b4cf245
fix mmap call
absolutelightning Jun 28, 2023
f6424ab
Merge branch 'main' into southworks/qa-consul
absolutelightning Jun 28, 2023
c6aca86
Merge branch 'main' into southworks/qa-consul
absolutelightning Jun 28, 2023
05b3fbd
fix go mod
absolutelightning Jun 28, 2023
1388115
Merge branch 'main' into southworks/qa-consul
absolutelightning Jun 29, 2023
7a0b163
made code similar to unix using npipe
absolutelightning Jul 3, 2023
cd3e7ae
Merge branch 'southworks/qa-consul' of ssh://github.com/hashicorp/con…
absolutelightning Jul 3, 2023
fd4ceed
fix go.mod
absolutelightning Jul 3, 2023
fd75fb7
fix dialing of npipe
absolutelightning Jul 3, 2023
f500139
dont wait
absolutelightning Jul 3, 2023
74de240
Merge branch 'main' into southworks/qa-consul
absolutelightning Jul 3, 2023
796a88a
check size of written json
absolutelightning Jul 4, 2023
a39760a
fix undefined n
absolutelightning Jul 4, 2023
e142630
Merge branch 'main' into southworks/qa-consul
absolutelightning Jul 4, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/17694.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:feature
Windows: support consul connect envoy command on Windows
```
2 changes: 1 addition & 1 deletion command/connect/envoy/envoy.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func New(ui cli.Ui) *cmd {
return c
}

const DefaultAdminAccessLogPath = "/dev/null"
const DefaultAdminAccessLogPath = os.DevNull

type cmd struct {
UI cli.Ui
Expand Down
55 changes: 55 additions & 0 deletions command/connect/envoy/exec_supported.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//go:build linux || darwin || windows
// +build linux darwin windows

package envoy

import (
"fmt"
"os"
"strings"
)

func isHotRestartOption(s string) bool {
restartOpts := []string{
"--restart-epoch",
"--hot-restart-version",
"--drain-time-s",
"--parent-shutdown-time-s",
}
for _, opt := range restartOpts {
if s == opt {
return true
}
if strings.HasPrefix(s, opt+"=") {
return true
}
}
return false
}

func hasHotRestartOption(argSets ...[]string) bool {
for _, args := range argSets {
for _, opt := range args {
if isHotRestartOption(opt) {
return true
}
}
}
return false
}

// execArgs returns the command and args used to execute a binary. By default it
// will return a command of os.Executable with the args unmodified. This is a shim
// for testing, and can be overridden to execute using 'go run' instead.
var execArgs = func(args ...string) (string, []string, error) {
execPath, err := os.Executable()
if err != nil {
return "", nil, err
}

if strings.HasSuffix(execPath, "/envoy.test") {
return "", nil, fmt.Errorf("set execArgs to use 'go run' instead of doing a self-exec")
}

return execPath, args, nil
}
51 changes: 0 additions & 51 deletions command/connect/envoy/exec_unix.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,63 +12,12 @@ import (
"os"
"os/exec"
"path/filepath"
"strings"
"syscall"
"time"

"golang.org/x/sys/unix"
)

// testSelfExecOverride is a way for the tests to no fork-bomb themselves by
// self-executing the whole test suite for each case recursively. It's gross but
// the least gross option I could think of.
var testSelfExecOverride string

func isHotRestartOption(s string) bool {
restartOpts := []string{
"--restart-epoch",
"--hot-restart-version",
"--drain-time-s",
"--parent-shutdown-time-s",
}
for _, opt := range restartOpts {
if s == opt {
return true
}
if strings.HasPrefix(s, opt+"=") {
return true
}
}
return false
}

func hasHotRestartOption(argSets ...[]string) bool {
for _, args := range argSets {
for _, opt := range args {
if isHotRestartOption(opt) {
return true
}
}
}
return false
}

// execArgs returns the command and args used to execute a binary. By default it
// will return a command of os.Executable with the args unmodified. This is a shim
// for testing, and can be overridden to execute using 'go run' instead.
var execArgs = func(args ...string) (string, []string, error) {
execPath, err := os.Executable()
if err != nil {
return "", nil, err
}

if strings.HasSuffix(execPath, "/envoy.test") {
return "", nil, fmt.Errorf("set execArgs to use 'go run' instead of doing a self-exec")
}

return execPath, args, nil
}

func makeBootstrapPipe(bootstrapJSON []byte) (string, error) {
pipeFile := filepath.Join(os.TempDir(),
fmt.Sprintf("envoy-%x-bootstrap.json", time.Now().UnixNano()+int64(os.Getpid())))
Expand Down
4 changes: 2 additions & 2 deletions command/connect/envoy/exec_unsupported.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

//go:build !linux && !darwin
// +build !linux,!darwin
//go:build !linux && !darwin && !windows
// +build !linux,!darwin,!windows

package envoy

Expand Down
110 changes: 110 additions & 0 deletions command/connect/envoy/exec_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//go:build windows
// +build windows

package envoy

import (
"errors"
"fmt"
"github.com/natefinch/npipe"
"os"
"os/exec"
"path/filepath"
"time"
)

func makeBootstrapPipe(bootstrapJSON []byte) (string, error) {
pipeFile := filepath.Join(os.TempDir(),
fmt.Sprintf("envoy-%x-bootstrap.json", time.Now().UnixNano()+int64(os.Getpid())))

binary, args, err := execArgs("connect", "envoy", "pipe-bootstrap", pipeFile)
if err != nil {
return pipeFile, err
}

// Dial the named pipe
pipeConn, err := npipe.Dial(pipeFile)
if err != nil {
return pipeFile, err
}
defer pipeConn.Close()

// Start the command to connect to the named pipe
cmd := exec.Command(binary, args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = pipeConn

// Start the command
err = cmd.Start()
if err != nil {
return pipeFile, err
}

// Write the config
n, err := pipeConn.Write(bootstrapJSON)
if err != nil {
return pipeFile, err
}

if n < len(bootstrapJSON) {
return pipeFile, fmt.Errorf("failed writing boostrap to child STDIN: %s", err)
}

// We can't wait for the process since we need to exec into Envoy before it
// will be able to complete so it will be remain as a zombie until Envoy is
// killed then will be reaped by the init process (pid 0). This is all a bit
// gross but the cleanest workaround I can think of for Envoy 1.10 not
// supporting /dev/fd/<fd> config paths any more. So we are done and leaving
// the child to run it's course without reaping it.
Comment on lines +54 to +59
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the reasoning behind that, why don't we wait for it to be done and is this specific to Windows?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return pipeFile, nil
}

func startProc(binary string, args []string) (p *os.Process, err error) {
if binary, err = exec.LookPath(binary); err == nil {
var procAttr os.ProcAttr
procAttr.Files = []*os.File{os.Stdin,
os.Stdout, os.Stderr}
p, err := os.StartProcess(binary, args, &procAttr)
if err == nil {
return p, nil
}
}
return nil, err
}

func execEnvoy(binary string, prefixArgs, suffixArgs []string, bootstrapJSON []byte) error {
tempFile, err := makeBootstrapPipe(bootstrapJSON)
if err != nil {
os.RemoveAll(tempFile)
return err
}
// We don't defer a cleanup since we are about to Exec into Envoy which means
// defer will never fire. The child process cleans up for us in the happy
// path.
Comment on lines +82 to +84
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the reasoning here, why don't we defer the clean up to after the envoy process finish?

Copy link
Contributor Author

@absolutelightning absolutelightning Jul 10, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


// We default to disabling hot restart because it makes it easier to run
// multiple envoys locally for testing without them trying to share memory and
// unix sockets and complain about being different IDs. But if user is
// actually configuring hot-restart explicitly with the --restart-epoch option
// then don't disable it!
disableHotRestart := !hasHotRestartOption(prefixArgs, suffixArgs)

// First argument needs to be the executable name.
envoyArgs := []string{}
envoyArgs = append(envoyArgs, prefixArgs...)
if disableHotRestart {
envoyArgs = append(envoyArgs, "--disable-hot-restart")
}
envoyArgs = append(envoyArgs, suffixArgs...)
envoyArgs = append(envoyArgs, "--config-path", tempFile)

// Exec
if proc, err := startProc(binary, envoyArgs); err == nil {
proc.Wait()
} else if err != nil {
return errors.New("Failed to exec envoy: " + err.Error())
}

return nil
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ require (
github.com/mitchellh/mapstructure v1.5.0
github.com/mitchellh/pointerstructure v1.2.1
github.com/mitchellh/reflectwalk v1.0.2
github.com/natefinch/npipe v0.0.0-20160621034901-c1b8fa8bdcce
github.com/oklog/ulid/v2 v2.1.0
github.com/olekukonko/tablewriter v0.0.4
github.com/patrickmn/go-cache v2.1.0+incompatible
Expand Down Expand Up @@ -250,6 +251,7 @@ require (
google.golang.org/appengine v1.6.7 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.66.2 // indirect
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
gopkg.in/resty.v1 v1.12.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/natefinch/npipe v0.0.0-20160621034901-c1b8fa8bdcce h1:TqjP/BTDrwN7zP9xyXVuLsMBXYMt6LLYi55PlrIcq8U=
github.com/natefinch/npipe v0.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:ifHPsLndGGzvgzcaXUvzmt6LxKT4pJ+uzEhtnMt+f7A=
github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2 h1:BQ1HW7hr4IVovMwWg0E0PYcyW8CzqDcVmaew9cujU4s=
github.com/nicolai86/scaleway-sdk v1.10.2-0.20180628010248-798f60e20bb2/go.mod h1:TLb2Sg7HQcgGdloNxkrmtgDNR9uVYF3lfdFIN4Ro6Sk=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
Expand Down Expand Up @@ -1458,6 +1460,8 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
gopkg.in/resty.v1 v1.9.1/go.mod h1:vo52Hzryw9PnPHcJfPsBiFW62XhNx5OczbV9y+IMpgc=
gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI=
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
Expand Down