-
Notifications
You must be signed in to change notification settings - Fork 4.4k
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
Clean up subprocess handling and make shell use optional #3509
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Made a quick pass through with some initial thoughts.
agent/util_other.go
Outdated
"os" | ||
"os/exec" | ||
|
||
"os/signal" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sort up.
agent/util_other.go
Outdated
select { | ||
case sig := <-signalCh: | ||
if err := cmd.Process.Signal(sig); err != nil { | ||
fmt.Println("Error relaying signal to subprocess: ", err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be fed a logger?
agent/watch_handler.go
Outdated
@@ -9,6 +9,8 @@ import ( | |||
"os" | |||
"strconv" | |||
|
|||
"os/exec" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sort up.
agent/watch_handler.go
Outdated
script, isScript := handler.(string) | ||
args, isArgs := handler.([]string) | ||
if isArgs { | ||
script = fmt.Sprintf("%v", args) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is nasty since we don't use this (and we super don't want to treat the args as a big string. Can you maybe always run the below code based on an args array, so there's less switching around, like this:
var args []string
switch h := hander.(type) {
case string:
// figure out the shell and stuff from the environment
args = []string{theShell, "-c", h...}
case []string:
args = h
}
// then down below you just always use ExecSubprocess with the args local
command/lock.go
Outdated
@@ -101,6 +104,9 @@ func (c *LockCommand) run(args []string, lu **LockUnlock) int { | |||
"is generated based on the provided child command.") | |||
f.BoolVar(&passStdin, "pass-stdin", false, | |||
"Pass stdin to the child process.") | |||
f.BoolVar(&shell, "shell", false, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we are going to move health checks away from using the shell (via having the user put the shell in themselves via args
then we could omit this part of the change.
h := wp.Exempt["handler"] | ||
if _, ok := h.(string); h == nil || !ok { | ||
// Get the handler and subprocess arguments | ||
handler, hasHandler := wp.Exempt["handler"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about issuing a deprecation warning for handler
here and in health checks?
b02415a
to
7489a7e
Compare
agent/util_other.go
Outdated
// If enabled, start a goroutine to relay signals to the subprocess and | ||
// another to watch for its shutdown. | ||
if echoSignals { | ||
signalCh := make(chan os.Signal, 4) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did you come up with 4 as the buffer size here?
Asking because the documentation on signal.Notify states that Package signal will not block sending to c: the caller must ensure that c has sufficient buffer space to keep up with the expected signal rate
. 4 seemed like an oddly specific choice, e.g what If I sent the subprocess a bunch of SIGHUPs in quick succession?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I took the same value we use in listening for signals on the agent: https://github.com/hashicorp/consul/blob/master/command/agent.go#L366
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That begs the question of why its set to 4 there as well.
I would set this buffer size to something like 10. Not that we can hypothesize anything about the incoming signal rate in how people would use this, but atleast 10 is conservatively large enough. If you are sending more than 10 signals at a time to the subprocess, you are probably doing something wrong.
With leaving it as 4, there is the possibility of missing a signal because Signal.Notify won't block when the buffer is full, and thus your go routine below will never see it.
agent/config/runtime_test.go
Outdated
@@ -3344,6 +3387,7 @@ func TestFullConfig(t *testing.T) { | |||
} | |||
|
|||
// check the warnings | |||
t.Log(b.Warnings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like some debug code got left around.
agent/structs/check_type.go
Outdated
@@ -59,7 +60,7 @@ func (c *CheckType) IsTTL() bool { | |||
|
|||
// IsMonitor checks if this is a Monitor type | |||
func (c *CheckType) IsMonitor() bool { | |||
return c.Script != "" && c.DockerContainerID == "" && c.Interval != 0 | |||
return (c.Script != "" || len(c.ScriptArgs) > 0) && c.DockerContainerID == "" && c.Interval != 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should call IsScript()
to be more clear / dedup.
agent/util_other.go
Outdated
// another to watch for its shutdown. | ||
if echoSignals { | ||
signalCh := make(chan os.Signal, 4) | ||
shutdownCh := make(chan struct{}, 0) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you need the , 0
argument here.
agent/watch_handler.go
Outdated
func makeWatchHandler(logOutput io.Writer, handler interface{}) watch.HandlerFunc { | ||
var args []string | ||
|
||
// Figure out whether to run in shell or raw subprocess mode |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh I'm sorry I just realized that ExecScript
has special stuff on Windows (there's util_windows.go). I know there are some open issues for Windows that will likely be cleaned up by just using args, so we should probably keep using ExecScript
in this path as well until we just deprecate it, that way we don't break more than we have to. Sorry to flip back on this.
command/lock.go
Outdated
@@ -9,6 +9,8 @@ import ( | |||
"syscall" | |||
"time" | |||
|
|||
"os/exec" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should sort up into the previous list.
command/watch.go
Outdated
@@ -8,6 +8,8 @@ import ( | |||
"strconv" | |||
"strings" | |||
|
|||
"os/exec" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should sort up into the previous list.
This avoids us trying to send SIGCHILD to the dead process.
command/exec.go
Outdated
@@ -161,10 +168,15 @@ func (c *ExecCommand) Run(args []string) int { | |||
return 1 | |||
} | |||
c.conf.script = buf.Bytes() | |||
} else { | |||
if !c.conf.shell { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be else if !c.conf.shell {
on line 171, I think
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
This aims at fixing the issues in #2999 around Consul's handling of child processes spawned from the exec/lock/watch commands and checks/watches run by the consul agent.