Setup panicwrap
This commit is contained in:
parent
38d4f2a1bd
commit
7b64c2597b
|
@ -0,0 +1,29 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// These are the environmental variables that determine if we log, and if
|
||||||
|
// we log whether or not the log should go to a file.
|
||||||
|
const EnvLog = "TF_LOG"
|
||||||
|
const EnvLogFile = "TF_LOG_PATH"
|
||||||
|
|
||||||
|
// logOutput determines where we should send logs (if anywhere).
|
||||||
|
func logOutput() (logOutput io.Writer, err error) {
|
||||||
|
logOutput = nil
|
||||||
|
if os.Getenv(EnvLog) != "" {
|
||||||
|
logOutput = os.Stderr
|
||||||
|
|
||||||
|
if logPath := os.Getenv(EnvLogFile); logPath != "" {
|
||||||
|
var err error
|
||||||
|
logOutput, err = os.Create(logPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
76
main.go
76
main.go
|
@ -6,7 +6,9 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/ActiveState/tail"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
|
"github.com/mitchellh/panicwrap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -14,7 +16,79 @@ func main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func realMain() int {
|
func realMain() int {
|
||||||
log.SetOutput(ioutil.Discard)
|
var wrapConfig panicwrap.WrapConfig
|
||||||
|
|
||||||
|
if !panicwrap.Wrapped(&wrapConfig) {
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 setup logging tempfile: %s", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
logTempFile.Close()
|
||||||
|
defer os.Remove(logTempFile.Name())
|
||||||
|
|
||||||
|
// Tell the logger to log to this file
|
||||||
|
os.Setenv(EnvLog, "1")
|
||||||
|
os.Setenv(EnvLogFile, logTempFile.Name())
|
||||||
|
|
||||||
|
if logWriter != nil {
|
||||||
|
// Start tailing the file beforehand to get the data
|
||||||
|
t, err := tail.TailFile(logTempFile.Name(), tail.Config{
|
||||||
|
Follow: true,
|
||||||
|
Logger: tail.DiscardingLogger,
|
||||||
|
MustExist: true,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Couldn't setup logging tempfile: %s", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
go func() {
|
||||||
|
for line := range t.Lines {
|
||||||
|
logWriter.Write([]byte(line.Text + "\n"))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the configuration for panicwrap and wrap our executable
|
||||||
|
wrapConfig.Handler = panicHandler(logTempFile)
|
||||||
|
exitStatus, err := panicwrap.Wrap(&wrapConfig)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Couldn't start Terraform: %s", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// If >= 0, we're the parent, so just exit
|
||||||
|
if exitStatus >= 0 {
|
||||||
|
return exitStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call the real main
|
||||||
|
return wrappedMain()
|
||||||
|
}
|
||||||
|
|
||||||
|
func wrappedMain() int {
|
||||||
|
logOutput, err := logOutput()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error starting Terraform: %s", err)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if logOutput != nil {
|
||||||
|
log.SetOutput(logOutput)
|
||||||
|
}
|
||||||
|
|
||||||
// Get the command line args. We shortcut "--version" and "-v" to
|
// Get the command line args. We shortcut "--version" and "-v" to
|
||||||
// just show the version.
|
// just show the version.
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mitchellh/panicwrap"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This output is shown if a panic happens.
|
||||||
|
const panicOutput = `
|
||||||
|
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
|
||||||
|
Terraform crashed! This is always indicative of a bug within Terraform.
|
||||||
|
A crash log has been placed at "crash.log" relative to your current
|
||||||
|
working directory. It would be immensely helpful if you could please
|
||||||
|
report the crash with Terraform[1] so that we can fix this.
|
||||||
|
|
||||||
|
[1]: https://github.com/hashicorp/terraform/issues
|
||||||
|
|
||||||
|
!!!!!!!!!!!!!!!!!!!!!!!!!!! TERRAFORM CRASH !!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||||
|
`
|
||||||
|
|
||||||
|
// panicHandler is what is called by panicwrap when a panic is encountered
|
||||||
|
// within Terraform. It is guaranteed to run after the resulting process has
|
||||||
|
// exited so we can take the log file, add in the panic, and store it
|
||||||
|
// somewhere locally.
|
||||||
|
func panicHandler(logF *os.File) panicwrap.HandlerFunc {
|
||||||
|
return func(m string) {
|
||||||
|
// Right away just output this thing on stderr so that it gets
|
||||||
|
// shown in case anything below fails.
|
||||||
|
fmt.Fprintf(os.Stderr, fmt.Sprintf("%s\n", m))
|
||||||
|
|
||||||
|
// Create the crash log file where we'll write the logs
|
||||||
|
f, err := os.Create("crash.log")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to create crash log file: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
// Seek the log file back to the beginning
|
||||||
|
if _, err = logF.Seek(0, 0); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to seek log file for crash: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the contents to the crash file. This will include
|
||||||
|
// the panic that just happened.
|
||||||
|
if _, err = io.Copy(f, logF); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to write crash log: %s", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the user a crash occurred in some helpful way that
|
||||||
|
// they'll hopefully notice.
|
||||||
|
fmt.Printf("\n\n")
|
||||||
|
fmt.Println(strings.TrimSpace(panicOutput))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue