diff --git a/builder/googlecompute/config.go b/builder/googlecompute/config.go index df55d99d2..7c78a93cd 100644 --- a/builder/googlecompute/config.go +++ b/builder/googlecompute/config.go @@ -62,6 +62,7 @@ type Config struct { Subnetwork string `mapstructure:"subnetwork"` Tags []string `mapstructure:"tags"` UseInternalIP bool `mapstructure:"use_internal_ip"` + MetadataFiles map[string]string `mapstructure:"metadata_files"` Zone string `mapstructure:"zone"` Account AccountFile diff --git a/builder/googlecompute/config_test.go b/builder/googlecompute/config_test.go index 4db82040b..f562d4b8f 100644 --- a/builder/googlecompute/config_test.go +++ b/builder/googlecompute/config_test.go @@ -431,7 +431,8 @@ func testConfig(t *testing.T) (config map[string]interface{}, tempAccountFile st "image_licenses": []string{ "test-license", }, - "zone": "us-east1-a", + "metadata_files": map[string]string{}, + "zone": "us-east1-a", } return config, tempAccountFile @@ -484,6 +485,21 @@ func testAccountFile(t *testing.T) string { return tf.Name() } +const testMetadataFileContent = `testMetadata` + +func testMetadataFile(t *testing.T) string { + tf, err := ioutil.TempFile("", "packer") + if err != nil { + t.Fatalf("err: %s", err) + } + defer tf.Close() + if _, err := tf.Write([]byte(testMetadataFileContent)); err != nil { + t.Fatalf("err: %s", err) + } + + return tf.Name() +} + // This is just some dummy data that doesn't actually work (it was revoked // a long time ago). const testAccountContent = `{}` diff --git a/builder/googlecompute/step_create_instance.go b/builder/googlecompute/step_create_instance.go index 5ed854670..6004c2fab 100644 --- a/builder/googlecompute/step_create_instance.go +++ b/builder/googlecompute/step_create_instance.go @@ -19,6 +19,7 @@ type StepCreateInstance struct { func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string) (map[string]string, error) { instanceMetadata := make(map[string]string) var err error + var errs *packer.MultiError // Copy metadata from config. for k, v := range c.Metadata { @@ -41,10 +42,24 @@ func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string) if c.StartupScriptFile != "" { var content []byte content, err = ioutil.ReadFile(c.StartupScriptFile) + if err != nil { + return nil, err + } instanceMetadata[StartupWrappedScriptKey] = string(content) } else if wrappedStartupScript, exists := instanceMetadata[StartupScriptKey]; exists { instanceMetadata[StartupWrappedScriptKey] = wrappedStartupScript } + + // Read metadata from files specified with metadata_files + for key, value := range c.MetadataFiles { + var content []byte + content, err = ioutil.ReadFile(value) + if err != nil { + errs = packer.MultiErrorAppend(errs, err) + } + instanceMetadata[key] = string(content) + } + if sourceImage.IsWindows() { // Windows startup script support is not yet implemented. // Mark the startup script as done. @@ -55,7 +70,10 @@ func (c *Config) createInstanceMetadata(sourceImage *Image, sshPublicKey string) instanceMetadata[StartupScriptStatusKey] = StartupScriptStatusNotDone } - return instanceMetadata, err + if errs != nil && len(errs.Errors) > 0 { + return instanceMetadata, errs + } + return instanceMetadata, nil } func getImage(c *Config, d Driver) (*Image, error) { @@ -98,7 +116,13 @@ func (s *StepCreateInstance) Run(ctx context.Context, state multistep.StateBag) var errCh <-chan error var metadata map[string]string - metadata, err = c.createInstanceMetadata(sourceImage, string(c.Comm.SSHPublicKey)) + metadata, errs := c.createInstanceMetadata(sourceImage, string(c.Comm.SSHPublicKey)) + if errs != nil { + state.Put("error", errs.Error()) + ui.Error(errs.Error()) + return multistep.ActionHalt + } + errCh, err = d.RunInstance(&InstanceConfig{ AcceleratorType: c.AcceleratorType, AcceleratorCount: c.AcceleratorCount, diff --git a/builder/googlecompute/step_create_instance_test.go b/builder/googlecompute/step_create_instance_test.go index e56159138..33f0371fb 100644 --- a/builder/googlecompute/step_create_instance_test.go +++ b/builder/googlecompute/step_create_instance_test.go @@ -325,3 +325,20 @@ func TestCreateInstanceMetadata_noPublicKey(t *testing.T) { // ensure the ssh metadata hasn't changed assert.Equal(t, metadata["sshKeys"], sshKeys, "Instance metadata should not have been modified") } + +func TestCreateInstanceMetadata_metadataFile(t *testing.T) { + state := testState(t) + c := state.Get("config").(*Config) + image := StubImage("test-image", "test-project", []string{}, 100) + content := testMetadataFileContent + fileName := testMetadataFile(t) + c.MetadataFiles["user-data"] = fileName + + // create our metadata + metadata, err := c.createInstanceMetadata(image, "") + + assert.True(t, err == nil, "Metadata creation should have succeeded.") + + // ensure the user-data key in metadata is updated with file content + assert.Equal(t, metadata["user-data"], content, "user-data field of the instance metadata should have been updated.") +}