-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Paul Glass
committed
Nov 23, 2021
1 parent
d1a37d7
commit be6d810
Showing
10 changed files
with
423 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package appentrypoint | ||
|
||
func (c *Command) Help() string { | ||
return "" | ||
} | ||
|
||
func (c *Command) Synopsis() string { | ||
return "Entrypoint for running Envoy in ECS" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,152 @@ | ||
//go:build !windows | ||
// +build !windows | ||
|
||
package appentrypoint | ||
|
||
import ( | ||
"flag" | ||
"fmt" | ||
"os" | ||
"os/signal" | ||
"sync" | ||
"syscall" | ||
"time" | ||
|
||
"github.com/hashicorp/consul-ecs/entrypoint" | ||
"github.com/hashicorp/go-hclog" | ||
"github.com/mitchellh/cli" | ||
) | ||
|
||
const ( | ||
flagShutdownDelay = "shutdown-delay" | ||
) | ||
|
||
type Command struct { | ||
UI cli.Ui | ||
log hclog.Logger | ||
once sync.Once | ||
flagSet *flag.FlagSet | ||
|
||
sigs chan os.Signal | ||
appCmd *entrypoint.Cmd | ||
shutdownDelay time.Duration | ||
} | ||
|
||
func (c *Command) init() { | ||
c.log = hclog.New(&hclog.LoggerOptions{Name: "consul-ecs"}) | ||
c.flagSet = flag.NewFlagSet("", flag.ContinueOnError) | ||
c.flagSet.DurationVar(&c.shutdownDelay, flagShutdownDelay, 0, | ||
`Continue running for this long after receiving SIGTERM. Must be a duration (e.g. "10s").`) | ||
c.log = hclog.New(nil) | ||
|
||
} | ||
|
||
func (c *Command) Run(args []string) int { | ||
c.once.Do(c.init) | ||
|
||
// Flag parsing stops just before the first non-flag argument ("-" is a non-flag argument) | ||
// or after the terminator "--" | ||
err := c.flagSet.Parse(args) | ||
if err != nil { | ||
c.UI.Error(fmt.Sprint(err)) | ||
return 1 | ||
} | ||
|
||
// Remaining args for the application command, after parsing our flags | ||
args = c.flagSet.Args() | ||
|
||
if len(args) == 0 { | ||
c.UI.Error("command is required") | ||
return 1 | ||
} | ||
|
||
c.sigs = make(chan os.Signal, 1) | ||
c.appCmd = entrypoint.NewCmd(c.log, args) | ||
|
||
return c.realRun() | ||
} | ||
|
||
func (c *Command) realRun() int { | ||
signal.Notify(c.sigs) | ||
defer c.cleanup() | ||
|
||
go c.appCmd.Run() | ||
if _, ok := <-c.appCmd.Started(); !ok { | ||
return 1 | ||
} | ||
|
||
if exitCode, exited := c.waitForSigterm(); exited { | ||
return exitCode | ||
} | ||
if c.shutdownDelay > 0 { | ||
if exitCode, exited := c.waitForShutdownDelay(); exited { | ||
return exitCode | ||
} | ||
} | ||
// We've signaled the process to exit, so wait until it does. | ||
c.waitForAppExit() | ||
return c.appCmd.ProcessState.ExitCode() | ||
} | ||
|
||
// waitForSigterm waits until c.appCmd has exited, or until a sigterm is received. | ||
// It returns (exitCode, exited), where if exited=true, then c.appCmd has exited. | ||
func (c *Command) waitForSigterm() (int, bool) { | ||
for { | ||
select { | ||
case <-c.appCmd.Done(): | ||
return c.appCmd.ProcessState.ExitCode(), true | ||
case sig := <-c.sigs: | ||
if sig == syscall.SIGTERM { | ||
return -1, false | ||
} | ||
c.forwardSignal(sig) | ||
} | ||
} | ||
} | ||
|
||
// waitForShutdownDelay waits for c.appCmd to exit for `delay` seconds. | ||
// After the delay has passed, it sends a sigterm to c.appCmd. | ||
// It returns (exitCode, exited), where if exited=true, then c.appCmd has exited. | ||
func (c *Command) waitForShutdownDelay() (int, bool) { | ||
timer := time.After(c.shutdownDelay) | ||
for { | ||
select { | ||
case <-c.appCmd.Done(): | ||
return c.appCmd.ProcessState.ExitCode(), true | ||
case sig := <-c.sigs: | ||
c.forwardSignal(sig) | ||
case <-timer: | ||
if err := syscall.Kill(-c.appCmd.Process.Pid, syscall.SIGTERM); err != nil { | ||
c.log.Warn("sending sigterm to application", "error", err.Error()) | ||
} | ||
} | ||
} | ||
|
||
} | ||
|
||
func (c *Command) waitForAppExit() { | ||
for { | ||
select { | ||
case <-c.appCmd.Done(): | ||
return | ||
case sig := <-c.sigs: | ||
c.forwardSignal(sig) | ||
} | ||
} | ||
} | ||
|
||
func (c *Command) forwardSignal(sig os.Signal) { | ||
switch sig { | ||
case syscall.SIGCHLD, syscall.SIGURG: | ||
return | ||
default: | ||
if err := c.appCmd.Process.Signal(sig); err != nil { | ||
c.log.Warn("forwarding signal", "err", err.Error()) | ||
} | ||
} | ||
} | ||
|
||
func (c *Command) cleanup() { | ||
signal.Stop(c.sigs) | ||
<-c.appCmd.Done() | ||
} |
Oops, something went wrong.