Compare commits

...

1 Commits

Author SHA1 Message Date
Dom Del Nano
d080278e7b Crude proof of concept using XO as a web socket proxy for packer 2021-06-03 21:26:10 -07:00

View File

@ -6,9 +6,7 @@ import (
"context" "context"
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io"
"log" "log"
"net"
"strings" "strings"
"time" "time"
"unicode" "unicode"
@ -18,6 +16,7 @@ import (
"github.com/hashicorp/packer-plugin-sdk/packer" "github.com/hashicorp/packer-plugin-sdk/packer"
"github.com/hashicorp/packer-plugin-sdk/template/interpolate" "github.com/hashicorp/packer-plugin-sdk/template/interpolate"
"github.com/mitchellh/go-vnc" "github.com/mitchellh/go-vnc"
"golang.org/x/net/websocket"
) )
const KeyLeftShift uint = 0xFFE1 const KeyLeftShift uint = 0xFFE1
@ -35,7 +34,6 @@ type StepTypeBootCommand struct {
func (self *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction { func (self *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
config := state.Get("commonconfig").(CommonConfig) config := state.Get("commonconfig").(CommonConfig)
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
c := state.Get("client").(*Connection)
httpPort := state.Get("http_port").(int) httpPort := state.Get("http_port").(int)
// skip this step if we have nothing to type // skip this step if we have nothing to type
@ -43,80 +41,30 @@ func (self *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateB
return multistep.ActionContinue return multistep.ActionContinue
} }
vmRef, err := c.client.VM.GetByNameLabel(c.session, config.VMName)
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(vmRef) != 1 {
ui.Error(fmt.Sprintf("expected to find a single VM, instead found '%d'. Ensure the VM name is unique", len(vmRef)))
}
consoles, err := c.client.VM.GetConsoles(c.session, vmRef[0])
if err != nil {
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
if len(consoles) != 1 {
ui.Error(fmt.Sprintf("expected to find a VM console, instead found '%d'. Ensure there is only one console", len(consoles)))
return multistep.ActionHalt
}
location, err := c.client.Console.GetLocation(c.session, consoles[0])
if err != nil {
ui.Error(err.Error())
return multistep.ActionHalt
}
locationPieces := strings.SplitAfter(location, "/")
consoleHost := strings.TrimSuffix(locationPieces[2], "/")
ui.Say(fmt.Sprintf("Connecting to the VM console VNC over xapi via %s", consoleHost))
conn, err := net.Dial("tcp", fmt.Sprintf("%s:443", consoleHost))
if err != nil {
err := fmt.Errorf("Error connecting to VNC: %s", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
defer conn.Close()
tlsConfig := &tls.Config{ tlsConfig := &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
} }
tlsConn := tls.Client(conn, tlsConfig) hostname := "wss://xoa.internal.ddelnano.com"
cfg, err := websocket.NewConfig(hostname, hostname)
consoleLocation := strings.TrimSpace(fmt.Sprintf("/%s", locationPieces[len(locationPieces)-1])) ui.Say("Using new websocket implementation")
httpReq := fmt.Sprintf("CONNECT %s HTTP/1.0\r\nHost: %s\r\nCookie: session_id=%s\r\n\r\n", consoleLocation, consoleHost, c.session)
fmt.Printf("Sending the follow http req: %v", httpReq)
ui.Say(fmt.Sprintf("Making HTTP request to initiate VNC connection: %s", httpReq))
_, err = io.WriteString(tlsConn, httpReq)
if err != nil { if err != nil {
err := fmt.Errorf("failed to start vnc session: %v", err) err := fmt.Errorf("Error creating websocket config: %v", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
cfg.TlsConfig = tlsConfig
wss, err := websocket.DialConfig(cfg)
if err != nil {
err := fmt.Errorf("Error creating websocket client: %v", err)
state.Put("error", err) state.Put("error", err)
ui.Error(err.Error()) ui.Error(err.Error())
return multistep.ActionHalt return multistep.ActionHalt
} }
buffer := make([]byte, 10000) vncClient, err := vnc.Client(wss, &vnc.ClientConfig{Exclusive: true})
_, err = tlsConn.Read(buffer)
if err != nil && err != io.EOF {
err := fmt.Errorf("failed to read vnc session response: %v", err)
state.Put("error", err)
ui.Error(err.Error())
return multistep.ActionHalt
}
ui.Say(fmt.Sprintf("Received response: %s", string(buffer)))
vncClient, err := vnc.Client(tlsConn, &vnc.ClientConfig{Exclusive: true})
if err != nil { if err != nil {
err := fmt.Errorf("Error establishing VNC session: %s", err) err := fmt.Errorf("Error establishing VNC session: %s", err)
@ -164,7 +112,7 @@ func (self *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateB
return multistep.ActionHalt return multistep.ActionHalt
} }
vncSendString(vncClient, command) VncSendString(vncClient, command)
} }
ui.Say("Finished typing.") ui.Say("Finished typing.")
@ -175,7 +123,7 @@ func (self *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateB
func (self *StepTypeBootCommand) Cleanup(multistep.StateBag) {} func (self *StepTypeBootCommand) Cleanup(multistep.StateBag) {}
// Taken from qemu's builder plugin - not an exported function. // Taken from qemu's builder plugin - not an exported function.
func vncSendString(c *vnc.ClientConn, original string) { func VncSendString(c *vnc.ClientConn, original string) {
// Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h // Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h
special := make(map[string]uint32) special := make(map[string]uint32)
special["<bs>"] = 0xFF08 special["<bs>"] = 0xFF08