2020-04-22 16:59:29 -04:00
|
|
|
// +build !windows
|
|
|
|
|
|
|
|
package googlecompute
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"os/exec"
|
|
|
|
"strings"
|
|
|
|
"syscall"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
func NewTunnelDriver() TunnelDriver {
|
|
|
|
return &TunnelDriverLinux{}
|
|
|
|
}
|
|
|
|
|
|
|
|
type TunnelDriverLinux struct {
|
|
|
|
cmd *exec.Cmd
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TunnelDriverLinux) StartTunnel(cancelCtx context.Context, tempScriptFileName string) error {
|
|
|
|
// set stdout and stderr so we can read what's going on.
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
|
|
|
|
|
|
cmd := exec.CommandContext(cancelCtx, tempScriptFileName)
|
|
|
|
cmd.Stdout = &stdout
|
|
|
|
cmd.Stderr = &stderr
|
|
|
|
|
|
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
|
|
|
|
|
|
|
err := cmd.Start()
|
|
|
|
log.Printf("Waiting 30s for tunnel to create...")
|
|
|
|
if err != nil {
|
|
|
|
err := fmt.Errorf("Error calling gcloud sdk to launch IAP tunnel: %s",
|
|
|
|
err)
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Wait for tunnel to launch and gather response. TODO: do this without
|
|
|
|
// a sleep.
|
|
|
|
time.Sleep(30 * time.Second)
|
|
|
|
|
|
|
|
// Track stdout.
|
|
|
|
sout := stdout.String()
|
|
|
|
if sout != "" {
|
|
|
|
log.Printf("[start-iap-tunnel] stdout is:")
|
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[start-iap-tunnel] stderr is:")
|
|
|
|
serr := stderr.String()
|
|
|
|
log.Println(serr)
|
|
|
|
if strings.Contains(serr, "ERROR") {
|
|
|
|
errIdx := strings.Index(serr, "ERROR:")
|
2020-04-22 17:20:29 -04:00
|
|
|
return fmt.Errorf("ERROR: %s", serr[errIdx+7:])
|
2020-04-22 16:59:29 -04:00
|
|
|
}
|
|
|
|
// Store successful command on step so we can access it to cancel it
|
|
|
|
// later.
|
|
|
|
t.cmd = cmd
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TunnelDriverLinux) StopTunnel() {
|
|
|
|
if t.cmd != nil && t.cmd.Process != nil {
|
|
|
|
log.Printf("Cleaning up the IAP tunnel...")
|
2020-04-22 17:26:50 -04:00
|
|
|
// Why not just cmd.Process.Kill()? I'm glad you asked. The gcloud
|
2020-04-22 16:59:29 -04:00
|
|
|
// call spawns a python subprocess that listens on the port, and you
|
|
|
|
// need to use the process _group_ id to kill this process and its
|
|
|
|
// daemon child. We create the group ID with the syscall.SysProcAttr
|
|
|
|
// call inside the retry loop above, and then store that ID on the
|
|
|
|
// command so we can destroy it here.
|
2020-04-22 17:20:29 -04:00
|
|
|
err := syscall.Kill(-t.cmd.Process.Pid, syscall.SIGKILL)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Issue stopping IAP tunnel: %s", err)
|
|
|
|
}
|
2020-04-22 16:59:29 -04:00
|
|
|
} else {
|
|
|
|
log.Printf("Couldn't find IAP tunnel process to kill. Continuing.")
|
|
|
|
}
|
|
|
|
}
|