From fde09025b27d7972b3b905542251252dad4ada93 Mon Sep 17 00:00:00 2001 From: Carlos Nunez <13461447+carlosonunez@users.noreply.github.com> Date: Sat, 30 Nov 2024 15:29:09 -0600 Subject: [PATCH] feat: enable predefined VNC passwords This commit makes it possible to set a predefined password when connecting to QEMU machines via VNC when `vnc_use_password` is `true`. This way, having to set `PACKER_LOG=1` to retrieve the password is no longer needed when troubleshooting builder VM bootstrap issues. Signed-off-by: Carlos Nunez <13461447+carlosonunez@users.noreply.github.com> --- .gitignore | 1 + builder/qemu/config.go | 2 + builder/qemu/config_test.go | 37 +++++++++++++++++++ builder/qemu/step_configure_vnc.go | 21 ++++++----- .../builder/qemu/Config-not-required.mdx | 3 ++ 5 files changed, 54 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index 6c45a7b..5170db3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ dist/* packer-plugin-scaffolding example/output-ubuntu1804 crash.log +packer-plugin-qemu diff --git a/builder/qemu/config.go b/builder/qemu/config.go index 998790d..3007d36 100644 --- a/builder/qemu/config.go +++ b/builder/qemu/config.go @@ -562,6 +562,8 @@ type Config struct { // binded to for VNC. By default packer will use 127.0.0.1 for this. If you // wish to bind to all interfaces use 0.0.0.0. VNCBindAddress string `mapstructure:"vnc_bind_address" required:"false"` + // The password to set when VNCUsePassword == true. + VNCPassword string `mapstructure:"vnc_password" required:"false"` // Whether or not to set a password on the VNC server. This option // automatically enables the QMP socket. See `qmp_socket_path`. Defaults to // `false`. diff --git a/builder/qemu/config_test.go b/builder/qemu/config_test.go index 821bcd9..5bfeb1c 100644 --- a/builder/qemu/config_test.go +++ b/builder/qemu/config_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/packer-plugin-sdk/common" packersdk "github.com/hashicorp/packer-plugin-sdk/packer" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) var testPem = ` @@ -642,6 +643,42 @@ func TestBuilderPrepare_VNCPassword(t *testing.T) { } } +func TestVNCPasswordEmptyWhenUsePasswordFalse(t *testing.T) { + var c Config + config := testConfig() + config["vnc_use_password"] = false + config["vnc_password"] = "supersecret" + warns, err := c.Prepare(config) + require.NoError(t, err) + assert.Len(t, warns, 0, "bad: %#v", warns) + p := vncpwd{} + assert.Empty(t, p.VNCPassword(&c)) +} + +func TestVNCPasswordRandomWhenVNCPasswordEmpty(t *testing.T) { + var c Config + config := testConfig() + config["vnc_use_password"] = true + config["vnc_password"] = "" + warns, err := c.Prepare(config) + require.NoError(t, err) + assert.Len(t, warns, 0, "bad: %#v", warns) + p := vncpwd{} + assert.Len(t, p.VNCPassword(&c), 8) +} + +func TestVNCPasswordSetWhenVNCPasswordNotEmpty(t *testing.T) { + var c Config + config := testConfig() + config["vnc_use_password"] = true + config["vnc_password"] = "supersecret" + warns, err := c.Prepare(config) + require.NoError(t, err) + assert.Len(t, warns, 0, "bad: %#v", warns) + p := vncpwd{} + assert.Equal(t, p.VNCPassword(&c), config["vnc_password"]) +} + func TestCommConfigPrepare_BackwardsCompatibility(t *testing.T) { var c Config config := testConfig() diff --git a/builder/qemu/step_configure_vnc.go b/builder/qemu/step_configure_vnc.go index c3a9a94..56a1f27 100644 --- a/builder/qemu/step_configure_vnc.go +++ b/builder/qemu/step_configure_vnc.go @@ -28,7 +28,15 @@ type stepConfigureVNC struct { l *net.Listener } -func VNCPassword() string { +type vncpwd struct{} + +func (p *vncpwd) VNCPassword(c *Config) string { + if !c.VNCUsePassword { + return "" + } + if len(c.VNCPassword) != 0 { + return c.VNCPassword + } length := int(8) charSet := []byte("012345689abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") @@ -39,7 +47,6 @@ func VNCPassword() string { for i := 0; i < length; i++ { password[i] = charSet[rand.Intn(charSetLength)] } - return string(password) } @@ -54,7 +61,6 @@ func (s *stepConfigureVNC) Run(ctx context.Context, state multistep.StateBag) mu ui.Say(msg) log.Print(msg) - var vncPassword string var err error s.l, err = net.ListenRangeConfig{ Addr: config.VNCBindAddress, @@ -70,16 +76,11 @@ func (s *stepConfigureVNC) Run(ctx context.Context, state multistep.StateBag) mu } s.l.Listener.Close() // free port, but don't unlock lock file vncPort := s.l.Port - - if config.VNCUsePassword { - vncPassword = VNCPassword() - } else { - vncPassword = "" - } + vncPassword := vncpwd{} log.Printf("Found available VNC port: %d on IP: %s", vncPort, config.VNCBindAddress) state.Put("vnc_port", vncPort) - state.Put("vnc_password", vncPassword) + state.Put("vnc_password", vncPassword.VNCPassword(config)) return multistep.ActionContinue } diff --git a/docs-partials/builder/qemu/Config-not-required.mdx b/docs-partials/builder/qemu/Config-not-required.mdx index db346af..5d05106 100644 --- a/docs-partials/builder/qemu/Config-not-required.mdx +++ b/docs-partials/builder/qemu/Config-not-required.mdx @@ -331,6 +331,9 @@ binded to for VNC. By default packer will use 127.0.0.1 for this. If you wish to bind to all interfaces use 0.0.0.0. +- `vnc_password` (bool) - The password to set for the VNC password when + `vnc_use_password` is true; automatically generated otherwise. + - `vnc_use_password` (bool) - Whether or not to set a password on the VNC server. This option automatically enables the QMP socket. See `qmp_socket_path`. Defaults to `false`.