working on windows

This commit is contained in:
Megan Marsh 2020-04-29 12:13:22 -07:00
parent 32752d4577
commit c578afc62c
3 changed files with 81 additions and 28 deletions

View File

@ -8,6 +8,7 @@ import (
"fmt" "fmt"
"os" "os"
"regexp" "regexp"
"runtime"
"time" "time"
"github.com/hashicorp/packer/common" "github.com/hashicorp/packer/common"
@ -329,10 +330,16 @@ func (c *Config) Prepare(raws ...interface{}) ([]string, error) {
// set defaults for IAP // set defaults for IAP
if c.IAPConfig.IAPHashBang == "" { if c.IAPConfig.IAPHashBang == "" {
c.IAPConfig.IAPHashBang = "/bin/sh" if runtime.GOOS == "windows" {
c.IAPConfig.IAPHashBang = ""
} else {
c.IAPConfig.IAPHashBang = "/bin/sh"
}
} }
if c.IAPConfig.IAPExt == "" { if c.IAPConfig.IAPExt == "" {
c.IAPConfig.IAPExt = ".sh" if runtime.GOOS == "windows" {
c.IAPConfig.IAPExt = ".cmd"
}
} }
// Configure IAP: Update SSH config to use localhost proxy instead // Configure IAP: Update SSH config to use localhost proxy instead

View File

@ -105,12 +105,15 @@ func (s *StepStartTunnel) createTempGcloudScript(args []string) (string, error)
// Write our contents to it // Write our contents to it
writer := bufio.NewWriter(tf) writer := bufio.NewWriter(tf)
s.IAPConf.IAPHashBang = fmt.Sprintf("#!%s\n", s.IAPConf.IAPHashBang) if s.IAPConf.IAPHashBang != "" {
log.Printf("[INFO] (google): Prepending inline gcloud setup script with %s", s.IAPConf.IAPHashBang = fmt.Sprintf("#!%s\n", s.IAPConf.IAPHashBang)
s.IAPConf.IAPHashBang) log.Printf("[INFO] (google): Prepending inline gcloud setup script with %s",
_, err = writer.WriteString(s.IAPConf.IAPHashBang) s.IAPConf.IAPHashBang)
if err != nil { _, err = writer.WriteString(s.IAPConf.IAPHashBang)
return "", fmt.Errorf("Error preparing inline hashbang: %s", err) if err != nil {
return "", fmt.Errorf("Error preparing inline hashbang: %s", err)
}
} }
// authenticate to gcloud // authenticate to gcloud
@ -130,7 +133,8 @@ func (s *StepStartTunnel) createTempGcloudScript(args []string) (string, error)
if err := writer.Flush(); err != nil { if err := writer.Flush(); err != nil {
return "", fmt.Errorf("Error preparing shell script: %s", err) return "", fmt.Errorf("Error preparing shell script: %s", err)
} }
// Have to close temp file before renaming it or Windows will complain.
tf.Close()
err = os.Chmod(tf.Name(), 0700) err = os.Chmod(tf.Name(), 0700)
if err != nil { if err != nil {
log.Printf("[ERROR] (google): error modifying permissions of temp script file: %s", err.Error()) log.Printf("[ERROR] (google): error modifying permissions of temp script file: %s", err.Error())
@ -181,8 +185,7 @@ func (s *StepStartTunnel) Run(ctx context.Context, state multistep.StateBag) mul
// TODO make setting LocalHostPort optional // TODO make setting LocalHostPort optional
s.CommConf.SSHPort = s.IAPConf.IAPLocalhostPort s.CommConf.SSHPort = s.IAPConf.IAPLocalhostPort
log.Printf("Calling tunnel launch with args %#v", args) log.Printf("Creating tunnel launch script with args %#v", args)
// Create temp file that contains both gcloud authentication, and gcloud // Create temp file that contains both gcloud authentication, and gcloud
// proxy setup call. // proxy setup call.
tempScriptFileName, err := s.createTempGcloudScript(args) tempScriptFileName, err := s.createTempGcloudScript(args)
@ -211,6 +214,11 @@ func (s *StepStartTunnel) Run(ctx context.Context, state multistep.StateBag) mul
err := s.tunnelDriver.StartTunnel(ctx, tempScriptFileName) err := s.tunnelDriver.StartTunnel(ctx, tempScriptFileName)
return err return err
}) })
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
return multistep.ActionContinue return multistep.ActionContinue
} }
@ -221,5 +229,7 @@ func (s *StepStartTunnel) Cleanup(state multistep.StateBag) {
log.Printf("Skipping cleanup of IAP tunnel; \"iap\" is false.") log.Printf("Skipping cleanup of IAP tunnel; \"iap\" is false.")
return return
} }
s.tunnelDriver.StopTunnel() if s.tunnelDriver != nil {
s.tunnelDriver.StopTunnel()
}
} }

View File

@ -6,6 +6,7 @@ import (
"bytes" "bytes"
"context" "context"
"fmt" "fmt"
"io"
"log" "log"
"os/exec" "os/exec"
"strings" "strings"
@ -13,6 +14,7 @@ import (
) )
func NewTunnelDriver() TunnelDriver { func NewTunnelDriver() TunnelDriver {
log.Printf("Megan created driver for windows")
return &TunnelDriverWindows{} return &TunnelDriverWindows{}
} }
@ -21,37 +23,71 @@ type TunnelDriverWindows struct {
} }
func (t *TunnelDriverWindows) StartTunnel(cancelCtx context.Context, tempScriptFileName string) error { func (t *TunnelDriverWindows) StartTunnel(cancelCtx context.Context, tempScriptFileName string) error {
log.Printf("Megan inside StartTunnel for windows")
// set stdout and stderr so we can read what's going on. // set stdout and stderr so we can read what's going on.
var stdout, stderr bytes.Buffer var stdout, stderr bytes.Buffer
cmd := exec.CommandContext(cancelCtx, tempScriptFileName) args := []string{"/C", "call", tempScriptFileName}
cmd := exec.CommandContext(cancelCtx, "cmd", args...)
cmd.Stdout = &stdout cmd.Stdout = &stdout
cmd.Stderr = &stderr cmd.Stderr = &stderr
err := cmd.Start() err := cmd.Start()
log.Printf("Waiting 30s for tunnel to create...")
if err != nil { if err != nil {
log.Printf("Megan: error calling start.")
err := fmt.Errorf("Error calling gcloud sdk to launch IAP tunnel: %s", err := fmt.Errorf("Error calling gcloud sdk to launch IAP tunnel: %s",
err) err)
return err return err
} }
// Wait for tunnel to launch and gather response. TODO: do this without
// a sleep.
time.Sleep(30 * time.Second)
// Track stdout. // Give tunnel 30 seconds to either launch, or return an error.
sout := stdout.String() // Unfortunately, the SDK doesn't provide any official acknowledgment that
if sout != "" { // the tunnel is launched when it's not being run through a TTY so we
log.Printf("[start-iap-tunnel] stdout is:") // are just trusting here that 30s is enough to know whether the tunnel
// launch was going to fail. Yep, feels icky to me too. But I spent an
// afternoon trying to figure out how to get the SDK to actually send
// the "Listening on port [n]" line I see when I run it manually, and I
// can't justify spending more time than that on aesthetics.
for i := 0; i < 30; i++ {
time.Sleep(1 * time.Second)
lineStderr, err := stderr.ReadString('\n')
if err != nil && err != io.EOF {
log.Printf("Err from scanning stderr is %s", err)
return fmt.Errorf("Error reading stderr from tunnel launch: %s", err)
}
if lineStderr != "" {
log.Printf("stderr: %s", lineStderr)
}
lineStdout, err := stdout.ReadString('\n')
if err != nil && err != io.EOF {
log.Printf("Err from scanning stdout is %s", err)
return fmt.Errorf("Error reading stdout from tunnel launch: %s", err)
}
if lineStdout != "" {
log.Printf("stdout: %s", lineStdout)
}
if strings.Contains(lineStderr, "ERROR") {
// 4033: Either you don't have permission to access the instance,
// the instance doesn't exist, or the instance is stopped.
// The two sub-errors we may see while the permissions settle are
// "not authorized" and "failed to connect to backend," but after
// about a minute of retries this goes away and we're able to
// connect.
if strings.Contains(lineStderr, "4033") {
return RetryableTunnelError{lineStderr}
} else {
log.Printf("NOT RETRYABLE: %s", lineStderr)
return fmt.Errorf("Non-retryable tunnel error: %s", lineStderr)
}
}
} }
log.Printf("[start-iap-tunnel] stderr is:") log.Printf("No error detected after tunnel launch; continuing...")
serr := stderr.String()
log.Println(serr)
if strings.Contains(serr, "ERROR") {
errIdx := strings.Index(serr, "ERROR:")
return fmt.Errorf("ERROR: %s", serr[errIdx+7:])
}
// Store successful command on step so we can access it to cancel it // Store successful command on step so we can access it to cancel it
// later. // later.
t.cmd = cmd t.cmd = cmd