From 4bf6a56d71fc968b58732ad7c9f9e416d936f7a7 Mon Sep 17 00:00:00 2001 From: Adrien Delorme Date: Thu, 6 Jun 2019 16:44:48 +0200 Subject: [PATCH] split communitacor.SSH & communitacor.WinRM to facilitate documentation put doc for communicator into the struct --- builder/alicloud/ecs/run_config_test.go | 4 +- builder/amazon/common/run_config_test.go | 4 +- .../common/step_run_spot_instance_test.go | 4 +- builder/openstack/run_config_test.go | 4 +- builder/parallels/common/ssh_config_test.go | 4 +- builder/tencentcloud/cvm/run_config_test.go | 4 +- builder/virtualbox/common/ssh_config_test.go | 4 +- builder/vmware/common/ssh_config_test.go | 4 +- helper/communicator/config.go | 208 ++++++++++++++---- helper/communicator/config_test.go | 50 +++-- 10 files changed, 224 insertions(+), 66 deletions(-) diff --git a/builder/alicloud/ecs/run_config_test.go b/builder/alicloud/ecs/run_config_test.go index 46d9b11de..f0cb6d67b 100644 --- a/builder/alicloud/ecs/run_config_test.go +++ b/builder/alicloud/ecs/run_config_test.go @@ -13,7 +13,9 @@ func testConfig() *RunConfig { AlicloudSourceImage: "alicloud_images", InstanceType: "ecs.n1.tiny", Comm: communicator.Config{ - SSHUsername: "alicloud", + SSH: communicator.SSH{ + SSHUsername: "alicloud", + }, }, } } diff --git a/builder/amazon/common/run_config_test.go b/builder/amazon/common/run_config_test.go index 9228dbcba..2ea0406b0 100644 --- a/builder/amazon/common/run_config_test.go +++ b/builder/amazon/common/run_config_test.go @@ -24,7 +24,9 @@ func testConfig() *RunConfig { InstanceType: "m1.small", Comm: communicator.Config{ - SSHUsername: "foo", + SSH: communicator.SSH{ + SSHUsername: "foo", + }, }, } } diff --git a/builder/amazon/common/step_run_spot_instance_test.go b/builder/amazon/common/step_run_spot_instance_test.go index 0139e0b5a..b5a37292e 100644 --- a/builder/amazon/common/step_run_spot_instance_test.go +++ b/builder/amazon/common/step_run_spot_instance_test.go @@ -93,7 +93,9 @@ func getBasicStep() *StepRunSpotInstance { BlockDurationMinutes: 0, Debug: false, Comm: &communicator.Config{ - SSHKeyPairName: "foo", + SSH: communicator.SSH{ + SSHKeyPairName: "foo", + }, }, EbsOptimized: false, ExpectedRootDevice: "ebs", diff --git a/builder/openstack/run_config_test.go b/builder/openstack/run_config_test.go index fd535496f..bf08f209c 100644 --- a/builder/openstack/run_config_test.go +++ b/builder/openstack/run_config_test.go @@ -23,7 +23,9 @@ func testRunConfig() *RunConfig { Flavor: "m1.small", Comm: communicator.Config{ - SSHUsername: "foo", + SSH: communicator.SSH{ + SSHUsername: "foo", + }, }, } } diff --git a/builder/parallels/common/ssh_config_test.go b/builder/parallels/common/ssh_config_test.go index 7a96920bc..7e0b2ad36 100644 --- a/builder/parallels/common/ssh_config_test.go +++ b/builder/parallels/common/ssh_config_test.go @@ -11,7 +11,9 @@ import ( func testSSHConfig() *SSHConfig { return &SSHConfig{ Comm: communicator.Config{ - SSHUsername: "foo", + SSH: communicator.SSH{ + SSHUsername: "foo", + }, }, } } diff --git a/builder/tencentcloud/cvm/run_config_test.go b/builder/tencentcloud/cvm/run_config_test.go index 9f2dc5b35..d6ffb889b 100644 --- a/builder/tencentcloud/cvm/run_config_test.go +++ b/builder/tencentcloud/cvm/run_config_test.go @@ -13,7 +13,9 @@ func testConfig() *TencentCloudRunConfig { SourceImageId: "img-qwer1234", InstanceType: "S3.SMALL2", Comm: communicator.Config{ - SSHUsername: "tencentcloud", + SSH: communicator.SSH{ + SSHUsername: "tencentcloud", + }, }, } } diff --git a/builder/virtualbox/common/ssh_config_test.go b/builder/virtualbox/common/ssh_config_test.go index 1c89c8770..9231685bb 100644 --- a/builder/virtualbox/common/ssh_config_test.go +++ b/builder/virtualbox/common/ssh_config_test.go @@ -11,7 +11,9 @@ import ( func testSSHConfig() *SSHConfig { return &SSHConfig{ Comm: communicator.Config{ - SSHUsername: "foo", + SSH: communicator.SSH{ + SSHUsername: "foo", + }, }, } } diff --git a/builder/vmware/common/ssh_config_test.go b/builder/vmware/common/ssh_config_test.go index 7a96920bc..7e0b2ad36 100644 --- a/builder/vmware/common/ssh_config_test.go +++ b/builder/vmware/common/ssh_config_test.go @@ -11,7 +11,9 @@ import ( func testSSHConfig() *SSHConfig { return &SSHConfig{ Comm: communicator.Config{ - SSHUsername: "foo", + SSH: communicator.SSH{ + SSHUsername: "foo", + }, }, } } diff --git a/helper/communicator/config.go b/helper/communicator/config.go index 5488beccd..9d7486780 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -1,3 +1,5 @@ +//go:generate struct-markdown + package communicator import ( @@ -21,54 +23,180 @@ import ( // Config is the common configuration that communicators allow within // a builder. type Config struct { + // Packer currently supports three kinds of communicators: + // + // - `none` - No communicator will be used. If this is set, most + // provisioners also can't be used. + // + // - `ssh` - An SSH connection will be established to the machine. This + // is usually the default. + // + // - `winrm` - A WinRM connection will be established. + // + // In addition to the above, some builders have custom communicators they + // can use. For example, the Docker builder has a "docker" communicator + // that uses `docker exec` and `docker cp` to execute scripts and copy + // files. Type string `mapstructure:"communicator"` + // We recommend that you enable SSH or WinRM as the very last step in your + // guest's bootstrap script, but sometimes you may have a race condition where + // you need Packer to wait before attempting to connect to your guest. + // + // If you end up in this situation, you can use the template option + // `pause_before_connecting`. By default, there is no pause. For example: + // + // ```json + // { + // "communicator": "ssh", + // "ssh_username": "myuser", + // "pause_before_connecting": "10m" + // } + // ``` + // + // In this example, Packer will check whether it can connect, as normal. But once + // a connection attempt is successful, it will disconnect and then wait 10 minutes + // before connecting to the guest and beginning provisioning. + PauseBeforeConnect time.Duration `mapstructure:"pause_before_connecting"` + + SSH `mapstructure:",squash"` + WinRM `mapstructure:",squash"` +} + +type SSH struct { // SSH - SSHHost string `mapstructure:"ssh_host"` - SSHPort int `mapstructure:"ssh_port"` - SSHUsername string `mapstructure:"ssh_username"` - SSHPassword string `mapstructure:"ssh_password"` - SSHKeyPairName string `mapstructure:"ssh_keypair_name"` - SSHTemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"` - SSHClearAuthorizedKeys bool `mapstructure:"ssh_clear_authorized_keys"` - SSHPrivateKeyFile string `mapstructure:"ssh_private_key_file"` - SSHInterface string `mapstructure:"ssh_interface"` - SSHIPVersion string `mapstructure:"ssh_ip_version"` - SSHPty bool `mapstructure:"ssh_pty"` - SSHTimeout time.Duration `mapstructure:"ssh_timeout"` - SSHAgentAuth bool `mapstructure:"ssh_agent_auth"` - SSHDisableAgentForwarding bool `mapstructure:"ssh_disable_agent_forwarding"` - SSHHandshakeAttempts int `mapstructure:"ssh_handshake_attempts"` - SSHBastionHost string `mapstructure:"ssh_bastion_host"` - SSHBastionPort int `mapstructure:"ssh_bastion_port"` - SSHBastionAgentAuth bool `mapstructure:"ssh_bastion_agent_auth"` - SSHBastionUsername string `mapstructure:"ssh_bastion_username"` - SSHBastionPassword string `mapstructure:"ssh_bastion_password"` - SSHBastionPrivateKeyFile string `mapstructure:"ssh_bastion_private_key_file"` - SSHFileTransferMethod string `mapstructure:"ssh_file_transfer_method"` - SSHProxyHost string `mapstructure:"ssh_proxy_host"` - SSHProxyPort int `mapstructure:"ssh_proxy_port"` - SSHProxyUsername string `mapstructure:"ssh_proxy_username"` - SSHProxyPassword string `mapstructure:"ssh_proxy_password"` - SSHKeepAliveInterval time.Duration `mapstructure:"ssh_keep_alive_interval"` - SSHReadWriteTimeout time.Duration `mapstructure:"ssh_read_write_timeout"` + + // The address to SSH to. This usually is automatically configured by the + // builder. + SSHHost string `mapstructure:"ssh_host"` + // The port to connect to SSH. This defaults to `22`. + SSHPort int `mapstructure:"ssh_port"` + // The username to connect to SSH with. Required if using SSH. + SSHUsername string `mapstructure:"ssh_username"` + // A plaintext password to use to authenticate with SSH. + SSHPassword string `mapstructure:"ssh_password"` + // If specified, this is the key that will be used for SSH with the + // machine. The key must match a key pair name loaded up into Amazon EC2. + // By default, this is blank, and Packer will generate a temporary keypair + // unless [`ssh_password`](../templates/communicator.html#ssh_password) is + // used. + // [`ssh_private_key_file`](../templates/communicator.html#ssh_private_key_file) + // or `ssh_agent_auth` must be specified when `ssh_keypair_name` is + // utilized. + SSHKeyPairName string `mapstructure:"ssh_keypair_name"` + SSHTemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"` + // If true, Packer will attempt to remove its temporary key from + // `~/.ssh/authorized_keys` and `/root/.ssh/authorized_keys`. This is a + // mostly cosmetic option, since Packer will delete the temporary private + // key from the host system regardless of whether this is set to true + // (unless the user has set the `-debug` flag). Defaults to "false"; + // currently only works on guests with `sed` installed. + SSHClearAuthorizedKeys bool `mapstructure:"ssh_clear_authorized_keys"` + // Path to a PEM encoded private key file to use to authenticate with SSH. + // The `~` can be used in path and will be expanded to the home directory + // of current user. + SSHPrivateKeyFile string `mapstructure:"ssh_private_key_file"` + // One of `public_ip`, `private_ip`, `public_dns`, or `private_dns`. If + // set, either the public IP address, private IP address, public DNS name + // or private DNS name will used as the host for SSH. The default behaviour + // if inside a VPC is to use the public IP address if available, otherwise + // the private IP address will be used. If not in a VPC the public DNS name + // will be used. Also works for WinRM. + // + // Where Packer is configured for an outbound proxy but WinRM traffic + // should be direct, `ssh_interface` must be set to `private_dns` and + // `.compute.internal` included in the `NO_PROXY` environment + // variable. + SSHInterface string `mapstructure:"ssh_interface"` + SSHIPVersion string `mapstructure:"ssh_ip_version"` + // If `true`, a PTY will be requested for the SSH connection. This defaults + // to `false`. + SSHPty bool `mapstructure:"ssh_pty"` + // The time to wait for SSH to become available. Packer uses this to + // determine when the machine has booted so this is usually quite long. + // Example value: `10m`. + SSHTimeout time.Duration `mapstructure:"ssh_timeout"` + // If true, the local SSH agent will be used to authenticate connections to + // the source instance. No temporary keypair will be created, and the + // values of `ssh_password` and `ssh_private_key_file` will be ignored. To + // use this option with a key pair already configured in the source AMI, + // leave the `ssh_keypair_name` blank. To associate an existing key pair in + // AWS with the source instance, set the `ssh_keypair_name` field to the + // name of the key pair. + SSHAgentAuth bool `mapstructure:"ssh_agent_auth"` + // If true, SSH agent forwarding will be disabled. Defaults to `false`. + SSHDisableAgentForwarding bool `mapstructure:"ssh_disable_agent_forwarding"` + // The number of handshakes to attempt with SSH once it can connect. This + // defaults to `10`. + SSHHandshakeAttempts int `mapstructure:"ssh_handshake_attempts"` + // A bastion host to use for the actual SSH connection. + SSHBastionHost string `mapstructure:"ssh_bastion_host"` + // The port of the bastion host. Defaults to `22`. + SSHBastionPort int `mapstructure:"ssh_bastion_port"` + // If `true`, the local SSH agent will be used to authenticate with the + // bastion host. Defaults to `false`. + SSHBastionAgentAuth bool `mapstructure:"ssh_bastion_agent_auth"` + // The username to connect to the bastion host. + SSHBastionUsername string `mapstructure:"ssh_bastion_username"` + // The password to use to authenticate with the bastion host. + SSHBastionPassword string `mapstructure:"ssh_bastion_password"` + // Path to a PEM encoded private key file to use to authenticate with the + // bastion host. The `~` can be used in path and will be expanded to the + // home directory of current user. + SSHBastionPrivateKeyFile string `mapstructure:"ssh_bastion_private_key_file"` + // `scp` or `sftp` - How to transfer files, Secure copy (default) or SSH + // File Transfer Protocol. + SSHFileTransferMethod string `mapstructure:"ssh_file_transfer_method"` + // A SOCKS proxy host to use for SSH connection + SSHProxyHost string `mapstructure:"ssh_proxy_host"` + // A port of the SOCKS proxy. Defaults to `1080`. + SSHProxyPort int `mapstructure:"ssh_proxy_port"` + // The optional username to authenticate with the proxy server. + SSHProxyUsername string `mapstructure:"ssh_proxy_username"` + // The optional password to use to authenticate with the proxy server. + SSHProxyPassword string `mapstructure:"ssh_proxy_password"` + // How often to send "keep alive" messages to the server. Set to a negative + // value (`-1s`) to disable. Example value: `10s`. Defaults to `5s`. + SSHKeepAliveInterval time.Duration `mapstructure:"ssh_keep_alive_interval"` + // The amount of time to wait for a remote command to end. This might be + // useful if, for example, packer hangs on a connection after a reboot. + // Example: `5m`. Disabled by default. + SSHReadWriteTimeout time.Duration `mapstructure:"ssh_read_write_timeout"` + // SSH Internals SSHPublicKey []byte SSHPrivateKey []byte +} - // WinRM - WinRMUser string `mapstructure:"winrm_username"` - WinRMPassword string `mapstructure:"winrm_password"` - WinRMHost string `mapstructure:"winrm_host"` - WinRMPort int `mapstructure:"winrm_port"` - WinRMTimeout time.Duration `mapstructure:"winrm_timeout"` - WinRMUseSSL bool `mapstructure:"winrm_use_ssl"` - WinRMInsecure bool `mapstructure:"winrm_insecure"` - WinRMUseNTLM bool `mapstructure:"winrm_use_ntlm"` +type WinRM struct { + // The username to use to connect to WinRM. + WinRMUser string `mapstructure:"winrm_username"` + // The password to use to connect to WinRM. + WinRMPassword string `mapstructure:"winrm_password"` + // The address for WinRM to connect to. + // + // NOTE: If using an Amazon EBS builder, you can specify the interface + // WinRM connects to via + // [`ssh_interface`](https://www.packer.io/docs/builders/amazon-ebs.html#ssh_interface) + WinRMHost string `mapstructure:"winrm_host"` + // The WinRM port to connect to. This defaults to `5985` for plain + // unencrypted connection and `5986` for SSL when `winrm_use_ssl` is set to + // true. + WinRMPort int `mapstructure:"winrm_port"` + // The amount of time to wait for WinRM to become available. This defaults + // to `30m` since setting up a Windows machine generally takes a long time. + WinRMTimeout time.Duration `mapstructure:"winrm_timeout"` + // If `true`, use HTTPS for WinRM. + WinRMUseSSL bool `mapstructure:"winrm_use_ssl"` + // If `true`, do not check server certificate chain and host name. + WinRMInsecure bool `mapstructure:"winrm_insecure"` + // If `true`, NTLMv2 authentication (with session security) will be used + // for WinRM, rather than default (basic authentication), removing the + // requirement for basic authentication to be enabled within the target + // guest. Further reading for remote connection authentication can be found + // [here](https://msdn.microsoft.com/en-us/library/aa384295(v=vs.85).aspx). + WinRMUseNTLM bool `mapstructure:"winrm_use_ntlm"` WinRMTransportDecorator func() winrm.Transporter - - // Delay - PauseBeforeConnect time.Duration `mapstructure:"pause_before_connecting"` } // ReadSSHPrivateKeyFile returns the SSH private key bytes diff --git a/helper/communicator/config_test.go b/helper/communicator/config_test.go index c5af24114..688f56c55 100644 --- a/helper/communicator/config_test.go +++ b/helper/communicator/config_test.go @@ -10,7 +10,9 @@ import ( func testConfig() *Config { return &Config{ - SSHUsername: "root", + SSH: SSH{ + SSHUsername: "root", + }, } } @@ -41,8 +43,10 @@ func TestConfig_badtype(t *testing.T) { func TestConfig_winrm_noport(t *testing.T) { c := &Config{ - Type: "winrm", - WinRMUser: "admin", + Type: "winrm", + WinRM: WinRM{ + WinRMUser: "admin", + }, } if err := c.Prepare(testContext(t)); len(err) > 0 { t.Fatalf("bad: %#v", err) @@ -56,9 +60,11 @@ func TestConfig_winrm_noport(t *testing.T) { func TestConfig_winrm_noport_ssl(t *testing.T) { c := &Config{ - Type: "winrm", - WinRMUser: "admin", - WinRMUseSSL: true, + Type: "winrm", + WinRM: WinRM{ + WinRMUser: "admin", + WinRMUseSSL: true, + }, } if err := c.Prepare(testContext(t)); len(err) > 0 { t.Fatalf("bad: %#v", err) @@ -72,9 +78,11 @@ func TestConfig_winrm_noport_ssl(t *testing.T) { func TestConfig_winrm_port(t *testing.T) { c := &Config{ - Type: "winrm", - WinRMUser: "admin", - WinRMPort: 5509, + Type: "winrm", + WinRM: WinRM{ + WinRMUser: "admin", + WinRMPort: 5509, + }, } if err := c.Prepare(testContext(t)); len(err) > 0 { t.Fatalf("bad: %#v", err) @@ -88,10 +96,12 @@ func TestConfig_winrm_port(t *testing.T) { func TestConfig_winrm_port_ssl(t *testing.T) { c := &Config{ - Type: "winrm", - WinRMUser: "admin", - WinRMPort: 5510, - WinRMUseSSL: true, + Type: "winrm", + WinRM: WinRM{ + WinRMUser: "admin", + WinRMPort: 5510, + WinRMUseSSL: true, + }, } if err := c.Prepare(testContext(t)); len(err) > 0 { t.Fatalf("bad: %#v", err) @@ -105,9 +115,11 @@ func TestConfig_winrm_port_ssl(t *testing.T) { func TestConfig_winrm_use_ntlm(t *testing.T) { c := &Config{ - Type: "winrm", - WinRMUser: "admin", - WinRMUseNTLM: true, + Type: "winrm", + WinRM: WinRM{ + WinRMUser: "admin", + WinRMUseNTLM: true, + }, } if err := c.Prepare(testContext(t)); len(err) > 0 { t.Fatalf("bad: %#v", err) @@ -128,8 +140,10 @@ func TestConfig_winrm_use_ntlm(t *testing.T) { func TestConfig_winrm(t *testing.T) { c := &Config{ - Type: "winrm", - WinRMUser: "admin", + Type: "winrm", + WinRM: WinRM{ + WinRMUser: "admin", + }, } if err := c.Prepare(testContext(t)); len(err) > 0 { t.Fatalf("bad: %#v", err)