From ad2e5f1f08c97058d135cb19c0ee484fc6c29399 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 8 Feb 2018 12:52:44 -0800 Subject: [PATCH 1/3] fail in oracle classic builder if user tries winrm since it doesn't work yet, and add attributes and attributes_file fields to oracle builder --- builder/oracle/classic/config.go | 16 ++++++++++ .../oracle/classic/step_create_instance.go | 30 +++++++++++++++++++ builder/oracle/classic/step_list_images.go | 30 ++++++++++++++++--- .../docs/builders/oracle-classic.html.md | 13 +++++++- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 4cd7c42a7..52a08cbe9 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -3,6 +3,7 @@ package classic import ( "fmt" "net/url" + "os" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -27,6 +28,9 @@ type Config struct { Shape string `mapstructure:"shape"` SourceImageList string `mapstructure:"source_image_list"` DestImageList string `mapstructure:"dest_image_list"` + // Attributes and Atributes file are both optional and mutually exclusive. + Attributes string `mapstructure:"attributes"` + AttributesFile string `mapstructure:"attributes_file"` // Optional; if you don't enter anything, the image list description // will read "Packer-built image list" DestImageListDescription string `mapstructure:"image_description"` @@ -81,9 +85,21 @@ func NewConfig(raws ...interface{}) (*Config, error) { } } + if c.Attributes != "" && c.AttributesFile != "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Only one of user_data or user_data_file can be specified.")) + } else if c.AttributesFile != "" { + if _, err := os.Stat(c.AttributesFile); err != nil { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("attributes_file not found: %s", c.AttributesFile)) + } + } + if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { errs = packer.MultiErrorAppend(errs, es...) } + if c.Comm.Type == "winrm" { + err = fmt.Errorf("winRM is not supported with the oracle-classic builder yet.") + errs = packer.MultiErrorAppend(errs, err) + } if errs != nil && len(errs.Errors) > 0 { return nil, errs diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 041889ab1..cf4b043fe 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -2,7 +2,9 @@ package classic import ( "context" + "encoding/json" "fmt" + "io/ioutil" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/helper/multistep" @@ -30,6 +32,33 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu // get instances client instanceClient := client.Instances() + var data map[string]interface{} + + if config.Attributes != "" { + err := json.Unmarshal([]byte(config.Attributes), &data) + if err != nil { + err = fmt.Errorf("Problem parsing json from attributes: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + } else if config.AttributesFile != "" { + fidata, err := ioutil.ReadFile(config.AttributesFile) + if err != nil { + err = fmt.Errorf("Problem reading attributes_file: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + err = json.Unmarshal(fidata, &data) + if err != nil { + err = fmt.Errorf("Problem parsing json from attrinutes_file: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + } + // Instances Input input := &compute.CreateInstanceInput{ Name: config.ImageName, @@ -37,6 +66,7 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu ImageList: config.SourceImageList, SSHKeys: []string{keyName}, Networking: map[string]compute.NetworkingInfo{"eth0": netInfo}, + Attributes: data, } instanceInfo, err := instanceClient.CreateInstance(input) diff --git a/builder/oracle/classic/step_list_images.go b/builder/oracle/classic/step_list_images.go index a99612c14..247b96146 100644 --- a/builder/oracle/classic/step_list_images.go +++ b/builder/oracle/classic/step_list_images.go @@ -11,11 +11,10 @@ import ( type stepListImages struct{} -func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - // get variables from state - ui := state.Get("ui").(packer.Ui) - config := state.Get("config").(*Config) +func (s *stepListImages) listForSSH(state multistep.StateBag) multistep.StepAction { client := state.Get("client").(*compute.ComputeClient) + config := state.Get("config").(*Config) + ui := state.Get("ui").(packer.Ui) ui.Say("Adding image to image list...") imageListClient := client.ImageList() @@ -98,6 +97,29 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis return multistep.ActionContinue } +func (s *stepListImages) listForWinRM(state multistep.StateBag) multistep.StepAction { + // This is a placeholder function; we will never reach this because we already + // return an error when winRM is set when validating the Packer config. + ui := state.Get("ui").(packer.Ui) + err := fmt.Errorf("The Oracle Classic builder does not currently support winRM.") + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt +} + +func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + // get variables from state + config := state.Get("config").(*Config) + + var action multistep.StepAction + if config.Comm.Type == "winrm" { + action = s.listForWinRM(state) + } else if config.Comm.Type == "ssh" { + action = s.listForSSH(state) + } + return action +} + func (s *stepListImages) Cleanup(state multistep.StateBag) { // Nothing to do return diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md index f8991b968..98db5f2aa 100644 --- a/website/source/docs/builders/oracle-classic.html.md +++ b/website/source/docs/builders/oracle-classic.html.md @@ -63,6 +63,16 @@ This builder currently only works with the SSH communicator. ### Optional + - `attributes` (string) - (string) - Attributes to apply when launching the + instance. Note that you need to be careful about escaping characters due to + the templates being JSON. It is often more convenient to use + `attributes_file`, instead. You may only define either `attributes` or + `attributes_file`, not both. + + - `attributes_file` (string) - Path to a json file that will be used for the + attributes when launching the instance. You may only define either + `attributes` or `attributes_file`, not both. + - `image_description` (string) - a description for your destination image list. If you don't provide one, Packer will provide a generic description. @@ -91,7 +101,8 @@ obfuscated; you will need to add a working `username`, `password`, "api_endpoint": "https://api-###.compute.###.oraclecloud.com/", "source_image_list": "/oracle/public/OL_7.2_UEKR4_x86_64", "shape": "oc3", - "image_name": "Packer_Builder_Test_{{timestamp}}" + "image_name": "Packer_Builder_Test_{{timestamp}}", + "attributes": "{\"userdata\": {\"pre-bootstrap\": {\"script\": [\"...\"]}}}", "dest_image_list": "Packer_Builder_Test_List" } ], From ff717c57844bdfb2a934f1a41f7b538596070a2e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 8 Feb 2018 13:21:21 -0800 Subject: [PATCH 2/3] wrong place for differentiation between ssh and winrm --- builder/oracle/classic/step_list_images.go | 30 +++------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/builder/oracle/classic/step_list_images.go b/builder/oracle/classic/step_list_images.go index 247b96146..a99612c14 100644 --- a/builder/oracle/classic/step_list_images.go +++ b/builder/oracle/classic/step_list_images.go @@ -11,10 +11,11 @@ import ( type stepListImages struct{} -func (s *stepListImages) listForSSH(state multistep.StateBag) multistep.StepAction { - client := state.Get("client").(*compute.ComputeClient) - config := state.Get("config").(*Config) +func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { + // get variables from state ui := state.Get("ui").(packer.Ui) + config := state.Get("config").(*Config) + client := state.Get("client").(*compute.ComputeClient) ui.Say("Adding image to image list...") imageListClient := client.ImageList() @@ -97,29 +98,6 @@ func (s *stepListImages) listForSSH(state multistep.StateBag) multistep.StepActi return multistep.ActionContinue } -func (s *stepListImages) listForWinRM(state multistep.StateBag) multistep.StepAction { - // This is a placeholder function; we will never reach this because we already - // return an error when winRM is set when validating the Packer config. - ui := state.Get("ui").(packer.Ui) - err := fmt.Errorf("The Oracle Classic builder does not currently support winRM.") - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt -} - -func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { - // get variables from state - config := state.Get("config").(*Config) - - var action multistep.StepAction - if config.Comm.Type == "winrm" { - action = s.listForWinRM(state) - } else if config.Comm.Type == "ssh" { - action = s.listForSSH(state) - } - return action -} - func (s *stepListImages) Cleanup(state multistep.StateBag) { // Nothing to do return From 7f631fcb772616af9d8da80d66f4a4133acdaa7f Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 8 Feb 2018 14:12:39 -0800 Subject: [PATCH 3/3] unpack attributes in oracle-classic builder earlier so that we error fast if there's an issue --- builder/oracle/classic/config.go | 28 +++++++++++++++++ .../oracle/classic/step_create_instance.go | 31 +------------------ 2 files changed, 29 insertions(+), 30 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 52a08cbe9..233a3a0d1 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -1,7 +1,9 @@ package classic import ( + "encoding/json" "fmt" + "io/ioutil" "net/url" "os" @@ -15,6 +17,7 @@ import ( type Config struct { common.PackerConfig `mapstructure:",squash"` Comm communicator.Config `mapstructure:",squash"` + attribs map[string]interface{} // Access config overrides Username string `mapstructure:"username"` @@ -105,5 +108,30 @@ func NewConfig(raws ...interface{}) (*Config, error) { return nil, errs } + // unpack attributes from json into config + var data map[string]interface{} + + if c.Attributes != "" { + err := json.Unmarshal([]byte(c.Attributes), &data) + if err != nil { + err = fmt.Errorf("Problem parsing json from attributes: %s", err) + packer.MultiErrorAppend(errs, err) + } + c.attribs = data + } else if c.AttributesFile != "" { + fidata, err := ioutil.ReadFile(c.AttributesFile) + if err != nil { + err = fmt.Errorf("Problem reading attributes_file: %s", err) + packer.MultiErrorAppend(errs, err) + } + err = json.Unmarshal(fidata, &data) + c.attribs = data + if err != nil { + err = fmt.Errorf("Problem parsing json from attrinutes_file: %s", err) + packer.MultiErrorAppend(errs, err) + } + c.attribs = data + } + return c, nil } diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index cf4b043fe..cda67122c 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -2,9 +2,7 @@ package classic import ( "context" - "encoding/json" "fmt" - "io/ioutil" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/helper/multistep" @@ -32,33 +30,6 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu // get instances client instanceClient := client.Instances() - var data map[string]interface{} - - if config.Attributes != "" { - err := json.Unmarshal([]byte(config.Attributes), &data) - if err != nil { - err = fmt.Errorf("Problem parsing json from attributes: %s", err) - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt - } - } else if config.AttributesFile != "" { - fidata, err := ioutil.ReadFile(config.AttributesFile) - if err != nil { - err = fmt.Errorf("Problem reading attributes_file: %s", err) - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt - } - err = json.Unmarshal(fidata, &data) - if err != nil { - err = fmt.Errorf("Problem parsing json from attrinutes_file: %s", err) - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt - } - } - // Instances Input input := &compute.CreateInstanceInput{ Name: config.ImageName, @@ -66,7 +37,7 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu ImageList: config.SourceImageList, SSHKeys: []string{keyName}, Networking: map[string]compute.NetworkingInfo{"eth0": netInfo}, - Attributes: data, + Attributes: config.attribs, } instanceInfo, err := instanceClient.CreateInstance(input)