diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go index f0610c989..3d4263433 100644 --- a/builder/openstack/builder.go +++ b/builder/openstack/builder.go @@ -75,10 +75,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Flavor: b.config.Flavor, }, &StepKeyPair{ - Debug: b.config.PackerDebug, - DebugKeyPath: fmt.Sprintf("os_%s.pem", b.config.PackerBuildName), - KeyPairName: b.config.SSHKeyPairName, - PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey, + Debug: b.config.PackerDebug, + DebugKeyPath: fmt.Sprintf("os_%s.pem", b.config.PackerBuildName), + KeyPairName: b.config.SSHKeyPairName, + TemporaryKeyPairName: b.config.TemporaryKeyPairName, + PrivateKeyFile: b.config.RunConfig.Comm.SSHPrivateKey, + SSHAgentAuth: b.config.RunConfig.Comm.SSHAgentAuth, }, &StepRunSourceServer{ Name: b.config.ImageName, diff --git a/builder/openstack/run_config.go b/builder/openstack/run_config.go index f0bb4dc7c..06fd0ea73 100644 --- a/builder/openstack/run_config.go +++ b/builder/openstack/run_config.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" + "github.com/mitchellh/packer/common/uuid" "github.com/mitchellh/packer/helper/communicator" "github.com/mitchellh/packer/template/interpolate" ) @@ -11,10 +12,11 @@ import ( // RunConfig contains configuration for running an instance from a source // image and details on how to access that launched image. type RunConfig struct { - Comm communicator.Config `mapstructure:",squash"` - SSHKeyPairName string `mapstructure:"ssh_keypair_name"` - SSHInterface string `mapstructure:"ssh_interface"` - SSHIPVersion string `mapstructure:"ssh_ip_version"` + Comm communicator.Config `mapstructure:",squash"` + SSHKeyPairName string `mapstructure:"ssh_keypair_name"` + TemporaryKeyPairName string `mapstructure:"temporary_key_pair_name"` + SSHInterface string `mapstructure:"ssh_interface"` + SSHIPVersion string `mapstructure:"ssh_ip_version"` SourceImage string `mapstructure:"source_image"` SourceImageName string `mapstructure:"source_image_name"` @@ -38,6 +40,15 @@ type RunConfig struct { } func (c *RunConfig) Prepare(ctx *interpolate.Context) []error { + // If we are not given an explicit ssh_keypair_name or + // ssh_private_key_file, then create a temporary one, but only if the + // temporary_key_pair_name has not been provided and we are not using + // ssh_password. + if c.SSHKeyPairName == "" && c.TemporaryKeyPairName == "" && + c.Comm.SSHPrivateKey == "" && c.Comm.SSHPassword == "" { + + c.TemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID()) + } if c.UseFloatingIp && c.FloatingIpPool == "" { c.FloatingIpPool = "public" diff --git a/builder/openstack/step_key_pair.go b/builder/openstack/step_key_pair.go index 94ca5c768..b28994030 100644 --- a/builder/openstack/step_key_pair.go +++ b/builder/openstack/step_key_pair.go @@ -10,21 +10,24 @@ import ( "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs" "github.com/mitchellh/multistep" - "github.com/mitchellh/packer/common/uuid" "github.com/mitchellh/packer/packer" "golang.org/x/crypto/ssh" ) type StepKeyPair struct { - Debug bool - DebugKeyPath string - KeyPairName string - PrivateKeyFile string + Debug bool + SSHAgentAuth bool + DebugKeyPath string + TemporaryKeyPairName string + KeyPairName string + PrivateKeyFile string keyName string } func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + if s.PrivateKeyFile != "" { privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile) if err != nil { @@ -39,14 +42,25 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionContinue } - config := state.Get("config").(Config) - ui := state.Get("ui").(packer.Ui) + if s.SSHAgentAuth && s.KeyPairName == "" { + ui.Say("Using SSH Agent with key pair in Source image") + return multistep.ActionContinue + } - if config.Comm.Type == "ssh" && config.Comm.SSHPassword != "" { - ui.Say("Not creating temporary keypair when using password.") + if s.SSHAgentAuth && s.KeyPairName != "" { + ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.KeyPairName)) + state.Put("keyPair", s.KeyPairName) return multistep.ActionContinue } + if s.TemporaryKeyPairName == "" { + ui.Say("Not using temporary keypair") + state.Put("keyPair", "") + return multistep.ActionContinue + } + + config := state.Get("config").(Config) + // We need the v2 compute client computeClient, err := config.computeV2Client() if err != nil { @@ -55,10 +69,9 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - keyName := fmt.Sprintf("packer %s", uuid.TimeOrderedUUID()) - ui.Say(fmt.Sprintf("Creating temporary keypair: %s ...", keyName)) + ui.Say(fmt.Sprintf("Creating temporary keypair: %s ...", s.TemporaryKeyPairName)) keypair, err := keypairs.Create(computeClient, keypairs.CreateOpts{ - Name: keyName, + Name: s.TemporaryKeyPairName, }).Extract() if err != nil { state.Put("error", fmt.Errorf("Error creating temporary keypair: %s", err)) @@ -70,7 +83,7 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } - ui.Say(fmt.Sprintf("Created temporary keypair: %s", keyName)) + ui.Say(fmt.Sprintf("Created temporary keypair: %s", s.TemporaryKeyPairName)) keypair.PrivateKey = berToDer(keypair.PrivateKey, ui) @@ -101,10 +114,10 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { } // Set the keyname so we know to delete it later - s.keyName = keyName + s.keyName = s.TemporaryKeyPairName // Set some state data for use in future steps - state.Put("keyPair", keyName) + state.Put("keyPair", s.keyName) state.Put("privateKey", keypair.PrivateKey) return multistep.ActionContinue @@ -156,11 +169,11 @@ func berToDer(ber string, ui packer.Ui) string { func (s *StepKeyPair) Cleanup(state multistep.StateBag) { // If we used an SSH private key file, do not go about deleting // keypairs - if s.PrivateKeyFile != "" { + if s.PrivateKeyFile != "" || (s.KeyPairName == "" && s.keyName == "") { return } // If no key name is set, then we never created it, so just return - if s.keyName == "" { + if s.TemporaryKeyPairName == "" { return } @@ -171,14 +184,14 @@ func (s *StepKeyPair) Cleanup(state multistep.StateBag) { computeClient, err := config.computeV2Client() if err != nil { ui.Error(fmt.Sprintf( - "Error cleaning up keypair. Please delete the key manually: %s", s.keyName)) + "Error cleaning up keypair. Please delete the key manually: %s", s.TemporaryKeyPairName)) return } - ui.Say(fmt.Sprintf("Deleting temporary keypair: %s ...", s.keyName)) + ui.Say(fmt.Sprintf("Deleting temporary keypair: %s ...", s.TemporaryKeyPairName)) err = keypairs.Delete(computeClient, s.keyName).ExtractErr() if err != nil { ui.Error(fmt.Sprintf( - "Error cleaning up keypair. Please delete the key manually: %s", s.keyName)) + "Error cleaning up keypair. Please delete the key manually: %s", s.TemporaryKeyPairName)) } }