@ -5,6 +5,7 @@ import (
"errors"
"fmt"
"io/ioutil"
"log"
"strings"
"time"
@ -17,14 +18,23 @@ type StepCreateInstance struct {
Debug bool
}
func ( c * Config ) createInstanceMetadata ( sourceImage * Image , sshPublicKey string ) ( map [ string ] string , error ) {
instanceMetadata := make ( map [ string ] string )
func ( c * Config ) createInstanceMetadata ( sourceImage * Image , sshPublicKey string ) ( map [ string ] string , map [ string ] string , error ) {
instanceMetadataNoSSHKeys := make ( map [ string ] string )
instanceMetadataSSHKeys := make ( map [ string ] string )
sshMetaKey := "ssh-keys"
var err error
var errs * packersdk . MultiError
// Copy metadata from config.
for k , v := range c . Metadata {
instanceMetadata [ k ] = v
if k == sshMetaKey {
instanceMetadataSSHKeys [ k ] = v
} else {
instanceMetadataNoSSHKeys [ k ] = v
}
}
// Merge any existing ssh keys with our public key, unless there is no
@ -34,40 +44,40 @@ func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string)
sshMetaKey := "ssh-keys"
sshPublicKey = strings . TrimSuffix ( sshPublicKey , "\n" )
sshKeys := fmt . Sprintf ( "%s:%s %s" , c . Comm . SSHUsername , sshPublicKey , c . Comm . SSHUsername )
if confS sh Keys, exists := instanceMetadata [ sshMetaKey ] ; exists {
sshKeys = fmt . Sprintf ( "%s\n%s" , sshKeys , confS sh Keys)
if confS SH Keys, exists := instanceMetadata SSHKeys [ sshMetaKey ] ; exists {
sshKeys = fmt . Sprintf ( "%s\n%s" , sshKeys , confS SH Keys)
}
instanceMetadata [ sshMetaKey ] = sshKeys
instanceMetadata SSHKeys [ sshMetaKey ] = sshKeys
}
startupScript := instanceMetadata [ StartupScriptKey ]
startupScript := instanceMetadata NoSSHKeys [ StartupScriptKey ]
if c . StartupScriptFile != "" {
var content [ ] byte
content , err = ioutil . ReadFile ( c . StartupScriptFile )
if err != nil {
return nil , err
return nil , instanceMetadataNoSSHKeys, err
}
startupScript = string ( content )
}
instanceMetadata [ StartupScriptKey ] = startupScript
instanceMetadata NoSSHKeys [ StartupScriptKey ] = startupScript
// Wrap any found startup script with our own startup script wrapper.
if startupScript != "" && c . WrapStartupScriptFile . True ( ) {
instanceMetadata [ StartupScriptKey ] = StartupScriptLinux
instanceMetadata [ StartupWrappedScriptKey ] = startupScript
instanceMetadata [ StartupScriptStatusKey ] = StartupScriptStatusNotDone
instanceMetadata NoSSHKeys [ StartupScriptKey ] = StartupScriptLinux
instanceMetadata NoSSHKeys [ StartupWrappedScriptKey ] = startupScript
instanceMetadata NoSSHKeys [ StartupScriptStatusKey ] = StartupScriptStatusNotDone
}
if sourceImage . IsWindows ( ) {
// Windows startup script support is not yet implemented so clear any script data and set status to done
instanceMetadata [ StartupScriptKey ] = StartupScriptWindows
instanceMetadata [ StartupScriptStatusKey ] = StartupScriptStatusDone
instanceMetadata NoSSHKeys [ StartupScriptKey ] = StartupScriptWindows
instanceMetadata NoSSHKeys [ StartupScriptStatusKey ] = StartupScriptStatusDone
}
// If UseOSLogin is true, force `enable-oslogin` in metadata
// In the event that `enable-oslogin` is not enabled at project level
if c . UseOSLogin {
instanceMetadata [ EnableOSLoginKey ] = "TRUE"
instanceMetadata NoSSHKeys [ EnableOSLoginKey ] = "TRUE"
}
for key , value := range c . MetadataFiles {
@ -76,13 +86,13 @@ func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string)
if err != nil {
errs = packersdk . MultiErrorAppend ( errs , err )
}
instanceMetadata [ key ] = string ( content )
instanceMetadata NoSSHKeys [ key ] = string ( content )
}
if errs != nil && len ( errs . Errors ) > 0 {
return instanceMetadata , errs
return instanceMetadata NoSSHKeys, instanceMetadataSSHKeys , errs
}
return instanceMetadata , nil
return instanceMetadata NoSSHKeys, instanceMetadataSSHKeys , nil
}
func getImage ( c * Config , d Driver ) ( * Image , error ) {
@ -131,14 +141,28 @@ func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag)
name := c . InstanceName
var errCh <- chan error
var metadata map [ string ] string
metadata , errs := c . createInstanceMetadata ( sourceImage , string ( c . Comm . SSHPublicKey ) )
var metadataNoSSHKeys map [ string ] string
var metadataSSHKeys map [ string ] string
metadataForInstance := make ( map [ string ] string )
metadataNoSSHKeys , metadataSSHKeys , errs := c . createInstanceMetadata ( sourceImage , string ( c . Comm . SSHPublicKey ) )
if errs != nil {
state . Put ( "error" , errs . Error ( ) )
ui . Error ( errs . Error ( ) )
return multistep . ActionHalt
}
if c . WaitToAddSSHKeys > 0 {
log . Printf ( "[DEBUG] Adding metadata during instance creation, but not SSH keys..." )
metadataForInstance = metadataNoSSHKeys
} else {
log . Printf ( "[DEBUG] Adding metadata during instance creation..." )
// Union of both non-SSH key meta data and SSH key meta data
addmap ( metadataForInstance , metadataSSHKeys )
addmap ( metadataForInstance , metadataNoSSHKeys )
}
errCh , err = d . RunInstance ( & InstanceConfig {
AcceleratorType : c . AcceleratorType ,
AcceleratorCount : c . AcceleratorCount ,
@ -153,7 +177,7 @@ func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag)
Image : sourceImage ,
Labels : c . Labels ,
MachineType : c . MachineType ,
Metadata : metadata ,
Metadata : metadata ForInstance ,
MinCpuPlatform : c . MinCpuPlatform ,
Name : name ,
Network : c . Network ,
@ -199,9 +223,40 @@ func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag)
// instance id inside of the provisioners, used in step_provision.
state . Put ( "instance_id" , name )
if c . WaitToAddSSHKeys > 0 {
ui . Message ( fmt . Sprintf ( "Waiting %s before adding SSH keys..." ,
c . WaitToAddSSHKeys . String ( ) ) )
cancelled := s . waitForBoot ( ctx , c . WaitToAddSSHKeys )
if cancelled {
return multistep . ActionHalt
}
log . Printf ( "[DEBUG] %s wait is over. Adding SSH keys to existing instance..." ,
c . WaitToAddSSHKeys . String ( ) )
err = d . AddToInstanceMetadata ( c . Zone , name , metadataSSHKeys )
if err != nil {
err := fmt . Errorf ( "Error adding SSH keys to existing instance: %s" , err )
state . Put ( "error" , err )
ui . Error ( err . Error ( ) )
return multistep . ActionHalt
}
}
return multistep . ActionContinue
}
func ( s * StepCreateInstance ) waitForBoot ( ctx context . Context , waitLen time . Duration ) bool {
// Use a select to determine if we get cancelled during the wait
select {
case <- ctx . Done ( ) :
return true
case <- time . After ( waitLen ) :
}
return false
}
// Cleanup destroys the GCE instance created during the image creation process.
func ( s * StepCreateInstance ) Cleanup ( state multistep . StateBag ) {
nameRaw , ok := state . GetOk ( "instance_name" )
@ -260,3 +315,10 @@ func (s *StepCreateInstance) Cleanup(state multistep.StateBag) {
return
}
func addmap ( a map [ string ] string , b map [ string ] string ) {
for k , v := range b {
a [ k ] = v
}
}