remove the use of panicwrap
Stop using panicwrap, and execute terraform in the main process.
This commit is contained in:
parent
0fcb75020f
commit
622c4df14c
1
go.mod
1
go.mod
|
@ -63,7 +63,6 @@ require (
|
||||||
github.com/mitchellh/go-wordwrap v1.0.1
|
github.com/mitchellh/go-wordwrap v1.0.1
|
||||||
github.com/mitchellh/gox v1.0.1
|
github.com/mitchellh/gox v1.0.1
|
||||||
github.com/mitchellh/mapstructure v1.1.2
|
github.com/mitchellh/mapstructure v1.1.2
|
||||||
github.com/mitchellh/panicwrap v1.0.0
|
|
||||||
github.com/mitchellh/reflectwalk v1.0.2
|
github.com/mitchellh/reflectwalk v1.0.2
|
||||||
github.com/nishanths/exhaustive v0.2.3
|
github.com/nishanths/exhaustive v0.2.3
|
||||||
github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db
|
github.com/packer-community/winrmcp v0.0.0-20180921211025-c76d91c1e7db
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -515,8 +515,6 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/panicwrap v1.0.0 h1:67zIyVakCIvcs69A0FGfZjBdPleaonSgGlXRSRlb6fE=
|
|
||||||
github.com/mitchellh/panicwrap v1.0.0/go.mod h1:pKvZHwWrZowLUzftuFq7coarnxbBXU4aQh3N0BJOeeA=
|
|
||||||
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
|
|
|
@ -10,7 +10,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/internal/helper/wrappedreadline"
|
|
||||||
"github.com/hashicorp/terraform/internal/repl"
|
"github.com/hashicorp/terraform/internal/repl"
|
||||||
|
|
||||||
"github.com/chzyer/readline"
|
"github.com/chzyer/readline"
|
||||||
|
@ -19,12 +18,12 @@ import (
|
||||||
|
|
||||||
func (c *ConsoleCommand) modeInteractive(session *repl.Session, ui cli.Ui) int {
|
func (c *ConsoleCommand) modeInteractive(session *repl.Session, ui cli.Ui) int {
|
||||||
// Configure input
|
// Configure input
|
||||||
l, err := readline.NewEx(wrappedreadline.Override(&readline.Config{
|
l, err := readline.NewEx(&readline.Config{
|
||||||
Prompt: "> ",
|
Prompt: "> ",
|
||||||
InterruptPrompt: "^C",
|
InterruptPrompt: "^C",
|
||||||
EOFPrompt: "exit",
|
EOFPrompt: "exit",
|
||||||
HistorySearchFold: true,
|
HistorySearchFold: true,
|
||||||
}))
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(
|
c.Ui.Error(fmt.Sprintf(
|
||||||
"Error initializing console: %s",
|
"Error initializing console: %s",
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
package terminal
|
|
||||||
|
|
||||||
import "os"
|
|
||||||
|
|
||||||
// This file has some annoying nonsense to, yet again, work around the
|
|
||||||
// panicwrap hack.
|
|
||||||
//
|
|
||||||
// Specifically, typically when we're running Terraform the stderr handle is
|
|
||||||
// not directly connected to the terminal but is instead a pipe into a parent
|
|
||||||
// process gathering up the output just in case a panic message appears.
|
|
||||||
// However, this package needs to know whether the _real_ stderr is connected
|
|
||||||
// to a terminal and what its width is.
|
|
||||||
//
|
|
||||||
// To work around that, we'll first initialize the terminal in the parent
|
|
||||||
// process, and then capture information about stderr into an environment
|
|
||||||
// variable so we can pass it down to the child process. The child process
|
|
||||||
// will then use the environment variable to pretend that the panicwrap pipe
|
|
||||||
// has the same characteristics as the terminal that it's indirectly writing
|
|
||||||
// to.
|
|
||||||
//
|
|
||||||
// This file has some helpers for implementing that awkward handshake, but the
|
|
||||||
// handshake itself is in package main, interspersed with all of the other
|
|
||||||
// panicwrap machinery.
|
|
||||||
//
|
|
||||||
// You might think that the code in helper/wrappedstreams could avoid this
|
|
||||||
// problem, but that package is broken on Windows: it always fails to recover
|
|
||||||
// the real stderr, and it also gets an incorrect result if the user was
|
|
||||||
// redirecting or piping stdout/stdin. So... we have this hack instead, which
|
|
||||||
// gets a correct result even on Windows and even with I/O redirection.
|
|
||||||
|
|
||||||
// StateForAfterPanicWrap is part of the workaround for panicwrap that
|
|
||||||
// captures some characteristics of stderr that the caller can pass to the
|
|
||||||
// panicwrap child process somehow and then use ReinitInsidePanicWrap.
|
|
||||||
func (s *Streams) StateForAfterPanicWrap() *PrePanicwrapState {
|
|
||||||
return &PrePanicwrapState{
|
|
||||||
StderrIsTerminal: s.Stderr.IsTerminal(),
|
|
||||||
StderrWidth: s.Stderr.Columns(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReinitInsidePanicwrap is part of the workaround for panicwrap that
|
|
||||||
// produces a Streams containing a potentially-lying Stderr that might
|
|
||||||
// claim to be a terminal even if it's actually a pipe connected to the
|
|
||||||
// parent process.
|
|
||||||
//
|
|
||||||
// That's an okay lie in practice because the parent process will copy any
|
|
||||||
// data it recieves via that pipe verbatim to the real stderr anyway. (The
|
|
||||||
// original call to Init in the parent process should've already done any
|
|
||||||
// necessary modesetting on the Stderr terminal, if any.)
|
|
||||||
//
|
|
||||||
// The state argument can be nil if we're not running in panicwrap mode,
|
|
||||||
// in which case this function behaves exactly the same as Init.
|
|
||||||
func ReinitInsidePanicwrap(state *PrePanicwrapState) (*Streams, error) {
|
|
||||||
ret, err := Init()
|
|
||||||
if err != nil {
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
if state != nil {
|
|
||||||
// A lying stderr, then.
|
|
||||||
ret.Stderr = &OutputStream{
|
|
||||||
File: ret.Stderr.File,
|
|
||||||
isTerminal: func(f *os.File) bool {
|
|
||||||
return state.StderrIsTerminal
|
|
||||||
},
|
|
||||||
getColumns: func(f *os.File) int {
|
|
||||||
return state.StderrWidth
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PrePanicwrapState is a horrible thing we use to work around panicwrap,
|
|
||||||
// related to both Streams.StateForAfterPanicWrap and ReinitInsidePanicwrap.
|
|
||||||
type PrePanicwrapState struct {
|
|
||||||
StderrIsTerminal bool
|
|
||||||
StderrWidth int
|
|
||||||
}
|
|
89
main.go
89
main.go
|
@ -3,7 +3,6 @@ package main
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
@ -24,7 +23,6 @@ import (
|
||||||
"github.com/mattn/go-shellwords"
|
"github.com/mattn/go-shellwords"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
"github.com/mitchellh/colorstring"
|
"github.com/mitchellh/colorstring"
|
||||||
"github.com/mitchellh/panicwrap"
|
|
||||||
|
|
||||||
backendInit "github.com/hashicorp/terraform/internal/backend/init"
|
backendInit "github.com/hashicorp/terraform/internal/backend/init"
|
||||||
)
|
)
|
||||||
|
@ -35,12 +33,6 @@ const (
|
||||||
|
|
||||||
// The parent process will create a file to collect crash logs
|
// The parent process will create a file to collect crash logs
|
||||||
envTmpLogPath = "TF_TEMP_LOG_PATH"
|
envTmpLogPath = "TF_TEMP_LOG_PATH"
|
||||||
|
|
||||||
// Environment variable name used for smuggling true stderr terminal
|
|
||||||
// settings into a panicwrap child process. This is an implementation
|
|
||||||
// detail, subject to change in future, and should not ever be directly
|
|
||||||
// set by an end-user.
|
|
||||||
envTerminalPanicwrapWorkaround = "TF_PANICWRAP_STDERR"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ui wraps the primary output cli.Ui, and redirects Warn calls to Output
|
// ui wraps the primary output cli.Ui, and redirects Warn calls to Output
|
||||||
|
@ -54,67 +46,6 @@ func (u *ui) Warn(msg string) {
|
||||||
u.Ui.Output(msg)
|
u.Ui.Output(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
|
||||||
os.Exit(realMain())
|
|
||||||
}
|
|
||||||
|
|
||||||
func realMain() int {
|
|
||||||
var wrapConfig panicwrap.WrapConfig
|
|
||||||
|
|
||||||
// don't re-exec terraform as a child process for easier debugging
|
|
||||||
if os.Getenv("TF_FORK") == "0" {
|
|
||||||
return wrappedMain()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !panicwrap.Wrapped(&wrapConfig) {
|
|
||||||
// We always send logs to a temporary file that we use in case
|
|
||||||
// there is a panic. Otherwise, we delete it.
|
|
||||||
logTempFile, err := ioutil.TempFile("", "terraform-log")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Couldn't set up logging tempfile: %s", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
// Now that we have the file, close it and leave it for the wrapped
|
|
||||||
// process to write to.
|
|
||||||
logTempFile.Close()
|
|
||||||
defer os.Remove(logTempFile.Name())
|
|
||||||
|
|
||||||
// store the path in the environment for the wrapped executable
|
|
||||||
os.Setenv(envTmpLogPath, logTempFile.Name())
|
|
||||||
|
|
||||||
// We also need to do our terminal initialization before we fork,
|
|
||||||
// because the child process doesn't necessarily have access to
|
|
||||||
// the true stderr in order to initialize it.
|
|
||||||
streams, err := terminal.Init()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to initialize terminal: %s", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need the child process to behave _as if_ connected to the real
|
|
||||||
// stderr, even though panicwrap is about to add a pipe in the way,
|
|
||||||
// so we'll smuggle the true stderr information in an environment
|
|
||||||
// varible.
|
|
||||||
streamState := streams.StateForAfterPanicWrap()
|
|
||||||
os.Setenv(envTerminalPanicwrapWorkaround, fmt.Sprintf("%t:%d", streamState.StderrIsTerminal, streamState.StderrWidth))
|
|
||||||
|
|
||||||
// Create the configuration for panicwrap and wrap our executable
|
|
||||||
wrapConfig.Handler = logging.PanicHandler(logTempFile.Name())
|
|
||||||
wrapConfig.IgnoreSignals = ignoreSignals
|
|
||||||
wrapConfig.ForwardSignals = forwardSignals
|
|
||||||
exitStatus, err := panicwrap.Wrap(&wrapConfig)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Couldn't start Terraform: %s", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return exitStatus
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the real main
|
|
||||||
return wrappedMain()
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Ui = &ui{&cli.BasicUi{
|
Ui = &ui{&cli.BasicUi{
|
||||||
Writer: os.Stdout,
|
Writer: os.Stdout,
|
||||||
|
@ -123,7 +54,11 @@ func init() {
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
func wrappedMain() int {
|
func main() {
|
||||||
|
os.Exit(realMain())
|
||||||
|
}
|
||||||
|
|
||||||
|
func realMain() int {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
tmpLogPath := os.Getenv(envTmpLogPath)
|
tmpLogPath := os.Getenv(envTmpLogPath)
|
||||||
|
@ -145,19 +80,7 @@ func wrappedMain() int {
|
||||||
log.Printf("[INFO] Go runtime version: %s", runtime.Version())
|
log.Printf("[INFO] Go runtime version: %s", runtime.Version())
|
||||||
log.Printf("[INFO] CLI args: %#v", os.Args)
|
log.Printf("[INFO] CLI args: %#v", os.Args)
|
||||||
|
|
||||||
// This is the recieving end of our workaround to retain the metadata
|
streams, err := terminal.Init()
|
||||||
// about the real stderr even though we're talking to it via the panicwrap
|
|
||||||
// pipe. See the call to StateForAfterPanicWrap above for the producer
|
|
||||||
// part of this.
|
|
||||||
var streamState *terminal.PrePanicwrapState
|
|
||||||
if raw := os.Getenv(envTerminalPanicwrapWorkaround); raw != "" {
|
|
||||||
streamState = &terminal.PrePanicwrapState{}
|
|
||||||
if _, err := fmt.Sscanf(raw, "%t:%d", &streamState.StderrIsTerminal, &streamState.StderrWidth); err != nil {
|
|
||||||
log.Printf("[WARN] %s is set but is incorrectly-formatted: %s", envTerminalPanicwrapWorkaround, err)
|
|
||||||
streamState = nil // leave it unset for a normal init, then
|
|
||||||
}
|
|
||||||
}
|
|
||||||
streams, err := terminal.ReinitInsidePanicwrap(streamState)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Ui.Error(fmt.Sprintf("Failed to configure the terminal: %s", err))
|
Ui.Error(fmt.Sprintf("Failed to configure the terminal: %s", err))
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -130,7 +130,7 @@ func TestMain_cliArgsFromEnv(t *testing.T) {
|
||||||
// Run it!
|
// Run it!
|
||||||
os.Args = args
|
os.Args = args
|
||||||
testCommand.Args = nil
|
testCommand.Args = nil
|
||||||
exit := wrappedMain()
|
exit := realMain()
|
||||||
if (exit != 0) != tc.Err {
|
if (exit != 0) != tc.Err {
|
||||||
t.Fatalf("bad: %d", exit)
|
t.Fatalf("bad: %d", exit)
|
||||||
}
|
}
|
||||||
|
@ -237,7 +237,7 @@ func TestMain_cliArgsFromEnvAdvanced(t *testing.T) {
|
||||||
// Run it!
|
// Run it!
|
||||||
os.Args = args
|
os.Args = args
|
||||||
testCommand.Args = nil
|
testCommand.Args = nil
|
||||||
exit := wrappedMain()
|
exit := realMain()
|
||||||
if (exit != 0) != tc.Err {
|
if (exit != 0) != tc.Err {
|
||||||
t.Fatalf("unexpected exit status %d; want 0", exit)
|
t.Fatalf("unexpected exit status %d; want 0", exit)
|
||||||
}
|
}
|
||||||
|
@ -275,7 +275,7 @@ func TestMain_autoComplete(t *testing.T) {
|
||||||
|
|
||||||
// Run it!
|
// Run it!
|
||||||
os.Args = []string{"terraform", "terraform", "versio"}
|
os.Args = []string{"terraform", "terraform", "versio"}
|
||||||
exit := wrappedMain()
|
exit := realMain()
|
||||||
if exit != 0 {
|
if exit != 0 {
|
||||||
t.Fatalf("unexpected exit status %d; want 0", exit)
|
t.Fatalf("unexpected exit status %d; want 0", exit)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue