-
Notifications
You must be signed in to change notification settings - Fork 45
Read only the http header #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,13 +10,11 @@ import ( | |
| "log" | ||
| "net" | ||
| "strings" | ||
| "time" | ||
| "unicode" | ||
| "unicode/utf8" | ||
|
|
||
| "github.com/hashicorp/packer-plugin-sdk/multistep" | ||
| "github.com/hashicorp/packer-plugin-sdk/packer" | ||
| "github.com/hashicorp/packer-plugin-sdk/template/interpolate" | ||
| "github.com/hashicorp/packer-plugin-sdk/bootcommand" | ||
| "github.com/mitchellh/go-vnc" | ||
| ) | ||
|
|
||
|
|
@@ -105,18 +103,38 @@ func (self *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateB | |
| return multistep.ActionHalt | ||
| } | ||
|
|
||
| buffer := make([]byte, 10000) | ||
| _, 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 | ||
| } | ||
| // Look for \r\n\r\n sequence. Everything after the HTTP Header is for the vnc client. | ||
|
|
||
| builder := strings.Builder{} | ||
| buffer := make([]byte, 1) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added a comment elsewhere with a git diff that shows how to avoid the unbuffered reading without consuming the buffer. Please give that a try. |
||
| sequenceProgress := 0 | ||
|
|
||
| for { | ||
| if _, err := io.ReadFull(tlsConn, buffer); err != nil { | ||
| err := fmt.Errorf("failed to start vnc session: %v", err) | ||
| state.Put("error", err) | ||
| ui.Error(err.Error()) | ||
| return multistep.ActionHalt | ||
| } | ||
|
|
||
| ui.Say(fmt.Sprintf("Received response: %s", string(buffer))) | ||
| builder.WriteByte(buffer[0]) | ||
|
|
||
| if buffer[0] == '\n' && sequenceProgress % 2 == 1 { | ||
| sequenceProgress++ | ||
| } else if buffer[0] == '\r' && sequenceProgress % 2 == 0 { | ||
| sequenceProgress++ | ||
| } else { | ||
| sequenceProgress = 0 | ||
| } | ||
|
|
||
| if sequenceProgress == 4 { | ||
| break | ||
| } | ||
| } | ||
|
|
||
| ui.Say(fmt.Sprintf("Received response: %s", builder.String())) | ||
|
|
||
| vncClient, err := vnc.Client(tlsConn, &vnc.ClientConfig{Exclusive: true}) | ||
| vncClient, err := vnc.Client(tlsConn, &vnc.ClientConfig{Exclusive: false}) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did you notice any bad behavior with the previous setting? |
||
|
|
||
| if err != nil { | ||
| err := fmt.Errorf("Error establishing VNC session: %s", err) | ||
|
|
@@ -126,6 +144,7 @@ func (self *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateB | |
| } | ||
|
|
||
| defer vncClient.Close() | ||
|
|
||
|
|
||
| log.Printf("Connected to the VNC console: %s", vncClient.DesktopName) | ||
|
|
||
|
|
@@ -148,125 +167,39 @@ func (self *StepTypeBootCommand) Run(ctx context.Context, state multistep.StateB | |
| uint(httpPort), | ||
| } | ||
|
|
||
| ui.Say("Typing boot commands over VNC...") | ||
| for _, command := range config.BootCommand { | ||
|
|
||
| command, err := interpolate.Render(command, &self.Ctx) | ||
| if err != nil { | ||
| err := fmt.Errorf("Error preparing boot command: %s", err) | ||
| state.Put("error", err) | ||
| ui.Error(err.Error()) | ||
| return multistep.ActionHalt | ||
| } | ||
|
|
||
| // Check for interrupts | ||
| if _, ok := state.GetOk(multistep.StateCancelled); ok { | ||
| return multistep.ActionHalt | ||
| } | ||
| vncDriver := bootcommand.NewVNCDriver(vncClient, config.VNCConfig.BootKeyInterval) | ||
|
|
||
| vncSendString(vncClient, command) | ||
| ui.Say("Typing boot commands over VNC...") | ||
|
|
||
| command, err := interpolate.Render(config.VNCConfig.FlatBootCommand(), &self.Ctx) | ||
|
|
||
| if err != nil { | ||
| err := fmt.Errorf("Error preparing boot command: %s", err) | ||
| state.Put("error", err) | ||
| ui.Error(err.Error()) | ||
| return multistep.ActionHalt | ||
| } | ||
|
|
||
| seq, err := bootcommand.GenerateExpressionSequence(command) | ||
|
|
||
| if err != nil { | ||
| err := fmt.Errorf("Error generating boot command: %s", err) | ||
| state.Put("error", err) | ||
| ui.Error(err.Error()) | ||
| return multistep.ActionHalt | ||
| } | ||
|
|
||
| if err := seq.Do(ctx, vncDriver); err != nil { | ||
| err := fmt.Errorf("Error running boot command: %s", err) | ||
| state.Put("error", err) | ||
| ui.Error(err.Error()) | ||
| return multistep.ActionHalt | ||
| } | ||
|
|
||
|
|
||
| ui.Say("Finished typing.") | ||
|
|
||
| return multistep.ActionContinue | ||
| } | ||
|
|
||
| func (self *StepTypeBootCommand) Cleanup(multistep.StateBag) {} | ||
|
|
||
| // Taken from qemu's builder plugin - not an exported function. | ||
| func vncSendString(c *vnc.ClientConn, original string) { | ||
| // Scancodes reference: https://github.com/qemu/qemu/blob/master/ui/vnc_keysym.h | ||
| special := make(map[string]uint32) | ||
| special["<bs>"] = 0xFF08 | ||
| special["<del>"] = 0xFFFF | ||
| special["<enter>"] = 0xFF0D | ||
| special["<esc>"] = 0xFF1B | ||
| special["<f1>"] = 0xFFBE | ||
| special["<f2>"] = 0xFFBF | ||
| special["<f3>"] = 0xFFC0 | ||
| special["<f4>"] = 0xFFC1 | ||
| special["<f5>"] = 0xFFC2 | ||
| special["<f6>"] = 0xFFC3 | ||
| special["<f7>"] = 0xFFC4 | ||
| special["<f8>"] = 0xFFC5 | ||
| special["<f9>"] = 0xFFC6 | ||
| special["<f10>"] = 0xFFC7 | ||
| special["<f11>"] = 0xFFC8 | ||
| special["<f12>"] = 0xFFC9 | ||
| special["<return>"] = 0xFF0D | ||
| special["<tab>"] = 0xFF09 | ||
| special["<up>"] = 0xFF52 | ||
| special["<down>"] = 0xFF54 | ||
| special["<left>"] = 0xFF51 | ||
| special["<right>"] = 0xFF53 | ||
| special["<spacebar>"] = 0x020 | ||
| special["<insert>"] = 0xFF63 | ||
| special["<home>"] = 0xFF50 | ||
| special["<end>"] = 0xFF57 | ||
| special["<pageUp>"] = 0xFF55 | ||
| special["<pageDown>"] = 0xFF56 | ||
|
|
||
| shiftedChars := "~!@#$%^&*()_+{}|:\"<>?" | ||
|
|
||
| // TODO(mitchellh): Ripe for optimizations of some point, perhaps. | ||
| for len(original) > 0 { | ||
| var keyCode uint32 | ||
| keyShift := false | ||
|
|
||
| if strings.HasPrefix(original, "<wait>") { | ||
| log.Printf("Special code '<wait>' found, sleeping one second") | ||
| time.Sleep(1 * time.Second) | ||
| original = original[len("<wait>"):] | ||
| continue | ||
| } | ||
|
|
||
| if strings.HasPrefix(original, "<wait5>") { | ||
| log.Printf("Special code '<wait5>' found, sleeping 5 seconds") | ||
| time.Sleep(5 * time.Second) | ||
| original = original[len("<wait5>"):] | ||
| continue | ||
| } | ||
|
|
||
| if strings.HasPrefix(original, "<wait10>") { | ||
| log.Printf("Special code '<wait10>' found, sleeping 10 seconds") | ||
| time.Sleep(10 * time.Second) | ||
| original = original[len("<wait10>"):] | ||
| continue | ||
| } | ||
|
|
||
| for specialCode, specialValue := range special { | ||
| if strings.HasPrefix(original, specialCode) { | ||
| log.Printf("Special code '%s' found, replacing with: %d", specialCode, specialValue) | ||
| keyCode = specialValue | ||
| original = original[len(specialCode):] | ||
| break | ||
| } | ||
| } | ||
|
|
||
| if keyCode == 0 { | ||
| r, size := utf8.DecodeRuneInString(original) | ||
| original = original[size:] | ||
| keyCode = uint32(r) | ||
| keyShift = unicode.IsUpper(r) || strings.ContainsRune(shiftedChars, r) | ||
|
|
||
| log.Printf("Sending char '%c', code %d, shift %v", r, keyCode, keyShift) | ||
| } | ||
|
|
||
| if keyShift { | ||
| c.KeyEvent(uint32(KeyLeftShift), true) | ||
| } | ||
|
|
||
| c.KeyEvent(keyCode, true) | ||
| time.Sleep(time.Second / 10) | ||
| c.KeyEvent(keyCode, false) | ||
| time.Sleep(time.Second / 10) | ||
|
|
||
| if keyShift { | ||
| c.KeyEvent(uint32(KeyLeftShift), false) | ||
| } | ||
|
|
||
| // no matter what, wait a small period | ||
| time.Sleep(50 * time.Millisecond) | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you remove the
/* Heavily borrowed from builder/quemu/step_type_boot_command.go */comment at the top of this file? That shouldn't be the case now that we are using thebootcommandpackage :)