From d18b7839b99c767f6e411fe3c488952267cc2b3e Mon Sep 17 00:00:00 2001 From: Dmitry Borodaenko Date: Thu, 1 Oct 2020 11:18:17 -0700 Subject: [PATCH] no panicwrap in plugins As of mitchellh/panicwrap#25, a call to panicwrap.Wrapped() unsets the cookie in the env, that makes packer plugin child process inherit an env without the panicwrap cookie and panicwrap itself. This trips up CleanupClients() in Packer's plugin client: instead of the real plugin server it now kills its panicwrap parent -- which doesn't forward SIGKILL to its child because it's not a signal that can be caught -- and ends up indefinitely waiting in client.Kill() for an EOF that will never come. This workaround is to not even try to panicwrap in a plugin server. --- main.go | 146 +++++++++++++++++++++++++++++--------------------------- 1 file changed, 76 insertions(+), 70 deletions(-) diff --git a/main.go b/main.go index 47f69d364..5079d9a61 100644 --- a/main.go +++ b/main.go @@ -43,85 +43,87 @@ func realMain() int { wrapConfig.CookieKey = "PACKER_WRAP_COOKIE" wrapConfig.CookieValue = "49C22B1A-3A93-4C98-97FA-E07D18C787B5" - if !panicwrap.Wrapped(&wrapConfig) { - // Generate a UUID for this packer run and pass it to the environment. - // GenerateUUID always returns a nil error (based on rand.Read) so we'll - // just ignore it. - UUID, _ := uuid.GenerateUUID() - os.Setenv("PACKER_RUN_UUID", UUID) - - // Determine where logs should go in general (requested by the user) - logWriter, err := logOutput() - if err != nil { - fmt.Fprintf(os.Stderr, "Couldn't setup log output: %s", err) - return 1 - } - if logWriter == nil { - logWriter = ioutil.Discard - } + if inPlugin() || panicwrap.Wrapped(&wrapConfig) { + // Call the real main + return wrappedMain() + } - packer.LogSecretFilter.SetOutput(logWriter) + // Generate a UUID for this packer run and pass it to the environment. + // GenerateUUID always returns a nil error (based on rand.Read) so we'll + // just ignore it. + UUID, _ := uuid.GenerateUUID() + os.Setenv("PACKER_RUN_UUID", UUID) - // Disable logging here - log.SetOutput(ioutil.Discard) + // Determine where logs should go in general (requested by the user) + logWriter, err := logOutput() + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't setup log output: %s", err) + return 1 + } + if logWriter == nil { + logWriter = ioutil.Discard + } - // We always send logs to a temporary file that we use in case - // there is a panic. Otherwise, we delete it. - logTempFile, err := tmp.File("packer-log") - if err != nil { - fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err) - return 1 - } - defer os.Remove(logTempFile.Name()) - defer logTempFile.Close() - - // Tell the logger to log to this file - os.Setenv(EnvLog, "") - os.Setenv(EnvLogFile, "") - - // Setup the prefixed readers that send data properly to - // stdout/stderr. - doneCh := make(chan struct{}) - outR, outW := io.Pipe() - go copyOutput(outR, doneCh) - - // Enable checkpoint for panic reporting - if config, _ := loadConfig(); config != nil && !config.DisableCheckpoint { - packer.CheckpointReporter = packer.NewCheckpointReporter( - config.DisableCheckpointSignature, - ) - } + packer.LogSecretFilter.SetOutput(logWriter) - // Create the configuration for panicwrap and wrap our executable - wrapConfig.Handler = panicHandler(logTempFile) - wrapConfig.Writer = io.MultiWriter(logTempFile, &packer.LogSecretFilter) - wrapConfig.Stdout = outW - wrapConfig.DetectDuration = 500 * time.Millisecond - wrapConfig.ForwardSignals = []os.Signal{syscall.SIGTERM} - exitStatus, err := panicwrap.Wrap(&wrapConfig) - if err != nil { - fmt.Fprintf(os.Stderr, "Couldn't start Packer: %s", err) - return 1 - } + // Disable logging here + log.SetOutput(ioutil.Discard) + + // We always send logs to a temporary file that we use in case + // there is a panic. Otherwise, we delete it. + logTempFile, err := tmp.File("packer-log") + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err) + return 1 + } + defer os.Remove(logTempFile.Name()) + defer logTempFile.Close() - // If >= 0, we're the parent, so just exit - if exitStatus >= 0 { - // Close the stdout writer so that our copy process can finish - outW.Close() + // Tell the logger to log to this file + os.Setenv(EnvLog, "") + os.Setenv(EnvLogFile, "") - // Wait for the output copying to finish - <-doneCh + // Setup the prefixed readers that send data properly to + // stdout/stderr. + doneCh := make(chan struct{}) + outR, outW := io.Pipe() + go copyOutput(outR, doneCh) - return exitStatus - } + // Enable checkpoint for panic reporting + if config, _ := loadConfig(); config != nil && !config.DisableCheckpoint { + packer.CheckpointReporter = packer.NewCheckpointReporter( + config.DisableCheckpointSignature, + ) + } - // We're the child, so just close the tempfile we made in order to - // save file handles since the tempfile is only used by the parent. - logTempFile.Close() + // Create the configuration for panicwrap and wrap our executable + wrapConfig.Handler = panicHandler(logTempFile) + wrapConfig.Writer = io.MultiWriter(logTempFile, &packer.LogSecretFilter) + wrapConfig.Stdout = outW + wrapConfig.DetectDuration = 500 * time.Millisecond + wrapConfig.ForwardSignals = []os.Signal{syscall.SIGTERM} + exitStatus, err := panicwrap.Wrap(&wrapConfig) + if err != nil { + fmt.Fprintf(os.Stderr, "Couldn't start Packer: %s", err) + return 1 + } + + // If >= 0, we're the parent, so just exit + if exitStatus >= 0 { + // Close the stdout writer so that our copy process can finish + outW.Close() + + // Wait for the output copying to finish + <-doneCh + + return exitStatus } - // Call the real main - return wrappedMain() + // We're the child, so just close the tempfile we made in order to + // save file handles since the tempfile is only used by the parent. + logTempFile.Close() + + return 0 } // wrappedMain is called only when we're wrapped by panicwrap and @@ -135,7 +137,7 @@ func wrappedMain() int { packer.LogSecretFilter.SetOutput(os.Stderr) log.SetOutput(&packer.LogSecretFilter) - inPlugin := os.Getenv(plugin.MagicCookieKey) == plugin.MagicCookieValue + inPlugin := inPlugin() if inPlugin { // This prevents double-logging timestamps log.SetFlags(0) @@ -387,6 +389,10 @@ func copyOutput(r io.Reader, doneCh chan<- struct{}) { wg.Wait() } +func inPlugin() bool { + return os.Getenv(plugin.MagicCookieKey) == plugin.MagicCookieValue +} + func init() { // Seed the random number generator rand.Seed(time.Now().UTC().UnixNano())