From 543caf3ec575621dd9210a6bd33116193d9da934 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 12 Jan 2018 14:12:15 -0800 Subject: [PATCH 01/61] WIP OCI Classic builder --- builder/oracle/classic/access_config.go | 4 + builder/oracle/classic/artifact.go | 34 ++++++++ builder/oracle/classic/builder.go | 87 +++++++++++++++++++ builder/oracle/classic/config.go | 44 ++++++++++ .../{oci => common}/step_ssh_key_pair.go | 8 +- builder/oracle/oci/builder.go | 3 +- 6 files changed, 175 insertions(+), 5 deletions(-) create mode 100644 builder/oracle/classic/access_config.go create mode 100644 builder/oracle/classic/artifact.go create mode 100644 builder/oracle/classic/builder.go create mode 100644 builder/oracle/classic/config.go rename builder/oracle/{oci => common}/step_ssh_key_pair.go (94%) diff --git a/builder/oracle/classic/access_config.go b/builder/oracle/classic/access_config.go new file mode 100644 index 000000000..99483ceec --- /dev/null +++ b/builder/oracle/classic/access_config.go @@ -0,0 +1,4 @@ +package classic + +type AccessConfig struct { +} diff --git a/builder/oracle/classic/artifact.go b/builder/oracle/classic/artifact.go new file mode 100644 index 000000000..ff01b186f --- /dev/null +++ b/builder/oracle/classic/artifact.go @@ -0,0 +1,34 @@ +package classic + +// Artifact is an artifact implementation that contains a built Custom Image. +type Artifact struct { +} + +// BuilderId uniquely identifies the builder. +func (a *Artifact) BuilderId() string { + return BuilderId +} + +// Files lists the files associated with an artifact. We don't have any files +// as the custom image is stored server side. +func (a *Artifact) Files() []string { + return nil +} + +// Id returns the OCID of the associated Image. +func (a *Artifact) Id() string { + return "" +} + +func (a *Artifact) String() string { + return "" +} + +func (a *Artifact) State(name string) interface{} { + return nil +} + +// Destroy deletes the custom image associated with the artifact. +func (a *Artifact) Destroy() error { + return nil +} diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go new file mode 100644 index 000000000..4b86c449c --- /dev/null +++ b/builder/oracle/classic/builder.go @@ -0,0 +1,87 @@ +package classic + +import ( + "fmt" + "log" + + ocommon "github.com/hashicorp/packer/builder/oracle/common" + "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/helper/communicator" + "github.com/hashicorp/packer/packer" + "github.com/mitchellh/multistep" +) + +// BuilderId uniquely identifies the builder +const BuilderId = "packer.oracle.classic" + +// Builder is a builder implementation that creates Oracle OCI custom images. +type Builder struct { + config *Config + runner multistep.Runner +} + +func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { + config, err := NewConfig(rawConfig...) + if err != nil { + return nil, err + } + b.config = config + + return nil, nil +} + +func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { + // Populate the state bag + state := new(multistep.BasicStateBag) + state.Put("config", b.config) + state.Put("hook", hook) + state.Put("ui", ui) + + // Build the steps + steps := []multistep.Step{ + &ocommon.StepKeyPair{ + Debug: b.config.PackerDebug, + DebugKeyPath: fmt.Sprintf("oci_classic_%s.pem", b.config.PackerBuildName), + PrivateKeyFile: b.config.Comm.SSHPrivateKey, + }, + &stepCreateInstance{}, + &communicator.StepConnect{ + Config: &b.config.Comm, + Host: commHost, + SSHConfig: SSHConfig( + b.config.Comm.SSHUsername, + b.config.Comm.SSHPassword), + }, + &common.StepProvision{}, + &stepSnapshot{}, + } + + // Run the steps + b.runner = common.NewRunner(steps, b.config.PackerConfig, ui) + b.runner.Run(state) + + // If there was an error, return that + if rawErr, ok := state.GetOk("error"); ok { + return nil, rawErr.(error) + } + + /* + // Build the artifact and return it + artifact := &Artifact{ + Image: state.Get("image").(client.Image), + Region: b.config.AccessCfg.Region, + driver: driver, + } + + return artifact, nil + */ + return nil, nil +} + +// Cancel terminates a running build. +func (b *Builder) Cancel() { + if b.runner != nil { + log.Println("Cancelling the step runner...") + b.runner.Cancel() + } +} diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go new file mode 100644 index 000000000..6a3c05fc8 --- /dev/null +++ b/builder/oracle/classic/config.go @@ -0,0 +1,44 @@ +package classic + +import ( + "fmt" + + "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/helper/communicator" + "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/template/interpolate" +) + +type Config struct { + common.PackerConfig `mapstructure:",squash"` + Comm communicator.Config `mapstructure:",squash"` + + Access *AccessConfig + + // Access config overrides + Username string `mapstructure:"username"` + Password string `mapstructure:"password"` + IdentityDomain string `mapstructure:"identity_domain"` + APIEndpoint string `mapstructure:"api_endpoint"` + + // Image + Shape string `mapstructure:"shape"` + ImageList string `json:"image_list"` + + ctx interpolate.Context +} + +func NewConfig(raws ...interface{}) (*Config, error) { + c := &Config{} + + // Decode from template + err := config.Decode(c, &config.DecodeOpts{ + Interpolate: true, + InterpolateContext: &c.ctx, + }, raws...) + if err != nil { + return nil, fmt.Errorf("Failed to mapstructure Config: %+v", err) + } + + return c, nil +} diff --git a/builder/oracle/oci/step_ssh_key_pair.go b/builder/oracle/common/step_ssh_key_pair.go similarity index 94% rename from builder/oracle/oci/step_ssh_key_pair.go rename to builder/oracle/common/step_ssh_key_pair.go index 9fa83c17d..c545a09e2 100644 --- a/builder/oracle/oci/step_ssh_key_pair.go +++ b/builder/oracle/common/step_ssh_key_pair.go @@ -1,4 +1,4 @@ -package oci +package common import ( "context" @@ -16,13 +16,13 @@ import ( "golang.org/x/crypto/ssh" ) -type stepKeyPair struct { +type StepKeyPair struct { Debug bool DebugKeyPath string PrivateKeyFile string } -func (s *stepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { +func (s *StepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) if s.PrivateKeyFile != "" { @@ -112,6 +112,6 @@ func (s *stepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep return multistep.ActionContinue } -func (s *stepKeyPair) Cleanup(state multistep.StateBag) { +func (s *StepKeyPair) Cleanup(state multistep.StateBag) { // Nothing to do } diff --git a/builder/oracle/oci/builder.go b/builder/oracle/oci/builder.go index 6a2f6efef..07d41b756 100644 --- a/builder/oracle/oci/builder.go +++ b/builder/oracle/oci/builder.go @@ -6,6 +6,7 @@ import ( "fmt" "log" + ocommon "github.com/hashicorp/packer/builder/oracle/common" client "github.com/hashicorp/packer/builder/oracle/oci/client" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -50,7 +51,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps steps := []multistep.Step{ - &stepKeyPair{ + &ocommon.StepKeyPair{ Debug: b.config.PackerDebug, DebugKeyPath: fmt.Sprintf("oci_%s.pem", b.config.PackerBuildName), PrivateKeyFile: b.config.Comm.SSHPrivateKey, From 9e8d845c03e7539c18bb8ad3d2ce357cd565382c Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 12 Jan 2018 14:19:13 -0800 Subject: [PATCH 02/61] create instance reservation --- builder/oracle/classic/builder.go | 1 + 1 file changed, 1 insertion(+) diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 4b86c449c..5fe719567 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -44,6 +44,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe DebugKeyPath: fmt.Sprintf("oci_classic_%s.pem", b.config.PackerBuildName), PrivateKeyFile: b.config.Comm.SSHPrivateKey, }, + &stepCreateIPReservation{}, &stepCreateInstance{}, &communicator.StepConnect{ Config: &b.config.Comm, From 3bf431a4238b8c1f38679214acc0f6a5e9881bc1 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 12 Jan 2018 15:44:40 -0800 Subject: [PATCH 03/61] construct OCI client --- builder/oracle/classic/builder.go | 25 +- builder/oracle/classic/config.go | 7 + .../go-oracle-terraform/CHANGELOG.md | 134 +++++++ .../hashicorp/go-oracle-terraform/GNUmakefile | 41 ++ .../hashicorp/go-oracle-terraform/LICENSE | 373 ++++++++++++++++++ .../hashicorp/go-oracle-terraform/README.md | 108 +++++ vendor/vendor.json | 6 + 7 files changed, 692 insertions(+), 2 deletions(-) create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/CHANGELOG.md create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/GNUmakefile create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/LICENSE create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/README.md diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 5fe719567..b8af96345 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -4,9 +4,10 @@ import ( "fmt" "log" - ocommon "github.com/hashicorp/packer/builder/oracle/common" + "github.com/hashicorp/go-cleanhttp" + "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/go-oracle-terraform/opc" "github.com/hashicorp/packer/common" - "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" ) @@ -31,14 +32,33 @@ func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { } func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { + + httpClient := cleanhttp.DefaultClient() + config := &opc.Config{ + Username: opc.String(b.config.Username), + Password: opc.String(b.config.Password), + IdentityDomain: opc.String(b.config.IdentityDomain), + APIEndpoint: b.config.apiEndpointURL, + LogLevel: opc.LogDebug, + // Logger: # Leave blank to use the default logger, or provide your own + HTTPClient: httpClient, + } + // Create the Compute Client + client, err := compute.NewComputeClient(config) + if err != nil { + return nil, fmt.Errorf("Error creating OPC Compute Client: %s", err) + } + // Populate the state bag state := new(multistep.BasicStateBag) state.Put("config", b.config) state.Put("hook", hook) state.Put("ui", ui) + state.Put("client", client) // Build the steps steps := []multistep.Step{ + /* &ocommon.StepKeyPair{ Debug: b.config.PackerDebug, DebugKeyPath: fmt.Sprintf("oci_classic_%s.pem", b.config.PackerBuildName), @@ -55,6 +75,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &common.StepProvision{}, &stepSnapshot{}, + */ } // Run the steps diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 6a3c05fc8..050f78d91 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -2,6 +2,7 @@ package classic import ( "fmt" + "net/url" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" @@ -20,6 +21,7 @@ type Config struct { Password string `mapstructure:"password"` IdentityDomain string `mapstructure:"identity_domain"` APIEndpoint string `mapstructure:"api_endpoint"` + apiEndpointURL *url.URL // Image Shape string `mapstructure:"shape"` @@ -40,5 +42,10 @@ func NewConfig(raws ...interface{}) (*Config, error) { return nil, fmt.Errorf("Failed to mapstructure Config: %+v", err) } + c.apiEndpointURL, err = url.Parse(c.APIEndpoint) + if err != nil { + return nil, fmt.Errorf("Error parsing API Endpoint: %s", err) + } + return c, nil } diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/CHANGELOG.md b/vendor/github.com/hashicorp/go-oracle-terraform/CHANGELOG.md new file mode 100644 index 000000000..70e5b3c31 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/CHANGELOG.md @@ -0,0 +1,134 @@ +## 0.6.6 (January 11, 2018) + +* compute: Create and delete machine images [GH-101] + +## 0.6.5 (January 8, 2018) + +* compute: Orchestration failures should explicitly tell the user why it failed [GH-100] + +## 0.6.4 (Decemeber 20, 2017) + +* compute: Added suspend functionality to orchestrated instances [GH-99] + +## 0.6.3 (December 13, 2017) + +* storage: Added remove header option to storage objects and containers [GH-96] + +## 0.6.2 (November 28, 2017) + +* client: Added a UserAgent to the Client [GH-98] + +## 0.6.1 (Novemeber 26, 2017) + +* compute: Added is_default_gateway to network attributes for instances [GH-97] + + +## 0.6.0 (November 10, 2017) + +* compute: Added is_default_gateway to network attributes for instances [GH-90] + +* compute: Added the orchestration resource, specifically for instance creation [GH-91] + +## 0.5.1 (October 5, 2017) + +* java: Fixed subscription_type field + +## 0.5.0 (October 5, 2017) + +* java: Added more fields to java service instance [GH-89] + +## 0.4.0 (September 14, 2017) + +* database: Add utility resources [GH-87] + +* compute: Increase storage volume snapshot create timeout [GH-88] + +## 0.3.4 (August 16, 2017) + +* storage_volumes: Actually capture errors during a storage volume create ([#86](https://github.com/hashicorp/go-oracle-terraform/issues/86)) + +## 0.3.3 (August 10, 2017) + +* Add `ExposedHeaders` to storage containers ([#85](https://github.com/hashicorp/go-oracle-terraform/issues/85)) + +* Fixed `AllowedOrigins` in storage containers ([#85](https://github.com/hashicorp/go-oracle-terraform/issues/85)) + +## 0.3.2 (August 7, 2017) + +* Add `id` for storage objects ([#84](https://github.com/hashicorp/go-oracle-terraform/issues/84)) + +## 0.3.1 (August 7, 2017) + +* Update tests for Database parameter changes ([#83](https://github.com/hashicorp/go-oracle-terraform/issues/83)) + +## 0.3.0 (August 7, 2017) + + * Add JaaS Service Instances ([#82](https://github.com/hashicorp/go-oracle-terraform/issues/82)) + + * Add storage objects ([#81](https://github.com/hashicorp/go-oracle-terraform/issues/81)) + +## 0.2.0 (July 27, 2017) + + * service_instance: Switches yes/no strings to bool in input struct and then converts back to strings for ease of use on user end ([#80](https://github.com/hashicorp/go-oracle-terraform/issues/80)) + +## 0.1.9 (July 20, 2017) + + * service_instance: Update delete retry count ([#79](https://github.com/hashicorp/go-oracle-terraform/issues/79)) + + * service_instance: Add additional fields ([#79](https://github.com/hashicorp/go-oracle-terraform/issues/79)) + +## 0.1.8 (July 19, 2017) + + * storage_volumes: Add SSD support ([#78](https://github.com/hashicorp/go-oracle-terraform/issues/78)) + +## 0.1.7 (July 19, 2017) + + * database: Adds the Oracle Database Cloud to the available sdks. ([#77](https://github.com/hashicorp/go-oracle-terraform/issues/77)) + + * database: Adds Service Instances to the database sdk ([#77](https://github.com/hashicorp/go-oracle-terraform/issues/77)) + +## 0.1.6 (July 18, 2017) + + * opc: Add timeouts to instance and storage inputs ([#75](https://github.com/hashicorp/go-oracle-terraform/issues/75)) + +## 0.1.5 (July 5, 2017) + + * storage: User must pass in Storage URL to CRUD resources ([#74](https://github.com/hashicorp/go-oracle-terraform/issues/74)) + +## 0.1.4 (June 30, 2017) + + * opc: Fix infinite loop around auth token exceeding it's 25 minute duration. ([#73](https://github.com/hashicorp/go-oracle-terraform/issues/73)) + +## 0.1.3 (June 30, 2017) + + * opc: Add additional logs instance logs ([#72](https://github.com/hashicorp/go-oracle-terraform/issues/72)) + + * opc: Increase instance creation and deletion timeout ([#72](https://github.com/hashicorp/go-oracle-terraform/issues/72)) + +## 0.1.2 (June 30, 2017) + + +FEATURES: + + * opc: Add image snapshots ([#67](https://github.com/hashicorp/go-oracle-terraform/issues/67)) + + * storage: Storage containers have been added ([#70](https://github.com/hashicorp/go-oracle-terraform/issues/70)) + + +IMPROVEMENTS: + + * opc: Refactored client to be generic for multiple Oracle api endpoints ([#68](https://github.com/hashicorp/go-oracle-terraform/issues/68)) + + * opc: Instance creation retries when an instance enters a deleted state ([#71](https://github.com/hashicorp/go-oracle-terraform/issues/71)) + +## 0.1.1 (May 31, 2017) + +IMPROVEMENTS: + + * opc: Add max_retries capabilities ([#66](https://github.com/hashicorp/go-oracle-terraform/issues/66)) + +## 0.1.0 (May 25, 2017) + +BACKWARDS INCOMPATIBILITIES / NOTES: + + * Initial Release of OPC SDK diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/GNUmakefile b/vendor/github.com/hashicorp/go-oracle-terraform/GNUmakefile new file mode 100644 index 000000000..9c5a0e2f2 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/GNUmakefile @@ -0,0 +1,41 @@ +TEST?=$$(go list ./... |grep -v 'vendor') +GOFMT_FILES?=$$(find . -name '*.go' |grep -v vendor) + +test: fmtcheck errcheck + go test -i $(TEST) || exit 1 + echo $(TEST) | \ + xargs -t -n4 go test $(TESTARGS) -timeout=60m -parallel=4 + +testacc: fmtcheck + ORACLE_ACC=1 go test -v $(TEST) $(TESTARGS) -timeout 120m + +testrace: fmtcheck + ORACLE_ACC= go test -race $(TEST) $(TESTARGS) + +cover: + @go tool cover 2>/dev/null; if [ $$? -eq 3 ]; then \ + go get -u golang.org/x/tools/cmd/cover; \ + fi + go test $(TEST) -coverprofile=coverage.out + go tool cover -html=coverage.out + rm coverage.out + +vet: + @echo "go vet ." + @go vet $$(go list ./... | grep -v vendor/) ; if [ $$? -eq 1 ]; then \ + echo ""; \ + echo "Vet found suspicious constructs. Please check the reported constructs"; \ + echo "and fix them if necessary before submitting the code for review."; \ + exit 1; \ + fi + +fmt: + gofmt -w $(GOFMT_FILES) + +fmtcheck: + @sh -c "'$(CURDIR)/scripts/gofmtcheck.sh'" + +errcheck: + @sh -c "'$(CURDIR)/scripts/errcheck.sh'" + +.PHONY: tools build test testacc testrace cover vet fmt fmtcheck errcheck diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/LICENSE b/vendor/github.com/hashicorp/go-oracle-terraform/LICENSE new file mode 100644 index 000000000..a612ad981 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/README.md b/vendor/github.com/hashicorp/go-oracle-terraform/README.md new file mode 100644 index 000000000..0accc2c32 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/README.md @@ -0,0 +1,108 @@ +Oracle SDK for Terraform +=========================================== + +**Note:** This SDK is _not_ meant to be a comprehensive SDK for Oracle Cloud. This is meant to be used solely with Terraform. + +OPC Config +---------- + +To create the Oracle clients, a populated configuration struct is required. +The config struct holds the following fields: + +* `Username` - (`*string`) The Username used to authenticate to Oracle Public Cloud. +* `Password` - (`*string`) The Password used to authenticate to Oracle Public Cloud. +* `IdentityDomain` - (`*string`) The identity domain for Oracle Public Cloud. +* `APIEndpoint` - (`*url.URL`) The API Endpoint provided by Oracle Public Cloud. +* `LogLevel` - (`LogLevelType`) Defaults to `opc.LogOff`, can be either `opc.LogOff` or `opc.LogDebug`. +* `Logger` - (`Logger`) Must satisfy the generic `Logger` interface. Defaults to `ioutil.Discard` for the `LogOff` loglevel, and `os.Stderr` for the `LogDebug` loglevel. +* `HTTPClient` - (`*http.Client`) Defaults to generic HTTP Client if unspecified. + +Oracle Compute Client +---------------------- +The Oracle Compute Client requires an OPC Config object to be populated in order to create the client. + +Full example to create an OPC Compute instance: +```go +package main + +import ( + "fmt" + "net/url" + "github.com/hashicorp/go-oracle-terraform/opc" + "github.com/hashicorp/go-oracle-terraform/compute" +) + +func main() { + apiEndpoint, err := url.Parse("myAPIEndpoint") + if err != nil { + fmt.Errorf("Error parsing API Endpoint: %s", err) + } + + config := &opc.Config{ + Username: opc.String("myusername"), + Password: opc.String("mypassword"), + IdentityDomain: opc.String("myidentitydomain"), + APIEndpoint: apiEndpoint, + LogLevel: opc.LogDebug, + // Logger: # Leave blank to use the default logger, or provide your own + // HTTPClient: # Leave blank to use default HTTP Client, or provider your own + } + // Create the Compute Client + client, err := compute.NewComputeClient(config) + if err != nil { + fmt.Errorf("Error creating OPC Compute Client: %s", err) + } + // Create instances client + instanceClient := client.Instances() + + // Instances Input + input := &compute.CreateInstanceInput{ + Name: "test-instance", + Label: "test", + Shape: "oc3", + ImageList: "/oracle/public/oel_6.7_apaas_16.4.5_1610211300", + Storage: nil, + BootOrder: nil, + SSHKeys: []string{}, + Attributes: map[string]interface{}{}, + } + + // Create the instance + instance, err := instanceClient.CreateInstance(input) + if err != nil { + fmt.Errorf("Error creating instance: %s", err) + } + fmt.Printf("Instance Created: %#v", instance) +} +``` + +Please refer to inline documentation for each resource that the compute client provides. + +Running the SDK Integration Tests +----------------------------- + +To authenticate with the Oracle Compute Cloud the following credentails must be set in the following environment variables: + +- `OPC_ENDPOINT` - Endpoint provided by Oracle Public Cloud (e.g. https://api-z13.compute.em2.oraclecloud.com/\) +- `OPC_USERNAME` - Username for Oracle Public Cloud +- `OPC_PASSWORD` - Password for Oracle Public Cloud +- `OPC_IDENTITY_DOMAIN` - Identity domain for Oracle Public Cloud + + +The Integration tests can be ran with the following command: +```sh +$ make testacc +``` + +Isolating a single SDK package can be done via the `TEST` environment variable +```sh +$ make testacc TEST=./compute +``` + +Isolating a single test within a package can be done via the `TESTARGS` environment variable +```sh +$ make testacc TEST=./compute TESTARGS='-run=TestAccIPAssociationLifeCycle' +``` + +Tests are ran with logs being sent to `ioutil.Discard` by default. +Display debug logs inside of tests by setting the `ORACLE_LOG` environment variable to any value. diff --git a/vendor/vendor.json b/vendor/vendor.json index e8c3fb6f1..2f8f8307d 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -790,6 +790,12 @@ "path": "github.com/hashicorp/go-multierror", "revision": "d30f09973e19c1dfcd120b2d9c4f168e68d6b5d5" }, + { + "checksumSHA1": "Nf2Gdn9M1KlUS3sovKfymO1VJF4=", + "path": "github.com/hashicorp/go-oracle-terraform", + "revision": "5a9a298c54339d2296d2f1135eae55a3a8f5e8c2", + "revisionTime": "2018-01-11T20:31:13Z" + }, { "checksumSHA1": "ErJHGU6AVPZM9yoY/xV11TwSjQs=", "path": "github.com/hashicorp/go-retryablehttp", From 967b858fc3262b00ab95433ce1bf2f598991b68d Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 12 Jan 2018 15:46:34 -0800 Subject: [PATCH 04/61] add go-oracle client --- .../go-oracle-terraform/client/client.go | 245 ++++++ .../go-oracle-terraform/client/logging.go | 28 + .../go-oracle-terraform/client/version.go | 35 + .../go-oracle-terraform/compute/acl.go | 138 +++ .../compute/authentication.go | 34 + .../compute/compute_client.go | 167 ++++ .../compute/compute_resource_client.go | 113 +++ .../go-oracle-terraform/compute/image_list.go | 154 ++++ .../compute/image_list_entries.go | 122 +++ .../go-oracle-terraform/compute/instances.go | 788 ++++++++++++++++++ .../compute/ip_address_associations.go | 152 ++++ .../compute/ip_address_prefix_set.go | 135 +++ .../compute/ip_address_reservations.go | 190 +++++ .../compute/ip_associations.go | 118 +++ .../compute/ip_network_exchange.go | 99 +++ .../compute/ip_networks.go | 186 +++++ .../compute/ip_reservations.go | 147 ++++ .../compute/machine_images.go | 143 ++++ .../compute/orchestration.go | 451 ++++++++++ .../go-oracle-terraform/compute/routes.go | 153 ++++ .../go-oracle-terraform/compute/sec_rules.go | 193 +++++ .../compute/security_applications.go | 150 ++++ .../compute/security_associations.go | 95 +++ .../compute/security_ip_lists.go | 113 +++ .../compute/security_lists.go | 131 +++ .../compute/security_protocols.go | 187 +++++ .../compute/security_rules.go | 266 ++++++ .../go-oracle-terraform/compute/snapshots.go | 217 +++++ .../go-oracle-terraform/compute/ssh_keys.go | 124 +++ .../compute/storage_volume_attachments.go | 178 ++++ .../compute/storage_volume_snapshots.go | 251 ++++++ .../compute/storage_volumes.go | 389 +++++++++ .../go-oracle-terraform/compute/test_utils.go | 119 +++ .../compute/virtual_nic.go | 52 ++ .../compute/virtual_nic_sets.go | 154 ++++ .../go-oracle-terraform/opc/config.go | 22 + .../go-oracle-terraform/opc/convert.go | 9 + .../go-oracle-terraform/opc/errors.go | 12 + .../go-oracle-terraform/opc/logger.go | 70 ++ vendor/vendor.json | 18 + 40 files changed, 6348 insertions(+) create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/client/client.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/client/logging.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/client/version.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/acl.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/authentication.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/compute_client.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/compute_resource_client.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list_entries.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/instances.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_associations.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_prefix_set.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_reservations.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_associations.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_network_exchange.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_networks.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_reservations.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/machine_images.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/orchestration.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/routes.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/sec_rules.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_applications.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_associations.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_ip_lists.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_lists.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_protocols.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/security_rules.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/snapshots.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/ssh_keys.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_attachments.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_snapshots.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volumes.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/test_utils.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic_sets.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/opc/config.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/opc/convert.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/opc/errors.go create mode 100644 vendor/github.com/hashicorp/go-oracle-terraform/opc/logger.go diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/client/client.go b/vendor/github.com/hashicorp/go-oracle-terraform/client/client.go new file mode 100644 index 000000000..4188d111b --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/client/client.go @@ -0,0 +1,245 @@ +package client + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "runtime" + "time" + + "github.com/hashicorp/go-oracle-terraform/opc" +) + +const DEFAULT_MAX_RETRIES = 1 +const USER_AGENT_HEADER = "User-Agent" + +var ( + // defaultUserAgent builds a string containing the Go version, system archityecture and OS, + // and the go-autorest version. + defaultUserAgent = fmt.Sprintf("Go/%s (%s-%s) go-oracle-terraform/%s", + runtime.Version(), + runtime.GOARCH, + runtime.GOOS, + Version(), + ) +) + +// Client represents an authenticated compute client, with compute credentials and an api client. +type Client struct { + IdentityDomain *string + UserName *string + Password *string + APIEndpoint *url.URL + httpClient *http.Client + MaxRetries *int + UserAgent *string + logger opc.Logger + loglevel opc.LogLevelType +} + +func NewClient(c *opc.Config) (*Client, error) { + // First create a client + client := &Client{ + IdentityDomain: c.IdentityDomain, + UserName: c.Username, + Password: c.Password, + APIEndpoint: c.APIEndpoint, + UserAgent: &defaultUserAgent, + httpClient: c.HTTPClient, + MaxRetries: c.MaxRetries, + loglevel: c.LogLevel, + } + if c.UserAgent != nil { + client.UserAgent = c.UserAgent + } + + // Setup logger; defaults to stdout + if c.Logger == nil { + client.logger = opc.NewDefaultLogger() + } else { + client.logger = c.Logger + } + + // If LogLevel was not set to something different, + // double check for env var + if c.LogLevel == 0 { + client.loglevel = opc.LogLevel() + } + + // Default max retries if unset + if c.MaxRetries == nil { + client.MaxRetries = opc.Int(DEFAULT_MAX_RETRIES) + } + + // Protect against any nil http client + if c.HTTPClient == nil { + return nil, fmt.Errorf("No HTTP client specified in config") + } + + return client, nil +} + +// Marshalls the request body and returns the resulting byte slice +// This is split out of the BuildRequestBody method so as to allow +// the developer to print a debug string of the request body if they +// should so choose. +func (c *Client) MarshallRequestBody(body interface{}) ([]byte, error) { + // Verify interface isnt' nil + if body == nil { + return nil, nil + } + + return json.Marshal(body) +} + +// Builds an HTTP Request that accepts a pre-marshaled body parameter as a raw byte array +// Returns the raw HTTP Request and any error occured +func (c *Client) BuildRequestBody(method, path string, body []byte) (*http.Request, error) { + // Parse URL Path + urlPath, err := url.Parse(path) + if err != nil { + return nil, err + } + + var requestBody io.ReadSeeker + if len(body) != 0 { + requestBody = bytes.NewReader(body) + } + + // Create Request + req, err := http.NewRequest(method, c.formatURL(urlPath), requestBody) + if err != nil { + return nil, err + } + // Adding UserAgent Header + req.Header.Add(USER_AGENT_HEADER, *c.UserAgent) + + return req, nil +} + +// Build a new HTTP request that doesn't marshall the request body +func (c *Client) BuildNonJSONRequest(method, path string, body io.ReadSeeker) (*http.Request, error) { + // Parse URL Path + urlPath, err := url.Parse(path) + if err != nil { + return nil, err + } + + // Create request + req, err := http.NewRequest(method, c.formatURL(urlPath), body) + if err != nil { + return nil, err + } + // Adding UserAgentHeader + req.Header.Add(USER_AGENT_HEADER, *c.UserAgent) + + return req, nil +} + +// This method executes the http.Request from the BuildRequest method. +// It is split up to add additional authentication that is Oracle API dependent. +func (c *Client) ExecuteRequest(req *http.Request) (*http.Response, error) { + // Execute request with supplied client + resp, err := c.retryRequest(req) + if err != nil { + return resp, err + } + + if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices { + return resp, nil + } + + oracleErr := &opc.OracleError{ + StatusCode: resp.StatusCode, + } + + // Even though the returned body will be in json form, it's undocumented what + // fields are actually returned. Once we get documentation of the actual + // error fields that are possible to be returned we can have stricter error types. + if resp.Body != nil { + buf := new(bytes.Buffer) + buf.ReadFrom(resp.Body) + oracleErr.Message = buf.String() + } + + // Should return the response object regardless of error, + // some resources need to verify and check status code on errors to + // determine if an error actually occurs or not. + return resp, oracleErr +} + +// Allow retrying the request until it either returns no error, +// or we exceed the number of max retries +func (c *Client) retryRequest(req *http.Request) (*http.Response, error) { + // Double check maxRetries is not nil + var retries int + if c.MaxRetries == nil { + retries = DEFAULT_MAX_RETRIES + } else { + retries = *c.MaxRetries + } + + var statusCode int + var errMessage string + + for i := 0; i < retries; i++ { + resp, err := c.httpClient.Do(req) + if err != nil { + return resp, err + } + + if resp.StatusCode >= http.StatusOK && resp.StatusCode < http.StatusMultipleChoices { + return resp, nil + } + + buf := new(bytes.Buffer) + buf.ReadFrom(resp.Body) + errMessage = buf.String() + statusCode = resp.StatusCode + c.DebugLogString(fmt.Sprintf("Encountered HTTP (%d) Error: %s", statusCode, errMessage)) + c.DebugLogString(fmt.Sprintf("%d/%d retries left", i+1, retries)) + } + + oracleErr := &opc.OracleError{ + StatusCode: statusCode, + Message: errMessage, + } + + // We ran out of retries to make, return the error and response + return nil, oracleErr +} + +func (c *Client) formatURL(path *url.URL) string { + return c.APIEndpoint.ResolveReference(path).String() +} + +// Retry function +func (c *Client) WaitFor(description string, timeout time.Duration, test func() (bool, error)) error { + tick := time.Tick(1 * time.Second) + + timeoutSeconds := int(timeout.Seconds()) + + for i := 0; i < timeoutSeconds; i++ { + select { + case <-tick: + completed, err := test() + c.DebugLogString(fmt.Sprintf("Waiting for %s (%d/%ds)", description, i, timeoutSeconds)) + if err != nil || completed { + return err + } + } + } + return fmt.Errorf("Timeout waiting for %s", description) +} + +// Used to determine if the checked resource was found or not. +func WasNotFoundError(e error) bool { + err, ok := e.(*opc.OracleError) + if ok { + return err.StatusCode == 404 + } + return false +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/client/logging.go b/vendor/github.com/hashicorp/go-oracle-terraform/client/logging.go new file mode 100644 index 000000000..893997486 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/client/logging.go @@ -0,0 +1,28 @@ +package client + +import ( + "bytes" + "fmt" + "net/http" + + "github.com/hashicorp/go-oracle-terraform/opc" +) + +// Log a string if debug logs are on +func (c *Client) DebugLogString(str string) { + if c.loglevel != opc.LogDebug { + return + } + c.logger.Log(str) +} + +func (c *Client) DebugLogReq(req *http.Request) { + // Don't need to log this if not debugging + if c.loglevel != opc.LogDebug { + return + } + buf := new(bytes.Buffer) + buf.ReadFrom(req.Body) + c.logger.Log(fmt.Sprintf("DEBUG: HTTP %s Req %s: %s", + req.Method, req.URL.String(), buf.String())) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/client/version.go b/vendor/github.com/hashicorp/go-oracle-terraform/client/version.go new file mode 100644 index 000000000..3538fd181 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/client/version.go @@ -0,0 +1,35 @@ +package client + +import ( + "bytes" + "fmt" + "strings" + "sync" +) + +const ( + major = 0 + minor = 6 + patch = 2 + tag = "" +) + +var once sync.Once +var version string + +// Version returns the semantic version (see http://semver.org). +func Version() string { + once.Do(func() { + semver := fmt.Sprintf("%d.%d.%d", major, minor, patch) + verBuilder := bytes.NewBufferString(semver) + if tag != "" && tag != "-" { + updated := strings.TrimPrefix(tag, "-") + _, err := verBuilder.WriteString("-" + updated) + if err == nil { + verBuilder = bytes.NewBufferString(semver) + } + } + version = verBuilder.String() + }) + return version +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/acl.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/acl.go new file mode 100644 index 000000000..877f51c28 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/acl.go @@ -0,0 +1,138 @@ +package compute + +// ACLsClient is a client for the ACLs functions of the Compute API. +type ACLsClient struct { + ResourceClient +} + +const ( + ACLDescription = "acl" + ACLContainerPath = "/network/v1/acl/" + ACLResourcePath = "/network/v1/acl" +) + +// ACLs obtains a ACLsClient which can be used to access to the +// ACLs functions of the Compute API +func (c *ComputeClient) ACLs() *ACLsClient { + return &ACLsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: ACLDescription, + ContainerPath: ACLContainerPath, + ResourceRootPath: ACLResourcePath, + }} +} + +// ACLInfo describes an existing ACL. +type ACLInfo struct { + // Description of the ACL + Description string `json:"description"` + // Indicates whether the ACL is enabled + Enabled bool `json:"enabledFlag"` + // The name of the ACL + Name string `json:"name"` + // Tags associated with the ACL + Tags []string `json:"tags"` + // Uniform Resource Identifier for the ACL + URI string `json:"uri"` +} + +// CreateACLInput defines a ACL to be created. +type CreateACLInput struct { + // Description of the ACL + // Optional + Description string `json:"description"` + + // Enables or disables the ACL. Set to true by default. + //Set this to false to disable the ACL. + // Optional + Enabled bool `json:"enabledFlag"` + + // The name of the ACL to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Strings that you can use to tag the ACL. + // Optional + Tags []string `json:"tags"` +} + +// CreateACL creates a new ACL. +func (c *ACLsClient) CreateACL(createInput *CreateACLInput) (*ACLInfo, error) { + createInput.Name = c.getQualifiedName(createInput.Name) + + var aclInfo ACLInfo + if err := c.createResource(createInput, &aclInfo); err != nil { + return nil, err + } + + return c.success(&aclInfo) +} + +// GetACLInput describes the ACL to get +type GetACLInput struct { + // The name of the ACL to query for + // Required + Name string `json:"name"` +} + +// GetACL retrieves the ACL with the given name. +func (c *ACLsClient) GetACL(getInput *GetACLInput) (*ACLInfo, error) { + var aclInfo ACLInfo + if err := c.getResource(getInput.Name, &aclInfo); err != nil { + return nil, err + } + + return c.success(&aclInfo) +} + +// UpdateACLInput describes a secruity rule to update +type UpdateACLInput struct { + // Description of the ACL + // Optional + Description string `json:"description"` + + // Enables or disables the ACL. Set to true by default. + //Set this to false to disable the ACL. + // Optional + Enabled bool `json:"enabledFlag"` + + // The name of the ACL to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Strings that you can use to tag the ACL. + // Optional + Tags []string `json:"tags"` +} + +// UpdateACL modifies the properties of the ACL with the given name. +func (c *ACLsClient) UpdateACL(updateInput *UpdateACLInput) (*ACLInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + + var aclInfo ACLInfo + if err := c.updateResource(updateInput.Name, updateInput, &aclInfo); err != nil { + return nil, err + } + + return c.success(&aclInfo) +} + +// DeleteACLInput describes the ACL to delete +type DeleteACLInput struct { + // The name of the ACL to delete. + // Required + Name string `json:"name"` +} + +// DeleteACL deletes the ACL with the given name. +func (c *ACLsClient) DeleteACL(deleteInput *DeleteACLInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *ACLsClient) success(aclInfo *ACLInfo) (*ACLInfo, error) { + aclInfo.Name = c.getUnqualifiedName(aclInfo.Name) + return aclInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/authentication.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/authentication.go new file mode 100644 index 000000000..6c62d7ddd --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/authentication.go @@ -0,0 +1,34 @@ +package compute + +import ( + "fmt" + "time" +) + +// AuthenticationReq represents the body of an authentication request. +type AuthenticationReq struct { + User string `json:"user"` + Password string `json:"password"` +} + +// Get a new auth cookie for the compute client +func (c *ComputeClient) getAuthenticationCookie() error { + req := AuthenticationReq{ + User: c.getUserName(), + Password: *c.client.Password, + } + + rsp, err := c.executeRequest("POST", "/authenticate/", req) + if err != nil { + return err + } + + if len(rsp.Cookies()) == 0 { + return fmt.Errorf("No authentication cookie found in response %#v", rsp) + } + + c.client.DebugLogString("Successfully authenticated to OPC") + c.authCookie = rsp.Cookies()[0] + c.cookieIssued = time.Now() + return nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/compute_client.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/compute_client.go new file mode 100644 index 000000000..3719dbb15 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/compute_client.go @@ -0,0 +1,167 @@ +package compute + +import ( + "fmt" + "net/http" + "regexp" + "strings" + "time" + + "github.com/hashicorp/go-oracle-terraform/client" + "github.com/hashicorp/go-oracle-terraform/opc" +) + +const CMP_ACME = "/Compute-%s" +const CMP_USERNAME = "/Compute-%s/%s" +const CMP_QUALIFIED_NAME = "%s/%s" + +// Client represents an authenticated compute client, with compute credentials and an api client. +type ComputeClient struct { + client *client.Client + authCookie *http.Cookie + cookieIssued time.Time +} + +func NewComputeClient(c *opc.Config) (*ComputeClient, error) { + computeClient := &ComputeClient{} + client, err := client.NewClient(c) + if err != nil { + return nil, err + } + computeClient.client = client + + if err := computeClient.getAuthenticationCookie(); err != nil { + return nil, err + } + + return computeClient, nil +} + +func (c *ComputeClient) executeRequest(method, path string, body interface{}) (*http.Response, error) { + reqBody, err := c.client.MarshallRequestBody(body) + if err != nil { + return nil, err + } + + req, err := c.client.BuildRequestBody(method, path, reqBody) + if err != nil { + return nil, err + } + + debugReqString := fmt.Sprintf("HTTP %s Req (%s)", method, path) + if body != nil { + req.Header.Set("Content-Type", "application/oracle-compute-v3+json") + // Don't leak credentials in STDERR + if path != "/authenticate/" { + debugReqString = fmt.Sprintf("%s:\n %+v", debugReqString, string(reqBody)) + } + } + // Log the request before the authentication cookie, so as not to leak credentials + c.client.DebugLogString(debugReqString) + // If we have an authentication cookie, let's authenticate, refreshing cookie if need be + if c.authCookie != nil { + if time.Since(c.cookieIssued).Minutes() > 25 { + c.authCookie = nil + if err := c.getAuthenticationCookie(); err != nil { + return nil, err + } + } + req.AddCookie(c.authCookie) + } + + resp, err := c.client.ExecuteRequest(req) + if err != nil { + return nil, err + } + return resp, nil +} + +func (c *ComputeClient) getACME() string { + return fmt.Sprintf(CMP_ACME, *c.client.IdentityDomain) +} + +func (c *ComputeClient) getUserName() string { + return fmt.Sprintf(CMP_USERNAME, *c.client.IdentityDomain, *c.client.UserName) +} + +func (c *ComputeClient) getQualifiedACMEName(name string) string { + if name == "" { + return "" + } + if strings.HasPrefix(name, "/Compute-") && len(strings.Split(name, "/")) == 1 { + return name + } + return fmt.Sprintf(CMP_QUALIFIED_NAME, c.getACME(), name) +} + +// From compute_client +// GetObjectName returns the fully-qualified name of an OPC object, e.g. /identity-domain/user@email/{name} +func (c *ComputeClient) getQualifiedName(name string) string { + if name == "" { + return "" + } + if strings.HasPrefix(name, "/oracle") || strings.HasPrefix(name, "/Compute-") { + return name + } + return fmt.Sprintf(CMP_QUALIFIED_NAME, c.getUserName(), name) +} + +func (c *ComputeClient) getObjectPath(root, name string) string { + return fmt.Sprintf("%s%s", root, c.getQualifiedName(name)) +} + +// GetUnqualifiedName returns the unqualified name of an OPC object, e.g. the {name} part of /identity-domain/user@email/{name} +func (c *ComputeClient) getUnqualifiedName(name string) string { + if name == "" { + return name + } + if strings.HasPrefix(name, "/oracle") { + return name + } + if !strings.Contains(name, "/") { + return name + } + + nameParts := strings.Split(name, "/") + return strings.Join(nameParts[3:], "/") +} + +func (c *ComputeClient) unqualify(names ...*string) { + for _, name := range names { + *name = c.getUnqualifiedName(*name) + } +} + +func (c *ComputeClient) unqualifyUrl(url *string) { + var validID = regexp.MustCompile(`(\/(Compute[^\/\s]+))(\/[^\/\s]+)(\/[^\/\s]+)`) + name := validID.FindString(*url) + *url = c.getUnqualifiedName(name) +} + +func (c *ComputeClient) getQualifiedList(list []string) []string { + for i, name := range list { + list[i] = c.getQualifiedName(name) + } + return list +} + +func (c *ComputeClient) getUnqualifiedList(list []string) []string { + for i, name := range list { + list[i] = c.getUnqualifiedName(name) + } + return list +} + +func (c *ComputeClient) getQualifiedListName(name string) string { + nameParts := strings.Split(name, ":") + listType := nameParts[0] + listName := nameParts[1] + return fmt.Sprintf("%s:%s", listType, c.getQualifiedName(listName)) +} + +func (c *ComputeClient) unqualifyListName(qualifiedName string) string { + nameParts := strings.Split(qualifiedName, ":") + listType := nameParts[0] + listName := nameParts[1] + return fmt.Sprintf("%s:%s", listType, c.getUnqualifiedName(listName)) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/compute_resource_client.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/compute_resource_client.go new file mode 100644 index 000000000..2c3e1dc9e --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/compute_resource_client.go @@ -0,0 +1,113 @@ +package compute + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + + "github.com/mitchellh/mapstructure" +) + +// ResourceClient is an AuthenticatedClient with some additional information about the resources to be addressed. +type ResourceClient struct { + *ComputeClient + ResourceDescription string + ContainerPath string + ResourceRootPath string +} + +func (c *ResourceClient) createResource(requestBody interface{}, responseBody interface{}) error { + resp, err := c.executeRequest("POST", c.ContainerPath, requestBody) + if err != nil { + return err + } + + return c.unmarshalResponseBody(resp, responseBody) +} + +func (c *ResourceClient) updateResource(name string, requestBody interface{}, responseBody interface{}) error { + resp, err := c.executeRequest("PUT", c.getObjectPath(c.ResourceRootPath, name), requestBody) + if err != nil { + return err + } + + return c.unmarshalResponseBody(resp, responseBody) +} + +func (c *ResourceClient) getResource(name string, responseBody interface{}) error { + var objectPath string + if name != "" { + objectPath = c.getObjectPath(c.ResourceRootPath, name) + } else { + objectPath = c.ResourceRootPath + } + resp, err := c.executeRequest("GET", objectPath, nil) + if err != nil { + return err + } + + return c.unmarshalResponseBody(resp, responseBody) +} + +func (c *ResourceClient) deleteResource(name string) error { + var objectPath string + if name != "" { + objectPath = c.getObjectPath(c.ResourceRootPath, name) + } else { + objectPath = c.ResourceRootPath + } + _, err := c.executeRequest("DELETE", objectPath, nil) + if err != nil { + return err + } + + // No errors and no response body to write + return nil +} + +func (c *ResourceClient) deleteOrchestration(name string) error { + var objectPath string + if name != "" { + objectPath = c.getObjectPath(c.ResourceRootPath, name) + } else { + objectPath = c.ResourceRootPath + } + // Set terminate to true as we always want to delete an orchestration + objectPath = fmt.Sprintf("%s?terminate=True", objectPath) + + _, err := c.executeRequest("DELETE", objectPath, nil) + if err != nil { + return err + } + + // No errors and no response body to write + return nil +} + +func (c *ResourceClient) unmarshalResponseBody(resp *http.Response, iface interface{}) error { + buf := new(bytes.Buffer) + buf.ReadFrom(resp.Body) + c.client.DebugLogString(fmt.Sprintf("HTTP Resp (%d): %s", resp.StatusCode, buf.String())) + // JSON decode response into interface + var tmp interface{} + dcd := json.NewDecoder(buf) + if err := dcd.Decode(&tmp); err != nil { + return err + } + + // Use mapstructure to weakly decode into the resulting interface + msdcd, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + WeaklyTypedInput: true, + Result: iface, + TagName: "json", + }) + if err != nil { + return err + } + + if err := msdcd.Decode(tmp); err != nil { + return err + } + return nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list.go new file mode 100644 index 000000000..0d4ca06ba --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list.go @@ -0,0 +1,154 @@ +package compute + +const ( + ImageListDescription = "Image List" + ImageListContainerPath = "/imagelist/" + ImageListResourcePath = "/imagelist" +) + +// ImageListClient is a client for the Image List functions of the Compute API. +type ImageListClient struct { + ResourceClient +} + +// ImageList obtains an ImageListClient which can be used to access to the +// Image List functions of the Compute API +func (c *ComputeClient) ImageList() *ImageListClient { + return &ImageListClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: ImageListDescription, + ContainerPath: ImageListContainerPath, + ResourceRootPath: ImageListResourcePath, + }} +} + +type ImageListEntry struct { + // User-defined parameters, in JSON format, that can be passed to an instance of this machine image when it is launched. + Attributes map[string]interface{} `json:"attributes"` + + // Name of the Image List. + ImageList string `json:"imagelist"` + + // A list of machine images. + MachineImages []string `json:"machineimages"` + + // Uniform Resource Identifier. + URI string `json:"uri"` + + // Version number of these Machine Images in the Image List. + Version int `json:"version"` +} + +// ImageList describes an existing Image List. +type ImageList struct { + // The image list entry to be used, by default, when launching instances using this image list + Default int `json:"default"` + + // A description of this image list. + Description string `json:"description"` + + // Each machine image in an image list is identified by an image list entry. + Entries []ImageListEntry `json:"entries"` + + // The name of the Image List + Name string `json:"name"` + + // Uniform Resource Identifier + URI string `json:"uri"` +} + +// CreateImageListInput defines an Image List to be created. +type CreateImageListInput struct { + // The image list entry to be used, by default, when launching instances using this image list. + // If you don't specify this value, it is set to 1. + // Optional + Default int `json:"default"` + + // A description of this image list. + // Required + Description string `json:"description"` + + // The name of the Image List + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` +} + +// CreateImageList creates a new Image List with the given name, key and enabled flag. +func (c *ImageListClient) CreateImageList(createInput *CreateImageListInput) (*ImageList, error) { + var imageList ImageList + createInput.Name = c.getQualifiedName(createInput.Name) + if err := c.createResource(&createInput, &imageList); err != nil { + return nil, err + } + + return c.success(&imageList) +} + +// DeleteKeyInput describes the image list to delete +type DeleteImageListInput struct { + // The name of the Image List + Name string `json:name` +} + +// DeleteImageList deletes the Image List with the given name. +func (c *ImageListClient) DeleteImageList(deleteInput *DeleteImageListInput) error { + deleteInput.Name = c.getQualifiedName(deleteInput.Name) + return c.deleteResource(deleteInput.Name) +} + +// GetImageListInput describes the image list to get +type GetImageListInput struct { + // The name of the Image List + Name string `json:name` +} + +// GetImageList retrieves the Image List with the given name. +func (c *ImageListClient) GetImageList(getInput *GetImageListInput) (*ImageList, error) { + getInput.Name = c.getQualifiedName(getInput.Name) + + var imageList ImageList + if err := c.getResource(getInput.Name, &imageList); err != nil { + return nil, err + } + + return c.success(&imageList) +} + +// UpdateImageListInput defines an Image List to be updated +type UpdateImageListInput struct { + // The image list entry to be used, by default, when launching instances using this image list. + // If you don't specify this value, it is set to 1. + // Optional + Default int `json:"default"` + + // A description of this image list. + // Required + Description string `json:"description"` + + // The name of the Image List + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` +} + +// UpdateImageList updates the key and enabled flag of the Image List with the given name. +func (c *ImageListClient) UpdateImageList(updateInput *UpdateImageListInput) (*ImageList, error) { + var imageList ImageList + updateInput.Name = c.getQualifiedName(updateInput.Name) + if err := c.updateResource(updateInput.Name, updateInput, &imageList); err != nil { + return nil, err + } + return c.success(&imageList) +} + +func (c *ImageListClient) success(imageList *ImageList) (*ImageList, error) { + c.unqualify(&imageList.Name) + + for _, v := range imageList.Entries { + v.MachineImages = c.getUnqualifiedList(v.MachineImages) + } + + return imageList, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list_entries.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list_entries.go new file mode 100644 index 000000000..ac72ef45a --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/image_list_entries.go @@ -0,0 +1,122 @@ +package compute + +import "fmt" + +const ( + ImageListEntryDescription = "image list entry" + ImageListEntryContainerPath = "/imagelist" + ImageListEntryResourcePath = "/imagelist" +) + +type ImageListEntriesClient struct { + ResourceClient +} + +// ImageListEntries() returns an ImageListEntriesClient that can be used to access the +// necessary CRUD functions for Image List Entry's. +func (c *ComputeClient) ImageListEntries() *ImageListEntriesClient { + return &ImageListEntriesClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: ImageListEntryDescription, + ContainerPath: ImageListEntryContainerPath, + ResourceRootPath: ImageListEntryResourcePath, + }, + } +} + +// ImageListEntryInfo contains the exported fields necessary to hold all the information about an +// Image List Entry +type ImageListEntryInfo struct { + // User-defined parameters, in JSON format, that can be passed to an instance of this machine + // image when it is launched. This field can be used, for example, to specify the location of + // a database server and login details. Instance metadata, including user-defined data is available + // at http://192.0.0.192/ within an instance. See Retrieving User-Defined Instance Attributes in Using + // Oracle Compute Cloud Service (IaaS). + Attributes map[string]interface{} `json:"attributes"` + // Name of the imagelist. + Name string `json:"imagelist"` + // A list of machine images. + MachineImages []string `json:"machineimages"` + // Uniform Resource Identifier for the Image List Entry + Uri string `json:"uri"` + // Version number of these machineImages in the imagelist. + Version int `json:"version"` +} + +type CreateImageListEntryInput struct { + // The name of the Image List + Name string + // User-defined parameters, in JSON format, that can be passed to an instance of this machine + // image when it is launched. This field can be used, for example, to specify the location of + // a database server and login details. Instance metadata, including user-defined data is + //available at http://192.0.0.192/ within an instance. See Retrieving User-Defined Instance + //Attributes in Using Oracle Compute Cloud Service (IaaS). + // Optional + Attributes map[string]interface{} `json:"attributes"` + // A list of machine images. + // Required + MachineImages []string `json:"machineimages"` + // The unique version of the entry in the image list. + // Required + Version int `json:"version"` +} + +// Create a new Image List Entry from an ImageListEntriesClient and an input struct. +// Returns a populated Info struct for the Image List Entry, and any errors +func (c *ImageListEntriesClient) CreateImageListEntry(input *CreateImageListEntryInput) (*ImageListEntryInfo, error) { + c.updateClientPaths(input.Name, -1) + var imageListEntryInfo ImageListEntryInfo + if err := c.createResource(&input, &imageListEntryInfo); err != nil { + return nil, err + } + return c.success(&imageListEntryInfo) +} + +type GetImageListEntryInput struct { + // The name of the Image List + Name string + // Version number of these machineImages in the imagelist. + Version int +} + +// Returns a populated ImageListEntryInfo struct from an input struct +func (c *ImageListEntriesClient) GetImageListEntry(input *GetImageListEntryInput) (*ImageListEntryInfo, error) { + c.updateClientPaths(input.Name, input.Version) + var imageListEntryInfo ImageListEntryInfo + if err := c.getResource("", &imageListEntryInfo); err != nil { + return nil, err + } + return c.success(&imageListEntryInfo) +} + +type DeleteImageListEntryInput struct { + // The name of the Image List + Name string + // Version number of these machineImages in the imagelist. + Version int +} + +func (c *ImageListEntriesClient) DeleteImageListEntry(input *DeleteImageListEntryInput) error { + c.updateClientPaths(input.Name, input.Version) + return c.deleteResource("") +} + +func (c *ImageListEntriesClient) updateClientPaths(name string, version int) { + var containerPath, resourcePath string + name = c.getQualifiedName(name) + containerPath = ImageListEntryContainerPath + name + "/entry/" + resourcePath = ImageListEntryContainerPath + name + "/entry" + if version != -1 { + containerPath = fmt.Sprintf("%s%d", containerPath, version) + resourcePath = fmt.Sprintf("%s/%d", resourcePath, version) + } + c.ContainerPath = containerPath + c.ResourceRootPath = resourcePath +} + +// Unqualifies any qualified fields in the IPNetworkInfo struct +func (c *ImageListEntriesClient) success(info *ImageListEntryInfo) (*ImageListEntryInfo, error) { + c.unqualifyUrl(&info.Uri) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/instances.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/instances.go new file mode 100644 index 000000000..ddfa8d6ec --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/instances.go @@ -0,0 +1,788 @@ +package compute + +import ( + "errors" + "fmt" + "strings" + "time" + + "github.com/hashicorp/go-oracle-terraform/client" +) + +const WaitForInstanceReadyTimeout = time.Duration(3600 * time.Second) +const WaitForInstanceDeleteTimeout = time.Duration(3600 * time.Second) + +// InstancesClient is a client for the Instance functions of the Compute API. +type InstancesClient struct { + ResourceClient +} + +// Instances obtains an InstancesClient which can be used to access to the +// Instance functions of the Compute API +func (c *ComputeClient) Instances() *InstancesClient { + return &InstancesClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "instance", + ContainerPath: "/launchplan/", + ResourceRootPath: "/instance", + }} +} + +type InstanceState string + +const ( + InstanceRunning InstanceState = "running" + InstanceInitializing InstanceState = "initializing" + InstancePreparing InstanceState = "preparing" + InstanceStarting InstanceState = "starting" + InstanceStopping InstanceState = "stopping" + InstanceShutdown InstanceState = "shutdown" + InstanceQueued InstanceState = "queued" + InstanceError InstanceState = "error" +) + +type InstanceDesiredState string + +const ( + InstanceDesiredRunning InstanceDesiredState = "running" + InstanceDesiredShutdown InstanceDesiredState = "shutdown" +) + +// InstanceInfo represents the Compute API's view of the state of an instance. +type InstanceInfo struct { + // The ID for the instance. Set by the SDK based on the request - not the API. + ID string + + // A dictionary of attributes to be made available to the instance. + // A value with the key "userdata" will be made available in an EC2-compatible manner. + Attributes map[string]interface{} `json:"attributes"` + + // The availability domain for the instance + AvailabilityDomain string `json:"availability_domain"` + + // Boot order list. + BootOrder []int `json:"boot_order"` + + // The default domain to use for the hostname and DNS lookups + Domain string `json:"domain"` + + // The desired state of an instance + DesiredState InstanceDesiredState `json:"desired_state"` + + // Optional ImageListEntry number. Default will be used if not specified + Entry int `json:"entry"` + + // The reason for the instance going to error state, if available. + ErrorReason string `json:"error_reason"` + + // SSH Server Fingerprint presented by the instance + Fingerprint string `json:"fingerprint"` + + // The hostname for the instance + Hostname string `json:"hostname"` + + // The format of the image + ImageFormat string `json:"image_format"` + + // Name of imagelist to be launched. + ImageList string `json:"imagelist"` + + // IP address of the instance. + IPAddress string `json:"ip"` + + // A label assigned by the user, specifically for defining inter-instance relationships. + Label string `json:"label"` + + // Name of this instance, generated by the server. + Name string `json:"name"` + + // Mapping of to network specifiers for virtual NICs to be attached to this instance. + Networking map[string]NetworkingInfo `json:"networking"` + + // A list of strings specifying arbitrary tags on nodes to be matched on placement. + PlacementRequirements []string `json:"placement_requirements"` + + // The OS platform for the instance. + Platform string `json:"platform"` + + // The priority at which this instance will be run + Priority string `json:"priority"` + + // Reference to the QuotaReservation, to be destroyed with the instance + QuotaReservation string `json:"quota_reservation"` + + // Array of relationship specifications to be satisfied on this instance's placement + Relationships []string `json:"relationships"` + + // Resolvers to use instead of the default resolvers + Resolvers []string `json:"resolvers"` + + // Add PTR records for the hostname + ReverseDNS bool `json:"reverse_dns"` + + // Type of instance, as defined on site configuration. + Shape string `json:"shape"` + + // Site to run on + Site string `json:"site"` + + // ID's of SSH keys that will be exposed to the instance. + SSHKeys []string `json:"sshkeys"` + + // The start time of the instance + StartTime string `json:"start_time"` + + // State of the instance. + State InstanceState `json:"state"` + + // The Storage Attachment information. + Storage []StorageAttachment `json:"storage_attachments"` + + // Array of tags associated with the instance. + Tags []string `json:"tags"` + + // vCable for this instance. + VCableID string `json:"vcable_id"` + + // Specify if the devices created for the instance are virtio devices. If not specified, the default + // will come from the cluster configuration file + Virtio bool `json:"virtio,omitempty"` + + // IP Address and port of the VNC console for the instance + VNC string `json:"vnc"` +} + +type StorageAttachment struct { + // The index number for the volume. + Index int `json:"index"` + + // The three-part name (/Compute-identity_domain/user/object) of the storage attachment. + Name string `json:"name"` + + // The three-part name (/Compute-identity_domain/user/object) of the storage volume attached to the instance. + StorageVolumeName string `json:"storage_volume_name"` +} + +func (i *InstanceInfo) getInstanceName() string { + return fmt.Sprintf(CMP_QUALIFIED_NAME, i.Name, i.ID) +} + +type CreateInstanceInput struct { + // A dictionary of user-defined attributes to be made available to the instance. + // Optional + Attributes map[string]interface{} `json:"attributes"` + // Boot order list + // Optional + BootOrder []int `json:"boot_order,omitempty"` + // The desired state of the opc instance. Can only be `running` or `shutdown` + // Omits if empty. + // Optional + DesiredState InstanceDesiredState `json:"desired_state,omitempty"` + // The host name assigned to the instance. On an Oracle Linux instance, + // this host name is displayed in response to the hostname command. + // Only relative DNS is supported. The domain name is suffixed to the host name + // that you specify. The host name must not end with a period. If you don't specify a + // host name, then a name is generated automatically. + // Optional + Hostname string `json:"hostname"` + // Name of imagelist to be launched. + // Optional + ImageList string `json:"imagelist"` + // A label assigned by the user, specifically for defining inter-instance relationships. + // Optional + Label string `json:"label"` + // Name of this instance, generated by the server. + // Optional + Name string `json:"name"` + // Networking information. + // Optional + Networking map[string]NetworkingInfo `json:"networking"` + // If set to true (default), then reverse DNS records are created. + // If set to false, no reverse DNS records are created. + // Optional + ReverseDNS bool `json:"reverse_dns,omitempty"` + // Type of instance, as defined on site configuration. + // Required + Shape string `json:"shape"` + // A list of the Storage Attachments you want to associate with the instance. + // Optional + Storage []StorageAttachmentInput `json:"storage_attachments,omitempty"` + // A list of the SSH public keys that you want to associate with the instance. + // Optional + SSHKeys []string `json:"sshkeys"` + // A list of tags to be supplied to the instance + // Optional + Tags []string `json:"tags"` + // Time to wait for an instance to be ready + Timeout time.Duration `json:"-"` +} + +type StorageAttachmentInput struct { + // The index number for the volume. The allowed range is 1 to 10. + // If you want to use a storage volume as the boot disk for an instance, you must specify the index number for that volume as 1. + // The index determines the device name by which the volume is exposed to the instance. + Index int `json:"index"` + // The three-part name (/Compute-identity_domain/user/object) of the storage volume that you want to attach to the instance. + // Note that volumes attached to an instance at launch time can't be detached. + Volume string `json:"volume"` +} + +const ReservationPrefix = "ipreservation" +const ReservationIPPrefix = "network/v1/ipreservation" + +type NICModel string + +const ( + NICDefaultModel NICModel = "e1000" +) + +// Struct of Networking info from a populated instance, or to be used as input to create an instance +type NetworkingInfo struct { + // The DNS name for the Shared network (Required) + // DNS A Record for an IP Network (Optional) + DNS []string `json:"dns,omitempty"` + // IP Network only. + // If you want to associate a static private IP Address, + // specify that here within the range of the supplied IPNetwork attribute. + // Optional + IPAddress string `json:"ip,omitempty"` + // IP Network only. + // The name of the IP Network you want to add the instance to. + // Required + IPNetwork string `json:"ipnetwork,omitempty"` + // IP Network only. + // Set interface as default gateway for all traffic + // Optional + IsDefaultGateway bool `json:"is_default_gateway,omitempty"` + // IP Network only. + // The hexadecimal MAC Address of the interface + // Optional + MACAddress string `json:"address,omitempty"` + // Shared Network only. + // The type of NIC used. Must be set to 'e1000' + // Required + Model NICModel `json:"model,omitempty"` + // IP Network and Shared Network + // The name servers that are sent through DHCP as option 6. + // You can specify a maximum of eight name server IP addresses per interface. + // Optional + NameServers []string `json:"name_servers,omitempty"` + // The names of an IP Reservation to associate in an IP Network (Optional) + // Indicates whether a temporary or permanent public IP Address should be assigned + // in a Shared Network (Required) + Nat []string `json:"nat,omitempty"` + // IP Network and Shared Network + // The search domains that should be sent through DHCP as option 119. + // You can enter a maximum of eight search domain zones per interface. + // Optional + SearchDomains []string `json:"search_domains,omitempty"` + // Shared Network only. + // The security lists that you want to add the instance to + // Required + SecLists []string `json:"seclists,omitempty"` + // IP Network Only + // The name of the vNIC + // Optional + Vnic string `json:"vnic,omitempty"` + // IP Network only. + // The names of the vNICSets you want to add the interface to. + // Optional + VnicSets []string `json:"vnicsets,omitempty"` +} + +// LaunchPlan defines a launch plan, used to launch instances with the supplied InstanceSpec(s) +type LaunchPlanInput struct { + // Describes an array of instances which should be launched + Instances []CreateInstanceInput `json:"instances"` + // Time to wait for instance boot + Timeout time.Duration `json:"-"` +} + +type LaunchPlanResponse struct { + // An array of instances which have been launched + Instances []InstanceInfo `json:"instances"` +} + +// LaunchInstance creates and submits a LaunchPlan to launch a new instance. +func (c *InstancesClient) CreateInstance(input *CreateInstanceInput) (*InstanceInfo, error) { + qualifiedSSHKeys := []string{} + for _, key := range input.SSHKeys { + qualifiedSSHKeys = append(qualifiedSSHKeys, c.getQualifiedName(key)) + } + + input.SSHKeys = qualifiedSSHKeys + + qualifiedStorageAttachments := []StorageAttachmentInput{} + for _, attachment := range input.Storage { + qualifiedStorageAttachments = append(qualifiedStorageAttachments, StorageAttachmentInput{ + Index: attachment.Index, + Volume: c.getQualifiedName(attachment.Volume), + }) + } + input.Storage = qualifiedStorageAttachments + + input.Networking = c.qualifyNetworking(input.Networking) + + input.Name = fmt.Sprintf(CMP_QUALIFIED_NAME, c.getUserName(), input.Name) + + plan := LaunchPlanInput{ + Instances: []CreateInstanceInput{*input}, + Timeout: input.Timeout, + } + + var ( + instanceInfo *InstanceInfo + instanceError error + ) + for i := 0; i < *c.ComputeClient.client.MaxRetries; i++ { + c.client.DebugLogString(fmt.Sprintf("(Iteration: %d of %d) Creating instance with name %s\n Plan: %+v", i, *c.ComputeClient.client.MaxRetries, input.Name, plan)) + + instanceInfo, instanceError = c.startInstance(input.Name, plan) + if instanceError == nil { + c.client.DebugLogString(fmt.Sprintf("(Iteration: %d of %d) Finished creating instance with name %s\n Info: %+v", i, *c.ComputeClient.client.MaxRetries, input.Name, instanceInfo)) + return instanceInfo, nil + } + } + return nil, instanceError +} + +func (c *InstancesClient) startInstance(name string, plan LaunchPlanInput) (*InstanceInfo, error) { + var responseBody LaunchPlanResponse + + if err := c.createResource(&plan, &responseBody); err != nil { + return nil, err + } + + if len(responseBody.Instances) == 0 { + return nil, fmt.Errorf("No instance information returned: %#v", responseBody) + } + + // Call wait for instance ready now, as creating the instance is an eventually consistent operation + getInput := &GetInstanceInput{ + Name: name, + ID: responseBody.Instances[0].ID, + } + + //timeout := WaitForInstanceReadyTimeout + if plan.Timeout == 0 { + plan.Timeout = WaitForInstanceReadyTimeout + } + + // Wait for instance to be ready and return the result + // Don't have to unqualify any objects, as the GetInstance method will handle that + instanceInfo, instanceError := c.WaitForInstanceRunning(getInput, plan.Timeout) + // If the instance enters an error state we need to delete the instance and retry + if instanceError != nil { + deleteInput := &DeleteInstanceInput{ + Name: name, + ID: responseBody.Instances[0].ID, + } + err := c.DeleteInstance(deleteInput) + if err != nil { + return nil, fmt.Errorf("Error deleting instance %s: %s", name, err) + } + return nil, instanceError + } + return instanceInfo, nil +} + +// Both of these fields are required. If they're not provided, things go wrong in +// incredibly amazing ways. +type GetInstanceInput struct { + // The Unqualified Name of this Instance + Name string + // The Unqualified ID of this Instance + ID string +} + +func (g *GetInstanceInput) String() string { + return fmt.Sprintf(CMP_QUALIFIED_NAME, g.Name, g.ID) +} + +// GetInstance retrieves information about an instance. +func (c *InstancesClient) GetInstance(input *GetInstanceInput) (*InstanceInfo, error) { + if input.ID == "" || input.Name == "" { + return nil, errors.New("Both instance name and ID need to be specified") + } + + var responseBody InstanceInfo + if err := c.getResource(input.String(), &responseBody); err != nil { + return nil, err + } + + if responseBody.Name == "" { + return nil, fmt.Errorf("Empty response body when requesting instance %s", input.Name) + } + + // The returned 'Name' attribute is the fully qualified instance name + "/" + ID + // Split these out to accurately populate the fields + nID := strings.Split(c.getUnqualifiedName(responseBody.Name), "/") + responseBody.Name = nID[0] + responseBody.ID = nID[1] + + c.unqualify(&responseBody.VCableID) + + // Unqualify SSH Key names + sshKeyNames := []string{} + for _, sshKeyRef := range responseBody.SSHKeys { + sshKeyNames = append(sshKeyNames, c.getUnqualifiedName(sshKeyRef)) + } + responseBody.SSHKeys = sshKeyNames + + var networkingErr error + responseBody.Networking, networkingErr = c.unqualifyNetworking(responseBody.Networking) + if networkingErr != nil { + return nil, networkingErr + } + responseBody.Storage = c.unqualifyStorage(responseBody.Storage) + + return &responseBody, nil +} + +type InstancesInfo struct { + Instances []InstanceInfo `json:"result"` +} + +type GetInstanceIdInput struct { + // Name of the instance you want to get + Name string +} + +// GetInstanceFromName loops through all the instances and finds the instance for the given name +// This is needed for orchestration since it doesn't return the id for the instance it creates. +func (c *InstancesClient) GetInstanceFromName(input *GetInstanceIdInput) (*InstanceInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var instancesInfo InstancesInfo + if err := c.getResource(fmt.Sprintf("%s/", c.getUserName()), &instancesInfo); err != nil { + return nil, err + } + + for _, i := range instancesInfo.Instances { + if strings.Contains(i.Name, input.Name) { + if i.Name == "" { + return nil, fmt.Errorf("Empty response body when requesting instance %s", input.Name) + } + + // The returned 'Name' attribute is the fully qualified instance name + "/" + ID + // Split these out to accurately populate the fields + nID := strings.Split(c.getUnqualifiedName(i.Name), "/") + i.Name = nID[0] + i.ID = nID[1] + + c.unqualify(&i.VCableID) + + // Unqualify SSH Key names + sshKeyNames := []string{} + for _, sshKeyRef := range i.SSHKeys { + sshKeyNames = append(sshKeyNames, c.getUnqualifiedName(sshKeyRef)) + } + i.SSHKeys = sshKeyNames + + var networkingErr error + i.Networking, networkingErr = c.unqualifyNetworking(i.Networking) + if networkingErr != nil { + return nil, networkingErr + } + i.Storage = c.unqualifyStorage(i.Storage) + + return &i, nil + } + } + + return nil, fmt.Errorf("Unable to find instance: %q", input.Name) +} + +type UpdateInstanceInput struct { + // Name of this instance, generated by the server. + // Required + Name string `json:"name"` + // The desired state of the opc instance. Can only be `running` or `shutdown` + // Omits if empty. + // Optional + DesiredState InstanceDesiredState `json:"desired_state,omitempty"` + // The ID of the instance + // Required + ID string `json:"-"` + // A list of tags to be supplied to the instance + // Optional + Tags []string `json:"tags,omitempty"` + // Time to wait for instance to be ready, or shutdown depending on desired state + Timeout time.Duration `json:"-"` +} + +func (g *UpdateInstanceInput) String() string { + return fmt.Sprintf(CMP_QUALIFIED_NAME, g.Name, g.ID) +} + +func (c *InstancesClient) UpdateInstance(input *UpdateInstanceInput) (*InstanceInfo, error) { + if input.Name == "" || input.ID == "" { + return nil, errors.New("Both instance name and ID need to be specified") + } + + input.Name = fmt.Sprintf(CMP_QUALIFIED_NAME, c.getUserName(), input.Name) + + var responseBody InstanceInfo + if err := c.updateResource(input.String(), input, &responseBody); err != nil { + return nil, err + } + + getInput := &GetInstanceInput{ + Name: input.Name, + ID: input.ID, + } + + if input.Timeout == 0 { + input.Timeout = WaitForInstanceReadyTimeout + } + + // Wait for the correct instance action depending on the current desired state. + // If the instance is already running, and the desired state is to be "running", the + // wait loop will only execute a single time to verify the instance state. Otherwise + // we wait until the correct action has finalized, either a shutdown or restart, catching + // any intermittent errors during the process. + if responseBody.DesiredState == InstanceDesiredRunning { + return c.WaitForInstanceRunning(getInput, input.Timeout) + } else { + return c.WaitForInstanceShutdown(getInput, input.Timeout) + } +} + +type DeleteInstanceInput struct { + // The Unqualified Name of this Instance + Name string + // The Unqualified ID of this Instance + ID string + // Time to wait for instance to be deleted + Timeout time.Duration +} + +func (d *DeleteInstanceInput) String() string { + return fmt.Sprintf(CMP_QUALIFIED_NAME, d.Name, d.ID) +} + +// DeleteInstance deletes an instance. +func (c *InstancesClient) DeleteInstance(input *DeleteInstanceInput) error { + // Call to delete the instance + if err := c.deleteResource(input.String()); err != nil { + return err + } + + if input.Timeout == 0 { + input.Timeout = WaitForInstanceDeleteTimeout + } + + // Wait for instance to be deleted + return c.WaitForInstanceDeleted(input, input.Timeout) +} + +// WaitForInstanceRunning waits for an instance to be completely initialized and available. +func (c *InstancesClient) WaitForInstanceRunning(input *GetInstanceInput, timeout time.Duration) (*InstanceInfo, error) { + var info *InstanceInfo + var getErr error + err := c.client.WaitFor("instance to be ready", timeout, func() (bool, error) { + info, getErr = c.GetInstance(input) + if getErr != nil { + return false, getErr + } + c.client.DebugLogString(fmt.Sprintf("Instance name is %v, Instance info is %+v", info.Name, info)) + switch s := info.State; s { + case InstanceError: + return false, fmt.Errorf("Error initializing instance: %s", info.ErrorReason) + case InstanceRunning: // Target State + c.client.DebugLogString("Instance Running") + return true, nil + case InstanceQueued: + c.client.DebugLogString("Instance Queuing") + return false, nil + case InstanceInitializing: + c.client.DebugLogString("Instance Initializing") + return false, nil + case InstancePreparing: + c.client.DebugLogString("Instance Preparing") + return false, nil + case InstanceStarting: + c.client.DebugLogString("Instance Starting") + return false, nil + default: + c.client.DebugLogString(fmt.Sprintf("Unknown instance state: %s, waiting", s)) + return false, nil + } + }) + return info, err +} + +// WaitForInstanceShutdown waits for an instance to be shutdown +func (c *InstancesClient) WaitForInstanceShutdown(input *GetInstanceInput, timeout time.Duration) (*InstanceInfo, error) { + var info *InstanceInfo + var getErr error + err := c.client.WaitFor("instance to be shutdown", timeout, func() (bool, error) { + info, getErr = c.GetInstance(input) + if getErr != nil { + return false, getErr + } + switch s := info.State; s { + case InstanceError: + return false, fmt.Errorf("Error initializing instance: %s", info.ErrorReason) + case InstanceRunning: + c.client.DebugLogString("Instance Running") + return false, nil + case InstanceQueued: + c.client.DebugLogString("Instance Queuing") + return false, nil + case InstanceInitializing: + c.client.DebugLogString("Instance Initializing") + return false, nil + case InstancePreparing: + c.client.DebugLogString("Instance Preparing") + return false, nil + case InstanceStarting: + c.client.DebugLogString("Instance Starting") + return false, nil + case InstanceShutdown: // Target State + c.client.DebugLogString("Instance Shutdown") + return true, nil + default: + c.client.DebugLogString(fmt.Sprintf("Unknown instance state: %s, waiting", s)) + return false, nil + } + }) + return info, err +} + +// WaitForInstanceDeleted waits for an instance to be fully deleted. +func (c *InstancesClient) WaitForInstanceDeleted(input *DeleteInstanceInput, timeout time.Duration) error { + return c.client.WaitFor("instance to be deleted", timeout, func() (bool, error) { + var info InstanceInfo + if err := c.getResource(input.String(), &info); err != nil { + if client.WasNotFoundError(err) { + // Instance could not be found, thus deleted + return true, nil + } + // Some other error occurred trying to get instance, exit + return false, err + } + switch s := info.State; s { + case InstanceError: + return false, fmt.Errorf("Error stopping instance: %s", info.ErrorReason) + case InstanceStopping: + c.client.DebugLogString("Instance stopping") + return false, nil + default: + c.client.DebugLogString(fmt.Sprintf("Unknown instance state: %s, waiting", s)) + return false, nil + } + }) +} + +func (c *InstancesClient) qualifyNetworking(info map[string]NetworkingInfo) map[string]NetworkingInfo { + qualifiedNetworks := map[string]NetworkingInfo{} + for k, v := range info { + qfd := v + sharedNetwork := false + if v.IPNetwork != "" { + // Network interface is for an IP Network + qfd.IPNetwork = c.getQualifiedName(v.IPNetwork) + sharedNetwork = true + } + if v.Vnic != "" { + qfd.Vnic = c.getQualifiedName(v.Vnic) + } + if v.Nat != nil { + qfd.Nat = c.qualifyNat(v.Nat, sharedNetwork) + } + if v.VnicSets != nil { + qfd.VnicSets = c.getQualifiedList(v.VnicSets) + } + if v.SecLists != nil { + // Network interface is for the shared network + secLists := []string{} + for _, v := range v.SecLists { + secLists = append(secLists, c.getQualifiedName(v)) + } + qfd.SecLists = secLists + } + + qualifiedNetworks[k] = qfd + } + return qualifiedNetworks +} + +func (c *InstancesClient) unqualifyNetworking(info map[string]NetworkingInfo) (map[string]NetworkingInfo, error) { + // Unqualify ip network + var err error + unqualifiedNetworks := map[string]NetworkingInfo{} + for k, v := range info { + unq := v + if v.IPNetwork != "" { + unq.IPNetwork = c.getUnqualifiedName(v.IPNetwork) + } + if v.Vnic != "" { + unq.Vnic = c.getUnqualifiedName(v.Vnic) + } + if v.Nat != nil { + unq.Nat, err = c.unqualifyNat(v.Nat) + if err != nil { + return nil, err + } + } + if v.VnicSets != nil { + unq.VnicSets = c.getUnqualifiedList(v.VnicSets) + } + if v.SecLists != nil { + secLists := []string{} + for _, v := range v.SecLists { + secLists = append(secLists, c.getUnqualifiedName(v)) + } + v.SecLists = secLists + } + unqualifiedNetworks[k] = unq + } + return unqualifiedNetworks, nil +} + +func (c *InstancesClient) qualifyNat(nat []string, shared bool) []string { + qualifiedNats := []string{} + for _, v := range nat { + if strings.HasPrefix(v, "ippool:/oracle") { + qualifiedNats = append(qualifiedNats, v) + continue + } + prefix := ReservationPrefix + if shared { + prefix = ReservationIPPrefix + } + qualifiedNats = append(qualifiedNats, fmt.Sprintf("%s:%s", prefix, c.getQualifiedName(v))) + } + return qualifiedNats +} + +func (c *InstancesClient) unqualifyNat(nat []string) ([]string, error) { + unQualifiedNats := []string{} + for _, v := range nat { + if strings.HasPrefix(v, "ippool:/oracle") { + unQualifiedNats = append(unQualifiedNats, v) + continue + } + n := strings.Split(v, ":") + if len(n) < 1 { + return nil, fmt.Errorf("Error unqualifying NAT: %s", v) + } + u := n[1] + unQualifiedNats = append(unQualifiedNats, c.getUnqualifiedName(u)) + } + return unQualifiedNats, nil +} + +func (c *InstancesClient) unqualifyStorage(attachments []StorageAttachment) []StorageAttachment { + unqAttachments := []StorageAttachment{} + for _, v := range attachments { + if v.StorageVolumeName != "" { + v.StorageVolumeName = c.getUnqualifiedName(v.StorageVolumeName) + } + unqAttachments = append(unqAttachments, v) + } + + return unqAttachments +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_associations.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_associations.go new file mode 100644 index 000000000..6c1167f12 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_associations.go @@ -0,0 +1,152 @@ +package compute + +const ( + IPAddressAssociationDescription = "ip address association" + IPAddressAssociationContainerPath = "/network/v1/ipassociation/" + IPAddressAssociationResourcePath = "/network/v1/ipassociation" +) + +type IPAddressAssociationsClient struct { + ResourceClient +} + +// IPAddressAssociations() returns an IPAddressAssociationsClient that can be used to access the +// necessary CRUD functions for IP Address Associations. +func (c *ComputeClient) IPAddressAssociations() *IPAddressAssociationsClient { + return &IPAddressAssociationsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: IPAddressAssociationDescription, + ContainerPath: IPAddressAssociationContainerPath, + ResourceRootPath: IPAddressAssociationResourcePath, + }, + } +} + +// IPAddressAssociationInfo contains the exported fields necessary to hold all the information about an +// IP Address Association +type IPAddressAssociationInfo struct { + // The name of the NAT IP address reservation. + IPAddressReservation string `json:"ipAddressReservation"` + // Name of the virtual NIC associated with this NAT IP reservation. + Vnic string `json:"vnic"` + // The name of the IP Address Association + Name string `json:"name"` + // Description of the IP Address Association + Description string `json:"description"` + // Slice of tags associated with the IP Address Association + Tags []string `json:"tags"` + // Uniform Resource Identifier for the IP Address Association + Uri string `json:"uri"` +} + +type CreateIPAddressAssociationInput struct { + // The name of the IP Address Association to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // The name of the NAT IP address reservation. + // Optional + IPAddressReservation string `json:"ipAddressReservation,omitempty"` + + // Name of the virtual NIC associated with this NAT IP reservation. + // Optional + Vnic string `json:"vnic,omitempty"` + + // Description of the IPAddressAssociation + // Optional + Description string `json:"description"` + + // String slice of tags to apply to the IP Address Association object + // Optional + Tags []string `json:"tags"` +} + +// Create a new IP Address Association from an IPAddressAssociationsClient and an input struct. +// Returns a populated Info struct for the IP Address Association, and any errors +func (c *IPAddressAssociationsClient) CreateIPAddressAssociation(input *CreateIPAddressAssociationInput) (*IPAddressAssociationInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.IPAddressReservation = c.getQualifiedName(input.IPAddressReservation) + input.Vnic = c.getQualifiedName(input.Vnic) + + var ipInfo IPAddressAssociationInfo + if err := c.createResource(&input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type GetIPAddressAssociationInput struct { + // The name of the IP Address Association to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated IPAddressAssociationInfo struct from an input struct +func (c *IPAddressAssociationsClient) GetIPAddressAssociation(input *GetIPAddressAssociationInput) (*IPAddressAssociationInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPAddressAssociationInfo + if err := c.getResource(input.Name, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +// UpdateIPAddressAssociationInput defines what to update in a ip address association +type UpdateIPAddressAssociationInput struct { + // The name of the IP Address Association to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // The name of the NAT IP address reservation. + // Optional + IPAddressReservation string `json:"ipAddressReservation,omitempty"` + + // Name of the virtual NIC associated with this NAT IP reservation. + // Optional + Vnic string `json:"vnic,omitempty"` + + // Description of the IPAddressAssociation + // Optional + Description string `json:"description"` + + // String slice of tags to apply to the IP Address Association object + // Optional + Tags []string `json:"tags"` +} + +// UpdateIPAddressAssociation update the ip address association +func (c *IPAddressAssociationsClient) UpdateIPAddressAssociation(updateInput *UpdateIPAddressAssociationInput) (*IPAddressAssociationInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + updateInput.IPAddressReservation = c.getQualifiedName(updateInput.IPAddressReservation) + updateInput.Vnic = c.getQualifiedName(updateInput.Vnic) + var ipInfo IPAddressAssociationInfo + if err := c.updateResource(updateInput.Name, updateInput, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type DeleteIPAddressAssociationInput struct { + // The name of the IP Address Association to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *IPAddressAssociationsClient) DeleteIPAddressAssociation(input *DeleteIPAddressAssociationInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the IPAddressAssociationInfo struct +func (c *IPAddressAssociationsClient) success(info *IPAddressAssociationInfo) (*IPAddressAssociationInfo, error) { + c.unqualify(&info.Name) + c.unqualify(&info.Vnic) + c.unqualify(&info.IPAddressReservation) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_prefix_set.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_prefix_set.go new file mode 100644 index 000000000..3f1503c08 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_prefix_set.go @@ -0,0 +1,135 @@ +package compute + +const ( + IPAddressPrefixSetDescription = "ip address prefix set" + IPAddressPrefixSetContainerPath = "/network/v1/ipaddressprefixset/" + IPAddressPrefixSetResourcePath = "/network/v1/ipaddressprefixset" +) + +type IPAddressPrefixSetsClient struct { + ResourceClient +} + +// IPAddressPrefixSets() returns an IPAddressPrefixSetsClient that can be used to access the +// necessary CRUD functions for IP Address Prefix Sets. +func (c *ComputeClient) IPAddressPrefixSets() *IPAddressPrefixSetsClient { + return &IPAddressPrefixSetsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: IPAddressPrefixSetDescription, + ContainerPath: IPAddressPrefixSetContainerPath, + ResourceRootPath: IPAddressPrefixSetResourcePath, + }, + } +} + +// IPAddressPrefixSetInfo contains the exported fields necessary to hold all the information about an +// IP Address Prefix Set +type IPAddressPrefixSetInfo struct { + // The name of the IP Address Prefix Set + Name string `json:"name"` + // Description of the IP Address Prefix Set + Description string `json:"description"` + // List of CIDR IPv4 prefixes assigned in the virtual network. + IPAddressPrefixes []string `json:"ipAddressPrefixes"` + // Slice of tags associated with the IP Address Prefix Set + Tags []string `json:"tags"` + // Uniform Resource Identifier for the IP Address Prefix Set + Uri string `json:"uri"` +} + +type CreateIPAddressPrefixSetInput struct { + // The name of the IP Address Prefix Set to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Description of the IPAddressPrefixSet + // Optional + Description string `json:"description"` + + // List of CIDR IPv4 prefixes assigned in the virtual network. + // Optional + IPAddressPrefixes []string `json:"ipAddressPrefixes"` + + // String slice of tags to apply to the IP Address Prefix Set object + // Optional + Tags []string `json:"tags"` +} + +// Create a new IP Address Prefix Set from an IPAddressPrefixSetsClient and an input struct. +// Returns a populated Info struct for the IP Address Prefix Set, and any errors +func (c *IPAddressPrefixSetsClient) CreateIPAddressPrefixSet(input *CreateIPAddressPrefixSetInput) (*IPAddressPrefixSetInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPAddressPrefixSetInfo + if err := c.createResource(&input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type GetIPAddressPrefixSetInput struct { + // The name of the IP Address Prefix Set to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated IPAddressPrefixSetInfo struct from an input struct +func (c *IPAddressPrefixSetsClient) GetIPAddressPrefixSet(input *GetIPAddressPrefixSetInput) (*IPAddressPrefixSetInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPAddressPrefixSetInfo + if err := c.getResource(input.Name, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +// UpdateIPAddressPrefixSetInput defines what to update in a ip address prefix set +type UpdateIPAddressPrefixSetInput struct { + // The name of the IP Address Prefix Set to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Description of the IPAddressPrefixSet + // Optional + Description string `json:"description"` + + // List of CIDR IPv4 prefixes assigned in the virtual network. + IPAddressPrefixes []string `json:"ipAddressPrefixes"` + + // String slice of tags to apply to the IP Address Prefix Set object + // Optional + Tags []string `json:"tags"` +} + +// UpdateIPAddressPrefixSet update the ip address prefix set +func (c *IPAddressPrefixSetsClient) UpdateIPAddressPrefixSet(updateInput *UpdateIPAddressPrefixSetInput) (*IPAddressPrefixSetInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + var ipInfo IPAddressPrefixSetInfo + if err := c.updateResource(updateInput.Name, updateInput, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type DeleteIPAddressPrefixSetInput struct { + // The name of the IP Address Prefix Set to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *IPAddressPrefixSetsClient) DeleteIPAddressPrefixSet(input *DeleteIPAddressPrefixSetInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the IPAddressPrefixSetInfo struct +func (c *IPAddressPrefixSetsClient) success(info *IPAddressPrefixSetInfo) (*IPAddressPrefixSetInfo, error) { + c.unqualify(&info.Name) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_reservations.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_reservations.go new file mode 100644 index 000000000..a1175711e --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_address_reservations.go @@ -0,0 +1,190 @@ +package compute + +import ( + "fmt" + "path/filepath" +) + +// IPAddressReservationsClient is a client to manage ip address reservation resources +type IPAddressReservationsClient struct { + *ResourceClient +} + +const ( + IPAddressReservationDescription = "IP Address Reservation" + IPAddressReservationContainerPath = "/network/v1/ipreservation/" + IPAddressReservationResourcePath = "/network/v1/ipreservation" + IPAddressReservationQualifier = "/oracle/public" +) + +// IPAddressReservations returns an IPAddressReservationsClient to manage IP address reservation +// resources +func (c *ComputeClient) IPAddressReservations() *IPAddressReservationsClient { + return &IPAddressReservationsClient{ + ResourceClient: &ResourceClient{ + ComputeClient: c, + ResourceDescription: IPAddressReservationDescription, + ContainerPath: IPAddressReservationContainerPath, + ResourceRootPath: IPAddressReservationResourcePath, + }, + } +} + +// IPAddressReservation describes an IP Address reservation +type IPAddressReservation struct { + // Description of the IP Address Reservation + Description string `json:"description"` + + // Reserved NAT IPv4 address from the IP Address Pool + IPAddress string `json:"ipAddress"` + + // Name of the IP Address pool to reserve the NAT IP from + IPAddressPool string `json:"ipAddressPool"` + + // Name of the reservation + Name string `json:"name"` + + // Tags associated with the object + Tags []string `json:"tags"` + + // Uniform Resource Identified for the reservation + Uri string `json:"uri"` +} + +const ( + PublicIPAddressPool = "public-ippool" + PrivateIPAddressPool = "cloud-ippool" +) + +// CreateIPAddressReservationInput defines input parameters to create an ip address reservation +type CreateIPAddressReservationInput struct { + // Description of the IP Address Reservation + // Optional + Description string `json:"description"` + + // IP Address pool from which to reserve an IP Address. + // Can be one of the following: + // + // 'public-ippool' - When you attach an IP Address from this pool to an instance, you enable + // access between the public Internet and the instance + // 'cloud-ippool' - When you attach an IP Address from this pool to an instance, the instance + // can communicate privately with other Oracle Cloud Services + // Optional + IPAddressPool string `json:"ipAddressPool"` + + // The name of the reservation to create + // Required + Name string `json:"name"` + + // Tags to associate with the IP Reservation + // Optional + Tags []string `json:"tags"` +} + +// Takes an input struct, creates an IP Address reservation, and returns the info struct and any errors +func (c *IPAddressReservationsClient) CreateIPAddressReservation(input *CreateIPAddressReservationInput) (*IPAddressReservation, error) { + var ipAddrRes IPAddressReservation + // Qualify supplied name + input.Name = c.getQualifiedName(input.Name) + // Qualify supplied address pool if not nil + if input.IPAddressPool != "" { + input.IPAddressPool = c.qualifyIPAddressPool(input.IPAddressPool) + } + + if err := c.createResource(input, &ipAddrRes); err != nil { + return nil, err + } + + return c.success(&ipAddrRes) +} + +// Parameters to retrieve information on an ip address reservation +type GetIPAddressReservationInput struct { + // Name of the IP Reservation + // Required + Name string `json:"name"` +} + +// Returns an IP Address Reservation and any errors +func (c *IPAddressReservationsClient) GetIPAddressReservation(input *GetIPAddressReservationInput) (*IPAddressReservation, error) { + var ipAddrRes IPAddressReservation + + input.Name = c.getQualifiedName(input.Name) + if err := c.getResource(input.Name, &ipAddrRes); err != nil { + return nil, err + } + + return c.success(&ipAddrRes) +} + +// Parameters to update an IP Address reservation +type UpdateIPAddressReservationInput struct { + // Description of the IP Address Reservation + // Optional + Description string `json:"description"` + + // IP Address pool from which to reserve an IP Address. + // Can be one of the following: + // + // 'public-ippool' - When you attach an IP Address from this pool to an instance, you enable + // access between the public Internet and the instance + // 'cloud-ippool' - When you attach an IP Address from this pool to an instance, the instance + // can communicate privately with other Oracle Cloud Services + // Optional + IPAddressPool string `json:"ipAddressPool"` + + // The name of the reservation to create + // Required + Name string `json:"name"` + + // Tags to associate with the IP Reservation + // Optional + Tags []string `json:"tags"` +} + +func (c *IPAddressReservationsClient) UpdateIPAddressReservation(input *UpdateIPAddressReservationInput) (*IPAddressReservation, error) { + var ipAddrRes IPAddressReservation + + // Qualify supplied name + input.Name = c.getQualifiedName(input.Name) + // Qualify supplied address pool if not nil + if input.IPAddressPool != "" { + input.IPAddressPool = c.qualifyIPAddressPool(input.IPAddressPool) + } + + if err := c.updateResource(input.Name, input, &ipAddrRes); err != nil { + return nil, err + } + + return c.success(&ipAddrRes) +} + +// Parameters to delete an IP Address Reservation +type DeleteIPAddressReservationInput struct { + // The name of the reservation to delete + Name string `json:"name"` +} + +func (c *IPAddressReservationsClient) DeleteIPAddressReservation(input *DeleteIPAddressReservationInput) error { + input.Name = c.getQualifiedName(input.Name) + return c.deleteResource(input.Name) +} + +func (c *IPAddressReservationsClient) success(result *IPAddressReservation) (*IPAddressReservation, error) { + c.unqualify(&result.Name) + if result.IPAddressPool != "" { + result.IPAddressPool = c.unqualifyIPAddressPool(result.IPAddressPool) + } + + return result, nil +} + +func (c *IPAddressReservationsClient) qualifyIPAddressPool(input string) string { + // Add '/oracle/public/' + return fmt.Sprintf("%s/%s", IPAddressReservationQualifier, input) +} + +func (c *IPAddressReservationsClient) unqualifyIPAddressPool(input string) string { + // Remove '/oracle/public/' + return filepath.Base(input) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_associations.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_associations.go new file mode 100644 index 000000000..09e194985 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_associations.go @@ -0,0 +1,118 @@ +package compute + +import ( + "fmt" + "strings" +) + +// IPAssociationsClient is a client for the IP Association functions of the Compute API. +type IPAssociationsClient struct { + *ResourceClient +} + +// IPAssociations obtains a IPAssociationsClient which can be used to access to the +// IP Association functions of the Compute API +func (c *ComputeClient) IPAssociations() *IPAssociationsClient { + return &IPAssociationsClient{ + ResourceClient: &ResourceClient{ + ComputeClient: c, + ResourceDescription: "ip association", + ContainerPath: "/ip/association/", + ResourceRootPath: "/ip/association", + }} +} + +// IPAssociationInfo describes an existing IP association. +type IPAssociationInfo struct { + // TODO: it'd probably make sense to expose the `ip` field here too? + + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + + // The three-part name of the IP reservation object in the format (/Compute-identity_domain/user/object). + // An IP reservation is a public IP address which is attached to an Oracle Compute Cloud Service instance that requires access to or from the Internet. + Reservation string `json:"reservation"` + + // The type of IP Address to associate with this instance + // for a Dynamic IP address specify `ippool:/oracle/public/ippool`. + // for a Static IP address specify the three part name of the existing IP reservation + ParentPool string `json:"parentpool"` + + // Uniform Resource Identifier for the IP Association + URI string `json:"uri"` + + // The three-part name of a vcable ID of an instance that is associated with the IP reservation. + VCable string `json:"vcable"` +} + +type CreateIPAssociationInput struct { + // The type of IP Address to associate with this instance + // for a Dynamic IP address specify `ippool:/oracle/public/ippool`. + // for a Static IP address specify the three part name of the existing IP reservation + // Required + ParentPool string `json:"parentpool"` + + // The three-part name of the vcable ID of the instance that you want to associate with an IP address. The three-part name is in the format: /Compute-identity_domain/user/object. + // Required + VCable string `json:"vcable"` +} + +// CreateIPAssociation creates a new IP association with the supplied vcable and parentpool. +func (c *IPAssociationsClient) CreateIPAssociation(input *CreateIPAssociationInput) (*IPAssociationInfo, error) { + input.VCable = c.getQualifiedName(input.VCable) + input.ParentPool = c.getQualifiedParentPoolName(input.ParentPool) + var assocInfo IPAssociationInfo + if err := c.createResource(input, &assocInfo); err != nil { + return nil, err + } + + return c.success(&assocInfo) +} + +type GetIPAssociationInput struct { + // The three-part name of the IP Association + // Required. + Name string `json:"name"` +} + +// GetIPAssociation retrieves the IP association with the given name. +func (c *IPAssociationsClient) GetIPAssociation(input *GetIPAssociationInput) (*IPAssociationInfo, error) { + var assocInfo IPAssociationInfo + if err := c.getResource(input.Name, &assocInfo); err != nil { + return nil, err + } + + return c.success(&assocInfo) +} + +type DeleteIPAssociationInput struct { + // The three-part name of the IP Association + // Required. + Name string `json:"name"` +} + +// DeleteIPAssociation deletes the IP association with the given name. +func (c *IPAssociationsClient) DeleteIPAssociation(input *DeleteIPAssociationInput) error { + return c.deleteResource(input.Name) +} + +func (c *IPAssociationsClient) getQualifiedParentPoolName(parentpool string) string { + parts := strings.Split(parentpool, ":") + pooltype := parts[0] + name := parts[1] + return fmt.Sprintf("%s:%s", pooltype, c.getQualifiedName(name)) +} + +func (c *IPAssociationsClient) unqualifyParentPoolName(parentpool *string) { + parts := strings.Split(*parentpool, ":") + pooltype := parts[0] + name := parts[1] + *parentpool = fmt.Sprintf("%s:%s", pooltype, c.getUnqualifiedName(name)) +} + +// Unqualifies identifiers +func (c *IPAssociationsClient) success(assocInfo *IPAssociationInfo) (*IPAssociationInfo, error) { + c.unqualify(&assocInfo.Name, &assocInfo.VCable) + c.unqualifyParentPoolName(&assocInfo.ParentPool) + return assocInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_network_exchange.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_network_exchange.go new file mode 100644 index 000000000..1df33f296 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_network_exchange.go @@ -0,0 +1,99 @@ +package compute + +const ( + IPNetworkExchangeDescription = "ip network exchange" + IPNetworkExchangeContainerPath = "/network/v1/ipnetworkexchange/" + IPNetworkExchangeResourcePath = "/network/v1/ipnetworkexchange" +) + +type IPNetworkExchangesClient struct { + ResourceClient +} + +// IPNetworkExchanges() returns an IPNetworkExchangesClient that can be used to access the +// necessary CRUD functions for IP Network Exchanges. +func (c *ComputeClient) IPNetworkExchanges() *IPNetworkExchangesClient { + return &IPNetworkExchangesClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: IPNetworkExchangeDescription, + ContainerPath: IPNetworkExchangeContainerPath, + ResourceRootPath: IPNetworkExchangeResourcePath, + }, + } +} + +// IPNetworkExchangeInfo contains the exported fields necessary to hold all the information about an +// IP Network Exchange +type IPNetworkExchangeInfo struct { + // The name of the IP Network Exchange + Name string `json:"name"` + // Description of the IP Network Exchange + Description string `json:"description"` + // Slice of tags associated with the IP Network Exchange + Tags []string `json:"tags"` + // Uniform Resource Identifier for the IP Network Exchange + Uri string `json:"uri"` +} + +type CreateIPNetworkExchangeInput struct { + // The name of the IP Network Exchange to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Description of the IPNetworkExchange + // Optional + Description string `json:"description"` + + // String slice of tags to apply to the IP Network Exchange object + // Optional + Tags []string `json:"tags"` +} + +// Create a new IP Network Exchange from an IPNetworkExchangesClient and an input struct. +// Returns a populated Info struct for the IP Network Exchange, and any errors +func (c *IPNetworkExchangesClient) CreateIPNetworkExchange(input *CreateIPNetworkExchangeInput) (*IPNetworkExchangeInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPNetworkExchangeInfo + if err := c.createResource(&input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type GetIPNetworkExchangeInput struct { + // The name of the IP Network Exchange to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated IPNetworkExchangeInfo struct from an input struct +func (c *IPNetworkExchangesClient) GetIPNetworkExchange(input *GetIPNetworkExchangeInput) (*IPNetworkExchangeInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPNetworkExchangeInfo + if err := c.getResource(input.Name, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type DeleteIPNetworkExchangeInput struct { + // The name of the IP Network Exchange to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *IPNetworkExchangesClient) DeleteIPNetworkExchange(input *DeleteIPNetworkExchangeInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the IPNetworkExchangeInfo struct +func (c *IPNetworkExchangesClient) success(info *IPNetworkExchangeInfo) (*IPNetworkExchangeInfo, error) { + c.unqualify(&info.Name) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_networks.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_networks.go new file mode 100644 index 000000000..bfc324845 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_networks.go @@ -0,0 +1,186 @@ +package compute + +const ( + IPNetworkDescription = "ip network" + IPNetworkContainerPath = "/network/v1/ipnetwork/" + IPNetworkResourcePath = "/network/v1/ipnetwork" +) + +type IPNetworksClient struct { + ResourceClient +} + +// IPNetworks() returns an IPNetworksClient that can be used to access the +// necessary CRUD functions for IP Networks. +func (c *ComputeClient) IPNetworks() *IPNetworksClient { + return &IPNetworksClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: IPNetworkDescription, + ContainerPath: IPNetworkContainerPath, + ResourceRootPath: IPNetworkResourcePath, + }, + } +} + +// IPNetworkInfo contains the exported fields necessary to hold all the information about an +// IP Network +type IPNetworkInfo struct { + // The name of the IP Network + Name string `json:"name"` + // The CIDR IPv4 prefix associated with the IP Network + IPAddressPrefix string `json:"ipAddressPrefix"` + // Name of the IP Network Exchange associated with the IP Network + IPNetworkExchange string `json:"ipNetworkExchange,omitempty"` + // Description of the IP Network + Description string `json:"description"` + // Whether public internet access was enabled using NAPT for VNICs without any public IP reservation + PublicNaptEnabled bool `json:"publicNaptEnabledFlag"` + // Slice of tags associated with the IP Network + Tags []string `json:"tags"` + // Uniform Resource Identifier for the IP Network + Uri string `json:"uri"` +} + +type CreateIPNetworkInput struct { + // The name of the IP Network to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Specify the size of the IP Subnet. It is a range of IPv4 addresses assigned in the virtual + // network, in CIDR address prefix format. + // While specifying the IP address prefix take care of the following points: + // + //* These IP addresses aren't part of the common pool of Oracle-provided IP addresses used by the shared network. + // + //* There's no conflict with the range of IP addresses used in another IP network, the IP addresses used your on-premises network, or with the range of private IP addresses used in the shared network. If IP networks with overlapping IP subnets are linked to an IP exchange, packets going to and from those IP networks are dropped. + // + //* The upper limit of the CIDR block size for an IP network is /16. + // + //Note: The first IP address of any IP network is reserved for the default gateway, the DHCP server, and the DNS server of that IP network. + // Required + IPAddressPrefix string `json:"ipAddressPrefix"` + + //Specify the IP network exchange to which the IP network belongs. + //You can add an IP network to only one IP network exchange, but an IP network exchange + //can include multiple IP networks. An IP network exchange enables access between IP networks + //that have non-overlapping addresses, so that instances on these networks can exchange packets + //with each other without NAT. + // Optional + IPNetworkExchange string `json:"ipNetworkExchange,omitempty"` + + // Description of the IPNetwork + // Optional + Description string `json:"description"` + + // Enable public internet access using NAPT for VNICs without any public IP reservation + // Optional + PublicNaptEnabled bool `json:"publicNaptEnabledFlag"` + + // String slice of tags to apply to the IP Network object + // Optional + Tags []string `json:"tags"` +} + +// Create a new IP Network from an IPNetworksClient and an input struct. +// Returns a populated Info struct for the IP Network, and any errors +func (c *IPNetworksClient) CreateIPNetwork(input *CreateIPNetworkInput) (*IPNetworkInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.IPNetworkExchange = c.getQualifiedName(input.IPNetworkExchange) + + var ipInfo IPNetworkInfo + if err := c.createResource(&input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type GetIPNetworkInput struct { + // The name of the IP Network to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated IPNetworkInfo struct from an input struct +func (c *IPNetworksClient) GetIPNetwork(input *GetIPNetworkInput) (*IPNetworkInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo IPNetworkInfo + if err := c.getResource(input.Name, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type UpdateIPNetworkInput struct { + // The name of the IP Network to update. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Specify the size of the IP Subnet. It is a range of IPv4 addresses assigned in the virtual + // network, in CIDR address prefix format. + // While specifying the IP address prefix take care of the following points: + // + //* These IP addresses aren't part of the common pool of Oracle-provided IP addresses used by the shared network. + // + //* There's no conflict with the range of IP addresses used in another IP network, the IP addresses used your on-premises network, or with the range of private IP addresses used in the shared network. If IP networks with overlapping IP subnets are linked to an IP exchange, packets going to and from those IP networks are dropped. + // + //* The upper limit of the CIDR block size for an IP network is /16. + // + //Note: The first IP address of any IP network is reserved for the default gateway, the DHCP server, and the DNS server of that IP network. + // Required + IPAddressPrefix string `json:"ipAddressPrefix"` + + //Specify the IP network exchange to which the IP network belongs. + //You can add an IP network to only one IP network exchange, but an IP network exchange + //can include multiple IP networks. An IP network exchange enables access between IP networks + //that have non-overlapping addresses, so that instances on these networks can exchange packets + //with each other without NAT. + // Optional + IPNetworkExchange string `json:"ipNetworkExchange,omitempty"` + + // Description of the IPNetwork + // Optional + Description string `json:"description"` + + // Enable public internet access using NAPT for VNICs without any public IP reservation + // Optional + PublicNaptEnabled bool `json:"publicNaptEnabledFlag"` + + // String slice of tags to apply to the IP Network object + // Optional + Tags []string `json:"tags"` +} + +func (c *IPNetworksClient) UpdateIPNetwork(input *UpdateIPNetworkInput) (*IPNetworkInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.IPNetworkExchange = c.getQualifiedName(input.IPNetworkExchange) + + var ipInfo IPNetworkInfo + if err := c.updateResource(input.Name, &input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type DeleteIPNetworkInput struct { + // The name of the IP Network to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *IPNetworksClient) DeleteIPNetwork(input *DeleteIPNetworkInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the IPNetworkInfo struct +func (c *IPNetworksClient) success(info *IPNetworkInfo) (*IPNetworkInfo, error) { + c.unqualify(&info.Name) + c.unqualify(&info.IPNetworkExchange) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_reservations.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_reservations.go new file mode 100644 index 000000000..42f9f1741 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ip_reservations.go @@ -0,0 +1,147 @@ +package compute + +// IPReservationsClient is a client for the IP Reservations functions of the Compute API. +type IPReservationsClient struct { + *ResourceClient +} + +const ( + IPReservationDesc = "ip reservation" + IPReservationContainerPath = "/ip/reservation/" + IPReservataionResourcePath = "/ip/reservation" +) + +// IPReservations obtains an IPReservationsClient which can be used to access to the +// IP Reservations functions of the Compute API +func (c *ComputeClient) IPReservations() *IPReservationsClient { + return &IPReservationsClient{ + ResourceClient: &ResourceClient{ + ComputeClient: c, + ResourceDescription: IPReservationDesc, + ContainerPath: IPReservationContainerPath, + ResourceRootPath: IPReservataionResourcePath, + }} +} + +type IPReservationPool string + +const ( + PublicReservationPool IPReservationPool = "/oracle/public/ippool" +) + +// IPReservationInput describes an existing IP reservation. +type IPReservation struct { + // Shows the default account for your identity domain. + Account string `json:"account"` + // Public IP address. + IP string `json:"ip"` + // The three-part name of the IP Reservation (/Compute-identity_domain/user/object). + Name string `json:"name"` + // Pool of public IP addresses + ParentPool IPReservationPool `json:"parentpool"` + // Is the IP Reservation Persistent (i.e. static) or not (i.e. Dynamic)? + Permanent bool `json:"permanent"` + // A comma-separated list of strings which helps you to identify IP reservation. + Tags []string `json:"tags"` + // Uniform Resource Identifier + Uri string `json:"uri"` + // Is the IP reservation associated with an instance? + Used bool `json:"used"` +} + +// CreateIPReservationInput defines an IP reservation to be created. +type CreateIPReservationInput struct { + // The name of the object + // If you don't specify a name for this object, then the name is generated automatically. + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. + // Object names are case-sensitive. + // Optional + Name string `json:"name"` + // Pool of public IP addresses. This must be set to `ippool` + // Required + ParentPool IPReservationPool `json:"parentpool"` + // Is the IP Reservation Persistent (i.e. static) or not (i.e. Dynamic)? + // Required + Permanent bool `json:"permanent"` + // A comma-separated list of strings which helps you to identify IP reservations. + // Optional + Tags []string `json:"tags"` +} + +// CreateIPReservation creates a new IP reservation with the given parentpool, tags and permanent flag. +func (c *IPReservationsClient) CreateIPReservation(input *CreateIPReservationInput) (*IPReservation, error) { + var ipInput IPReservation + + input.Name = c.getQualifiedName(input.Name) + if err := c.createResource(input, &ipInput); err != nil { + return nil, err + } + + return c.success(&ipInput) +} + +// GetIPReservationInput defines an IP Reservation to get +type GetIPReservationInput struct { + // The name of the IP Reservation + // Required + Name string +} + +// GetIPReservation retrieves the IP reservation with the given name. +func (c *IPReservationsClient) GetIPReservation(input *GetIPReservationInput) (*IPReservation, error) { + var ipInput IPReservation + + input.Name = c.getQualifiedName(input.Name) + if err := c.getResource(input.Name, &ipInput); err != nil { + return nil, err + } + + return c.success(&ipInput) +} + +// UpdateIPReservationInput defines an IP Reservation to be updated +type UpdateIPReservationInput struct { + // The name of the object + // If you don't specify a name for this object, then the name is generated automatically. + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. + // Object names are case-sensitive. + // Required + Name string `json:"name"` + // Pool of public IP addresses. + // Required + ParentPool IPReservationPool `json:"parentpool"` + // Is the IP Reservation Persistent (i.e. static) or not (i.e. Dynamic)? + // Required + Permanent bool `json:"permanent"` + // A comma-separated list of strings which helps you to identify IP reservations. + // Optional + Tags []string `json:"tags"` +} + +// UpdateIPReservation updates the IP reservation. +func (c *IPReservationsClient) UpdateIPReservation(input *UpdateIPReservationInput) (*IPReservation, error) { + var updateOutput IPReservation + input.Name = c.getQualifiedName(input.Name) + if err := c.updateResource(input.Name, input, &updateOutput); err != nil { + return nil, err + } + return c.success(&updateOutput) +} + +// DeleteIPReservationInput defines an IP Reservation to delete +type DeleteIPReservationInput struct { + // The name of the IP Reservation + // Required + Name string +} + +// DeleteIPReservation deletes the IP reservation with the given name. +func (c *IPReservationsClient) DeleteIPReservation(input *DeleteIPReservationInput) error { + input.Name = c.getQualifiedName(input.Name) + return c.deleteResource(input.Name) +} + +func (c *IPReservationsClient) success(result *IPReservation) (*IPReservation, error) { + c.unqualify(&result.Name) + return result, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/machine_images.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/machine_images.go new file mode 100644 index 000000000..bf7736a63 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/machine_images.go @@ -0,0 +1,143 @@ +package compute + +// MachineImagesClient is a client for the MachineImage functions of the Compute API. +type MachineImagesClient struct { + ResourceClient +} + +// MachineImages obtains an MachineImagesClient which can be used to access to the +// MachineImage functions of the Compute API +func (c *ComputeClient) MachineImages() *MachineImagesClient { + return &MachineImagesClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "MachineImage", + ContainerPath: "/machineimage/", + ResourceRootPath: "/machineimage", + }} +} + +// MahineImage describes an existing Machine Image. +type MachineImage struct { + // account of the associated Object Storage Classic instance + Account string `json:"account"` + + // Dictionary of attributes to be made available to the instance + Attributes map[string]interface{} `json:"attributes"` + + // Last time when this image was audited + Audited string `json:"audited"` + + // Describing the image + Description string `json:"description"` + + // Description of the state of the machine image if there is an error + ErrorReason string `json:"error_reason"` + + // dictionary of hypervisor-specific attributes + Hypervisor map[string]interface{} `json:"hypervisor"` + + // The format of the image + ImageFormat string `json:"image_format"` + + // name of the machine image file uploaded to Object Storage Classic + File string `json:"file"` + + // name of the machine image + Name string `json:"name"` + + // Indicates that the image file is available in Object Storage Classic + NoUpload bool `json:"no_upload"` + + // The OS platform of the image + Platform string `json:"platform"` + + // Size values of the image file + Sizes map[string]interface{} `json:"sizes"` + + // The state of the uploaded machine image + State string `json:"state"` + + // Uniform Resource Identifier + URI string `json:"uri"` +} + +// CreateMachineImageInput defines an Image List to be created. +type CreateMachineImageInput struct { + // account of the associated Object Storage Classic instance + Account string `json:"account"` + + // Dictionary of attributes to be made available to the instance + Attributes map[string]interface{} `json:"attributes,omitempty"` + + // Describing the image + Description string `json:"description,omitempty"` + + // name of the machine image file uploaded to Object Storage Classic + File string `json:"file,omitempty"` + + // name of the machine image + Name string `json:"name"` + + // Indicates that the image file is available in Object Storage Classic + NoUpload bool `json:"no_upload"` + + // Size values of the image file + Sizes map[string]interface{} `json:"sizes"` +} + +// CreateMachineImage creates a new Machine Image with the given parameters. +func (c *MachineImagesClient) CreateMachineImage(createInput *CreateMachineImageInput) (*MachineImage, error) { + var machineImage MachineImage + + // If `sizes` is not set then is mst be defaulted to {"total": 0} + if createInput.Sizes == nil { + createInput.Sizes = map[string]interface{}{"total": 0} + } + + // `no_upload` must always be true + createInput.NoUpload = true + + createInput.Name = c.getQualifiedName(createInput.Name) + if err := c.createResource(createInput, &machineImage); err != nil { + return nil, err + } + + return c.success(&machineImage) +} + +// DeleteMachineImageInput describes the MachineImage to delete +type DeleteMachineImageInput struct { + // The name of the MachineImage + Name string `json:name` +} + +// DeleteMachineImage deletes the MachineImage with the given name. +func (c *MachineImagesClient) DeleteMachineImage(deleteInput *DeleteMachineImageInput) error { + return c.deleteResource(deleteInput.Name) +} + +// GetMachineList describes the MachineImage to get +type GetMachineImageInput struct { + // account of the associated Object Storage Classic instance + Account string `json:"account"` + // The name of the Machine Image + Name string `json:name` +} + +// GetMachineImage retrieves the MachineImage with the given name. +func (c *MachineImagesClient) GetMachineImage(getInput *GetMachineImageInput) (*MachineImage, error) { + getInput.Name = c.getQualifiedName(getInput.Name) + + var machineImage MachineImage + if err := c.getResource(getInput.Name, &machineImage); err != nil { + return nil, err + } + + return c.success(&machineImage) +} + +func (c *MachineImagesClient) success(result *MachineImage) (*MachineImage, error) { + c.unqualify(&result.Name) + return result, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/orchestration.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/orchestration.go new file mode 100644 index 000000000..684e0d45f --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/orchestration.go @@ -0,0 +1,451 @@ +package compute + +import ( + "fmt" + "time" + + "github.com/hashicorp/go-oracle-terraform/client" +) + +const WaitForOrchestrationActiveTimeout = time.Duration(3600 * time.Second) +const WaitForOrchestrationDeleteTimeout = time.Duration(3600 * time.Second) + +// OrchestrationsClient is a client for the Orchestration functions of the Compute API. +type OrchestrationsClient struct { + ResourceClient +} + +// Orchestrations obtains an OrchestrationsClient which can be used to access to the +// Orchestration functions of the Compute API +func (c *ComputeClient) Orchestrations() *OrchestrationsClient { + return &OrchestrationsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "Orchestration", + ContainerPath: "/platform/v1/orchestration/", + ResourceRootPath: "/platform/v1/orchestration", + }} +} + +type OrchestrationDesiredState string + +const ( + // * active: Creates all the orchestration objects defined in the orchestration. + OrchestrationDesiredStateActive OrchestrationDesiredState = "active" + // * inactive: Adds the orchestration to Oracle Compute Cloud Service, but does not create any of the orchestration + OrchestrationDesiredStateInactive OrchestrationDesiredState = "inactive" + // * suspended: Suspends all orchestration objects defined in the orchestration + OrchestrationDesiredStateSuspend OrchestrationDesiredState = "suspend" +) + +type OrchestrationStatus string + +const ( + OrchestrationStatusActive OrchestrationStatus = "active" + OrchestrationStatusInactive OrchestrationStatus = "inactive" + OrchestrationStatusSuspend OrchestrationStatus = "suspend" + OrchestrationStatusActivating OrchestrationStatus = "activating" + OrchestrationStatusDeleting OrchestrationStatus = "deleting" + OrchestrationStatusError OrchestrationStatus = "terminal_error" + OrchestrationStatusStopping OrchestrationStatus = "stopping" + OrchestrationStatusSuspending OrchestrationStatus = "suspending" + OrchestrationStatusStarting OrchestrationStatus = "starting" + OrchestrationStatusDeactivating OrchestrationStatus = "deactivating" + OrchestrationStatusSuspended OrchestrationStatus = "suspended" +) + +type OrchestrationType string + +const ( + OrchestrationTypeInstance OrchestrationType = "Instance" +) + +// OrchestrationInfo describes an existing Orchestration. +type Orchestration struct { + // The default Oracle Compute Cloud Service account, such as /Compute-acme/default. + Account string `json:"account"` + // Description of this orchestration + Description string `json:"description"` + // The desired_state specified in the orchestration JSON file. A unique identifier for this orchestration. + DesiredState OrchestrationDesiredState `json:"desired_state"` + // Unique identifier of this orchestration + ID string `json:"id"` + // The three-part name of the Orchestration (/Compute-identity_domain/user/object). + Name string `json:"name"` + // List of orchestration objects + Objects []Object `json:"objects"` + // Current status of this orchestration + Status OrchestrationStatus `json:"status"` + // Strings that describe the orchestration and help you identify it. + Tags []string `json:"tags"` + // Time the orchestration was last audited + TimeAudited string `json:"time_audited"` + // The time when the orchestration was added to Oracle Compute Cloud Service. + TimeCreated string `json:"time_created"` + // The time when the orchestration was last updated in Oracle Compute Cloud Service. + TimeUpdated string `json:"time_updated"` + // Unique Resource Identifier + URI string `json:"uri"` + // Name of the user who added this orchestration or made the most recent update to this orchestration. + User string `json:"user"` + // Version of this orchestration. It is automatically generated by the server. + Version int `json:"version"` +} + +// CreateOrchestrationInput defines an Orchestration to be created. +type CreateOrchestrationInput struct { + // The default Oracle Compute Cloud Service account, such as /Compute-acme/default. + // Optional + Account string `json:"account,omitempty"` + // Description of this orchestration + // Optional + Description string `json:"description,omitempty"` + // Specify the desired state of this orchestration: active, inactive, or suspend. + // You can manage the state of the orchestration objects by changing the desired state of the orchestration. + // * active: Creates all the orchestration objects defined in the orchestration. + // * inactive: Adds the orchestration to Oracle Compute Cloud Service, but does not create any of the orchestration + // objects defined in the orchestration. + // Required + DesiredState OrchestrationDesiredState `json:"desired_state"` + // The three-part name of the Orchestration (/Compute-identity_domain/user/object). + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` + // The list of objects in the orchestration. An object is the primary building block of an orchestration. + // An orchestration can contain up to 100 objects. + // Required + Objects []Object `json:"objects"` + // Strings that describe the orchestration and help you identify it. + Tags []string `json:"tags,omitempty"` + // Version of this orchestration. It is automatically generated by the server. + Version int `json:"version,omitempty"` + // Time to wait for an orchestration to be ready + Timeout time.Duration `json:"-"` +} + +type Object struct { + // The default Oracle Compute Cloud Service account, such as /Compute-acme/default. + // Optional + Account string `json:"account,omitempty"` + // Description of this orchestration + // Optional + Description string `json:"description,omitempty"` + // The desired state of the object + // Optional + DesiredState OrchestrationDesiredState `json:"desired_state,omitempty"` + // Dictionary containing the current state of the object + Health Health `json:"health,omitempty"` + // A text string describing the object. Labels can't include spaces. In an orchestration, the label for + // each object must be unique. Maximum length is 256 characters. + // Required + Label string `json:"label"` + // The four-part name of the object (/Compute-identity_domain/user/orchestration/object). If you don't specify a name + // for this object, the name is generated automatically. Object names can contain only alphanumeric characters, hyphens, + // underscores, and periods. Object names are case-sensitive. When you specify the object name, ensure that an object of + // the same type and with the same name doesn't already exist. If such a object already exists, then another + // object of the same type and with the same name won't be created and the existing object won't be updated. + // Optional + Name string `json:"name,omitempty"` + // The three-part name (/Compute-identity_domain/user/object) of the orchestration to which the object belongs. + // Required + Orchestration string `json:"orchestration"` + // Specifies whether the object should persist when the orchestration is suspended. Specify one of the following: + // * true: The object persists when the orchestration is suspended. + // * false: The object is deleted when the orchestration is suspended. + // By default, persistent is set to false. It is recommended that you specify true for storage + // volumes and other critical objects. Persistence applies only when you're suspending an orchestration. + // When you terminate an orchestration, all the objects defined in it are deleted. + // Optional + Persistent bool `json:"persistent,omitempty"` + // The relationship between the objects that are created by this orchestration. The + // only supported relationship is depends, indicating that the specified target objects must be created first. + // Note that when recovering from a failure, the orchestration doesn't consider object relationships. + // Orchestrations v2 use object references to recover interdependent objects to a healthy state. SeeObject + // References and Relationships in Using Oracle Compute Cloud Service (IaaS). + Relationship []Object `json:"relationships,omitempty"` + // The template attribute defines the properties or characteristics of the Oracle Compute Cloud Service object + // that you want to create, as specified by the type attribute. + // The fields in the template section vary depending on the specified type. See Orchestration v2 Attributes + // Specific to Each Object Type in Using Oracle Compute Cloud Service (IaaS) to determine the parameters that are + // specific to each object type that you want to create. + // For example, if you want to create a storage volume, the type would be StorageVolume, and the template would include + // size and bootable. If you want to create an instance, the type would be Instance, and the template would include + // instance-specific attributes, such as imagelist and shape. + // Required + Template interface{} `json:"template"` + // Specify one of the following object types that you want to create. + // The only allowed type is Instance + // Required + Type OrchestrationType `json:"type"` + // Version of this object, generated by the server + // Optional + Version int `json:"version,omitempty"` +} + +type Health struct { + // The status of the object + Status OrchestrationStatus `json:"status,omitempty"` + // What caused the status of the object + Cause string `json:"cause,omitempty"` + // The specific details for what happened to the object + Detail string `json:"detail,omitempty"` + // Any errors associated with creation of the object + Error string `json:"error,omitempty"` +} + +// CreateOrchestration creates a new Orchestration with the given name, key and enabled flag. +func (c *OrchestrationsClient) CreateOrchestration(input *CreateOrchestrationInput) (*Orchestration, error) { + var createdOrchestration Orchestration + + input.Name = c.getQualifiedName(input.Name) + for _, i := range input.Objects { + i.Orchestration = c.getQualifiedName(i.Orchestration) + if i.Type == OrchestrationTypeInstance { + instanceClient := c.ComputeClient.Instances() + instanceInput := i.Template.(*CreateInstanceInput) + instanceInput.Name = c.getQualifiedName(instanceInput.Name) + + qualifiedSSHKeys := []string{} + for _, key := range instanceInput.SSHKeys { + qualifiedSSHKeys = append(qualifiedSSHKeys, c.getQualifiedName(key)) + } + + instanceInput.SSHKeys = qualifiedSSHKeys + + qualifiedStorageAttachments := []StorageAttachmentInput{} + for _, attachment := range instanceInput.Storage { + qualifiedStorageAttachments = append(qualifiedStorageAttachments, StorageAttachmentInput{ + Index: attachment.Index, + Volume: c.getQualifiedName(attachment.Volume), + }) + } + instanceInput.Storage = qualifiedStorageAttachments + + instanceInput.Networking = instanceClient.qualifyNetworking(instanceInput.Networking) + } + } + + if err := c.createResource(&input, &createdOrchestration); err != nil { + return nil, err + } + + // Call wait for orchestration ready now, as creating the orchestration is an eventually consistent operation + getInput := &GetOrchestrationInput{ + Name: createdOrchestration.Name, + } + + if input.Timeout == 0 { + input.Timeout = WaitForOrchestrationActiveTimeout + } + + // Wait for orchestration to be ready and return the result + // Don't have to unqualify any objects, as the GetOrchestration method will handle that + orchestrationInfo, orchestrationError := c.WaitForOrchestrationState(getInput, input.Timeout) + if orchestrationError != nil { + deleteInput := &DeleteOrchestrationInput{ + Name: createdOrchestration.Name, + } + err := c.DeleteOrchestration(deleteInput) + if err != nil { + return nil, fmt.Errorf("Error deleting orchestration %s: %s", getInput.Name, err) + } + return nil, fmt.Errorf("Error creating orchestration %s: %s", getInput.Name, orchestrationError) + } + + return &orchestrationInfo, nil +} + +// GetOrchestrationInput describes the Orchestration to get +type GetOrchestrationInput struct { + // The three-part name of the Orchestration (/Compute-identity_domain/user/object). + Name string `json:name` +} + +// GetOrchestration retrieves the Orchestration with the given name. +func (c *OrchestrationsClient) GetOrchestration(input *GetOrchestrationInput) (*Orchestration, error) { + var orchestrationInfo Orchestration + if err := c.getResource(input.Name, &orchestrationInfo); err != nil { + return nil, err + } + + return c.success(&orchestrationInfo) +} + +// UpdateOrchestrationInput defines an Orchestration to be updated +type UpdateOrchestrationInput struct { + // The default Oracle Compute Cloud Service account, such as /Compute-acme/default. + // Optional + Account string `json:"account,omitempty"` + // Description of this orchestration + // Optional + Description string `json:"description,omitempty"` + // Specify the desired state of this orchestration: active, inactive, or suspend. + // You can manage the state of the orchestration objects by changing the desired state of the orchestration. + // * active: Creates all the orchestration objects defined in the orchestration. + // * inactive: Adds the orchestration to Oracle Compute Cloud Service, but does not create any of the orchestration + // objects defined in the orchestration. + // Required + DesiredState OrchestrationDesiredState `json:"desired_state"` + // The three-part name of the Orchestration (/Compute-identity_domain/user/object). + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` + // The list of objects in the orchestration. An object is the primary building block of an orchestration. + // An orchestration can contain up to 100 objects. + // Required + Objects []Object `json:"objects"` + // Strings that describe the orchestration and help you identify it. + Tags []string `json:"tags,omitempty"` + // Version of this orchestration. It is automatically generated by the server. + Version int `json:"version,omitempty"` + // Time to wait for an orchestration to be ready + Timeout time.Duration `json:"-"` +} + +// UpdateOrchestration updates the orchestration. +func (c *OrchestrationsClient) UpdateOrchestration(input *UpdateOrchestrationInput) (*Orchestration, error) { + var updatedOrchestration Orchestration + input.Name = c.getQualifiedName(input.Name) + for _, i := range input.Objects { + i.Orchestration = c.getQualifiedName(i.Orchestration) + if i.Type == OrchestrationTypeInstance { + instanceInput := i.Template.(map[string]interface{}) + instanceInput["name"] = c.getQualifiedName(instanceInput["name"].(string)) + } + } + + if err := c.updateResource(input.Name, input, &updatedOrchestration); err != nil { + return nil, err + } + + // Call wait for orchestration ready now, as creating the orchestration is an eventually consistent operation + getInput := &GetOrchestrationInput{ + Name: updatedOrchestration.Name, + } + + if input.Timeout == 0 { + input.Timeout = WaitForOrchestrationActiveTimeout + } + + // Wait for orchestration to be ready and return the result + // Don't have to unqualify any objects, as the GetOrchestration method will handle that + orchestrationInfo, orchestrationError := c.WaitForOrchestrationState(getInput, input.Timeout) + if orchestrationError != nil { + return nil, orchestrationError + } + + return &orchestrationInfo, nil +} + +// DeleteOrchestrationInput describes the Orchestration to delete +type DeleteOrchestrationInput struct { + // The three-part name of the Orchestration (/Compute-identity_domain/user/object). + // Required + Name string `json:name` + // Timeout for delete request + Timeout time.Duration `json:"-"` +} + +// DeleteOrchestration deletes the Orchestration with the given name. +func (c *OrchestrationsClient) DeleteOrchestration(input *DeleteOrchestrationInput) error { + if err := c.deleteOrchestration(input.Name); err != nil { + return err + } + + if input.Timeout == 0 { + input.Timeout = WaitForOrchestrationDeleteTimeout + } + + return c.WaitForOrchestrationDeleted(input, input.Timeout) +} + +func (c *OrchestrationsClient) success(info *Orchestration) (*Orchestration, error) { + c.unqualify(&info.Name) + for _, i := range info.Objects { + c.unqualify(&i.Orchestration) + if OrchestrationType(i.Type) == OrchestrationTypeInstance { + instanceInput := i.Template.(map[string]interface{}) + instanceInput["name"] = c.getUnqualifiedName(instanceInput["name"].(string)) + } + } + + return info, nil +} + +// WaitForOrchestrationActive waits for an orchestration to be completely initialized and available. +func (c *OrchestrationsClient) WaitForOrchestrationState(input *GetOrchestrationInput, timeout time.Duration) (Orchestration, error) { + var info *Orchestration + var getErr error + err := c.client.WaitFor("orchestration to be ready", timeout, func() (bool, error) { + info, getErr = c.GetOrchestration(input) + if getErr != nil { + return false, getErr + } + c.client.DebugLogString(fmt.Sprintf("Orchestration name is %v, Orchestration info is %+v", info.Name, info)) + switch s := info.Status; s { + case OrchestrationStatusError: + // We need to check and see if an object the orchestration is trying to create is giving us an error instead of just the orchestration as a whole. + for _, object := range info.Objects { + if object.Health.Status == OrchestrationStatusError { + return false, fmt.Errorf("Error creating instance %s: %+v", object.Name, object.Health) + } + } + return false, fmt.Errorf("Error initializing orchestration: %+v", info) + case OrchestrationStatus(info.DesiredState): + c.client.DebugLogString(fmt.Sprintf("Orchestration %s", info.DesiredState)) + return true, nil + case OrchestrationStatusActivating: + c.client.DebugLogString("Orchestration activating") + return false, nil + case OrchestrationStatusStopping: + c.client.DebugLogString("Orchestration stopping") + return false, nil + case OrchestrationStatusSuspending: + c.client.DebugLogString("Orchestration suspending") + return false, nil + case OrchestrationStatusDeactivating: + c.client.DebugLogString("Orchestration deactivating") + return false, nil + case OrchestrationStatusSuspended: + c.client.DebugLogString("Orchestration suspended") + if info.DesiredState == OrchestrationDesiredStateSuspend { + return true, nil + } else { + return false, nil + } + default: + return false, fmt.Errorf("Unknown orchestration state: %s, erroring", s) + } + }) + return *info, err +} + +// WaitForOrchestrationDeleted waits for an orchestration to be fully deleted. +func (c *OrchestrationsClient) WaitForOrchestrationDeleted(input *DeleteOrchestrationInput, timeout time.Duration) error { + return c.client.WaitFor("orchestration to be deleted", timeout, func() (bool, error) { + var info Orchestration + if err := c.getResource(input.Name, &info); err != nil { + if client.WasNotFoundError(err) { + // Orchestration could not be found, thus deleted + return true, nil + } + // Some other error occurred trying to get Orchestration, exit + return false, err + } + switch s := info.Status; s { + case OrchestrationStatusError: + return false, fmt.Errorf("Error stopping orchestration: %+v", info) + case OrchestrationStatusStopping: + c.client.DebugLogString("Orchestration stopping") + return false, nil + case OrchestrationStatusDeleting: + c.client.DebugLogString("Orchestration deleting") + return false, nil + case OrchestrationStatusActive: + c.client.DebugLogString("Orchestration active") + return false, nil + default: + return false, fmt.Errorf("Unknown orchestration state: %s, erroring", s) + } + }) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/routes.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/routes.go new file mode 100644 index 000000000..f46beaecf --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/routes.go @@ -0,0 +1,153 @@ +package compute + +const ( + RoutesDescription = "IP Network Route" + RoutesContainerPath = "/network/v1/route/" + RoutesResourcePath = "/network/v1/route" +) + +type RoutesClient struct { + ResourceClient +} + +func (c *ComputeClient) Routes() *RoutesClient { + return &RoutesClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: RoutesDescription, + ContainerPath: RoutesContainerPath, + ResourceRootPath: RoutesResourcePath, + }, + } +} + +type RouteInfo struct { + // Admin distance associated with this route + AdminDistance int `json:"adminDistance"` + // Description of the route + Description string `json:"description"` + // CIDR IPv4 Prefix associated with this route + IPAddressPrefix string `json:"ipAddressPrefix"` + // Name of the route + Name string `json:"name"` + // Name of the VNIC set associated with the route + NextHopVnicSet string `json:"nextHopVnicSet"` + // Slice of Tags associated with the route + Tags []string `json:"tags,omitempty"` + // Uniform resource identifier associated with the route + Uri string `json:"uri"` +} + +type CreateRouteInput struct { + // Specify 0,1, or 2 as the route's administrative distance. + // If you do not specify a value, the default value is 0. + // The same prefix can be used in multiple routes. In this case, packets are routed over all the matching + // routes with the lowest administrative distance. + // In the case multiple routes with the same lowest administrative distance match, + // routing occurs over all these routes using ECMP. + // Optional + AdminDistance int `json:"adminDistance"` + // Description of the route + // Optional + Description string `json:"description"` + // The IPv4 address prefix in CIDR format, of the external network (external to the vNIC set) + // from which you want to route traffic + // Required + IPAddressPrefix string `json:"ipAddressPrefix"` + // Name of the route. + // Names can only contain alphanumeric, underscore, dash, and period characters. Case-sensitive + // Required + Name string `json:"name"` + // Name of the virtual NIC set to route matching packets to. + // Routed flows are load-balanced among all the virtual NICs in the virtual NIC set + // Required + NextHopVnicSet string `json:"nextHopVnicSet"` + // Slice of tags to be associated with the route + // Optional + Tags []string `json:"tags,omitempty"` +} + +func (c *RoutesClient) CreateRoute(input *CreateRouteInput) (*RouteInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.NextHopVnicSet = c.getQualifiedName(input.NextHopVnicSet) + + var routeInfo RouteInfo + if err := c.createResource(&input, &routeInfo); err != nil { + return nil, err + } + + return c.success(&routeInfo) +} + +type GetRouteInput struct { + // Name of the Route to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *RoutesClient) GetRoute(input *GetRouteInput) (*RouteInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var routeInfo RouteInfo + if err := c.getResource(input.Name, &routeInfo); err != nil { + return nil, err + } + return c.success(&routeInfo) +} + +type UpdateRouteInput struct { + // Specify 0,1, or 2 as the route's administrative distance. + // If you do not specify a value, the default value is 0. + // The same prefix can be used in multiple routes. In this case, packets are routed over all the matching + // routes with the lowest administrative distance. + // In the case multiple routes with the same lowest administrative distance match, + // routing occurs over all these routes using ECMP. + // Optional + AdminDistance int `json:"adminDistance"` + // Description of the route + // Optional + Description string `json:"description"` + // The IPv4 address prefix in CIDR format, of the external network (external to the vNIC set) + // from which you want to route traffic + // Required + IPAddressPrefix string `json:"ipAddressPrefix"` + // Name of the route. + // Names can only contain alphanumeric, underscore, dash, and period characters. Case-sensitive + // Required + Name string `json:"name"` + // Name of the virtual NIC set to route matching packets to. + // Routed flows are load-balanced among all the virtual NICs in the virtual NIC set + // Required + NextHopVnicSet string `json:"nextHopVnicSet"` + // Slice of tags to be associated with the route + // Optional + Tags []string `json:"tags"` +} + +func (c *RoutesClient) UpdateRoute(input *UpdateRouteInput) (*RouteInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.NextHopVnicSet = c.getQualifiedName(input.NextHopVnicSet) + + var routeInfo RouteInfo + if err := c.updateResource(input.Name, &input, &routeInfo); err != nil { + return nil, err + } + + return c.success(&routeInfo) +} + +type DeleteRouteInput struct { + // Name of the Route to delete. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *RoutesClient) DeleteRoute(input *DeleteRouteInput) error { + return c.deleteResource(input.Name) +} + +func (c *RoutesClient) success(info *RouteInfo) (*RouteInfo, error) { + c.unqualify(&info.Name) + c.unqualify(&info.NextHopVnicSet) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/sec_rules.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/sec_rules.go new file mode 100644 index 000000000..b45df047d --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/sec_rules.go @@ -0,0 +1,193 @@ +package compute + +// SecRulesClient is a client for the Sec Rules functions of the Compute API. +type SecRulesClient struct { + ResourceClient +} + +// SecRules obtains a SecRulesClient which can be used to access to the +// Sec Rules functions of the Compute API +func (c *ComputeClient) SecRules() *SecRulesClient { + return &SecRulesClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "security ip list", + ContainerPath: "/secrule/", + ResourceRootPath: "/secrule", + }} +} + +// SecRuleInfo describes an existing sec rule. +type SecRuleInfo struct { + // Set this parameter to PERMIT. + Action string `json:"action"` + // The name of the security application + Application string `json:"application"` + // A description of the sec rule + Description string `json:"description"` + // Indicates whether the security rule is enabled + Disabled bool `json:"disabled"` + // The name of the destination security list or security IP list. + DestinationList string `json:"dst_list"` + // The name of the sec rule + Name string `json:"name"` + // The name of the source security list or security IP list. + SourceList string `json:"src_list"` + // Uniform Resource Identifier for the sec rule + URI string `json:"uri"` +} + +// CreateSecRuleInput defines a sec rule to be created. +type CreateSecRuleInput struct { + // Set this parameter to PERMIT. + // Required + Action string `json:"action"` + + // The name of the security application for user-defined or predefined security applications. + // Required + Application string `json:"application"` + + // Description of the IP Network + // Optional + Description string `json:"description"` + + // Indicates whether the sec rule is enabled (set to false) or disabled (true). + // The default setting is false. + // Optional + Disabled bool `json:"disabled"` + + // The name of the destination security list or security IP list. + // + // You must use the prefix seclist: or seciplist: to identify the list type. + // + // You can specify a security IP list as the destination in a secrule, + // provided src_list is a security list that has DENY as its outbound policy. + // + // You cannot specify any of the security IP lists in the /oracle/public container + // as a destination in a secrule. + // Required + DestinationList string `json:"dst_list"` + + // The name of the Sec Rule to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // The name of the source security list or security IP list. + // + // You must use the prefix seclist: or seciplist: to identify the list type. + // + // Required + SourceList string `json:"src_list"` +} + +// CreateSecRule creates a new sec rule. +func (c *SecRulesClient) CreateSecRule(createInput *CreateSecRuleInput) (*SecRuleInfo, error) { + createInput.Name = c.getQualifiedName(createInput.Name) + createInput.SourceList = c.getQualifiedListName(createInput.SourceList) + createInput.DestinationList = c.getQualifiedListName(createInput.DestinationList) + createInput.Application = c.getQualifiedName(createInput.Application) + + var ruleInfo SecRuleInfo + if err := c.createResource(createInput, &ruleInfo); err != nil { + return nil, err + } + + return c.success(&ruleInfo) +} + +// GetSecRuleInput describes the Sec Rule to get +type GetSecRuleInput struct { + // The name of the Sec Rule to query for + // Required + Name string `json:"name"` +} + +// GetSecRule retrieves the sec rule with the given name. +func (c *SecRulesClient) GetSecRule(getInput *GetSecRuleInput) (*SecRuleInfo, error) { + var ruleInfo SecRuleInfo + if err := c.getResource(getInput.Name, &ruleInfo); err != nil { + return nil, err + } + + return c.success(&ruleInfo) +} + +// UpdateSecRuleInput describes a secruity rule to update +type UpdateSecRuleInput struct { + // Set this parameter to PERMIT. + // Required + Action string `json:"action"` + + // The name of the security application for user-defined or predefined security applications. + // Required + Application string `json:"application"` + + // Description of the IP Network + // Optional + Description string `json:"description"` + + // Indicates whether the sec rule is enabled (set to false) or disabled (true). + // The default setting is false. + // Optional + Disabled bool `json:"disabled"` + + // The name of the destination security list or security IP list. + // + // You must use the prefix seclist: or seciplist: to identify the list type. + // + // You can specify a security IP list as the destination in a secrule, + // provided src_list is a security list that has DENY as its outbound policy. + // + // You cannot specify any of the security IP lists in the /oracle/public container + // as a destination in a secrule. + // Required + DestinationList string `json:"dst_list"` + + // The name of the Sec Rule to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // The name of the source security list or security IP list. + // + // You must use the prefix seclist: or seciplist: to identify the list type. + // + // Required + SourceList string `json:"src_list"` +} + +// UpdateSecRule modifies the properties of the sec rule with the given name. +func (c *SecRulesClient) UpdateSecRule(updateInput *UpdateSecRuleInput) (*SecRuleInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + updateInput.SourceList = c.getQualifiedListName(updateInput.SourceList) + updateInput.DestinationList = c.getQualifiedListName(updateInput.DestinationList) + updateInput.Application = c.getQualifiedName(updateInput.Application) + + var ruleInfo SecRuleInfo + if err := c.updateResource(updateInput.Name, updateInput, &ruleInfo); err != nil { + return nil, err + } + + return c.success(&ruleInfo) +} + +// DeleteSecRuleInput describes the sec rule to delete +type DeleteSecRuleInput struct { + // The name of the Sec Rule to delete. + // Required + Name string `json:"name"` +} + +// DeleteSecRule deletes the sec rule with the given name. +func (c *SecRulesClient) DeleteSecRule(deleteInput *DeleteSecRuleInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *SecRulesClient) success(ruleInfo *SecRuleInfo) (*SecRuleInfo, error) { + ruleInfo.Name = c.getUnqualifiedName(ruleInfo.Name) + ruleInfo.SourceList = c.unqualifyListName(ruleInfo.SourceList) + ruleInfo.DestinationList = c.unqualifyListName(ruleInfo.DestinationList) + ruleInfo.Application = c.getUnqualifiedName(ruleInfo.Application) + return ruleInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_applications.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_applications.go new file mode 100644 index 000000000..c473ecb83 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_applications.go @@ -0,0 +1,150 @@ +package compute + +// SecurityApplicationsClient is a client for the Security Application functions of the Compute API. +type SecurityApplicationsClient struct { + ResourceClient +} + +// SecurityApplications obtains a SecurityApplicationsClient which can be used to access to the +// Security Application functions of the Compute API +func (c *ComputeClient) SecurityApplications() *SecurityApplicationsClient { + return &SecurityApplicationsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "security application", + ContainerPath: "/secapplication/", + ResourceRootPath: "/secapplication", + }} +} + +// SecurityApplicationInfo describes an existing security application. +type SecurityApplicationInfo struct { + // A description of the security application. + Description string `json:"description"` + // The TCP or UDP destination port number. This can be a port range, such as 5900-5999 for TCP. + DPort string `json:"dport"` + // The ICMP code. + ICMPCode SecurityApplicationICMPCode `json:"icmpcode"` + // The ICMP type. + ICMPType SecurityApplicationICMPType `json:"icmptype"` + // The three-part name of the Security Application (/Compute-identity_domain/user/object). + Name string `json:"name"` + // The protocol to use. + Protocol SecurityApplicationProtocol `json:"protocol"` + // The Uniform Resource Identifier + URI string `json:"uri"` +} + +type SecurityApplicationProtocol string + +const ( + All SecurityApplicationProtocol = "all" + AH SecurityApplicationProtocol = "ah" + ESP SecurityApplicationProtocol = "esp" + ICMP SecurityApplicationProtocol = "icmp" + ICMPV6 SecurityApplicationProtocol = "icmpv6" + IGMP SecurityApplicationProtocol = "igmp" + IPIP SecurityApplicationProtocol = "ipip" + GRE SecurityApplicationProtocol = "gre" + MPLSIP SecurityApplicationProtocol = "mplsip" + OSPF SecurityApplicationProtocol = "ospf" + PIM SecurityApplicationProtocol = "pim" + RDP SecurityApplicationProtocol = "rdp" + SCTP SecurityApplicationProtocol = "sctp" + TCP SecurityApplicationProtocol = "tcp" + UDP SecurityApplicationProtocol = "udp" +) + +type SecurityApplicationICMPCode string + +const ( + Admin SecurityApplicationICMPCode = "admin" + Df SecurityApplicationICMPCode = "df" + Host SecurityApplicationICMPCode = "host" + Network SecurityApplicationICMPCode = "network" + Port SecurityApplicationICMPCode = "port" + Protocol SecurityApplicationICMPCode = "protocol" +) + +type SecurityApplicationICMPType string + +const ( + Echo SecurityApplicationICMPType = "echo" + Reply SecurityApplicationICMPType = "reply" + TTL SecurityApplicationICMPType = "ttl" + TraceRoute SecurityApplicationICMPType = "traceroute" + Unreachable SecurityApplicationICMPType = "unreachable" +) + +func (c *SecurityApplicationsClient) success(result *SecurityApplicationInfo) (*SecurityApplicationInfo, error) { + c.unqualify(&result.Name) + return result, nil +} + +// CreateSecurityApplicationInput describes the Security Application to create +type CreateSecurityApplicationInput struct { + // A description of the security application. + // Optional + Description string `json:"description"` + // The TCP or UDP destination port number. + // You can also specify a port range, such as 5900-5999 for TCP. + // This parameter isn't relevant to the icmp protocol. + // Required if the Protocol is TCP or UDP + DPort string `json:"dport"` + // The ICMP code. This parameter is relevant only if you specify ICMP as the protocol. + // If you specify icmp as the protocol and don't specify icmptype or icmpcode, then all ICMP packets are matched. + // Optional + ICMPCode SecurityApplicationICMPCode `json:"icmpcode,omitempty"` + // This parameter is relevant only if you specify ICMP as the protocol. + // If you specify icmp as the protocol and don't specify icmptype or icmpcode, then all ICMP packets are matched. + // Optional + ICMPType SecurityApplicationICMPType `json:"icmptype,omitempty"` + // The three-part name of the Security Application (/Compute-identity_domain/user/object). + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` + // The protocol to use. + // Required + Protocol SecurityApplicationProtocol `json:"protocol"` +} + +// CreateSecurityApplication creates a new security application. +func (c *SecurityApplicationsClient) CreateSecurityApplication(input *CreateSecurityApplicationInput) (*SecurityApplicationInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var appInfo SecurityApplicationInfo + if err := c.createResource(&input, &appInfo); err != nil { + return nil, err + } + + return c.success(&appInfo) +} + +// GetSecurityApplicationInput describes the Security Application to obtain +type GetSecurityApplicationInput struct { + // The three-part name of the Security Application (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// GetSecurityApplication retrieves the security application with the given name. +func (c *SecurityApplicationsClient) GetSecurityApplication(input *GetSecurityApplicationInput) (*SecurityApplicationInfo, error) { + var appInfo SecurityApplicationInfo + if err := c.getResource(input.Name, &appInfo); err != nil { + return nil, err + } + + return c.success(&appInfo) +} + +// DeleteSecurityApplicationInput describes the Security Application to delete +type DeleteSecurityApplicationInput struct { + // The three-part name of the Security Application (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// DeleteSecurityApplication deletes the security application with the given name. +func (c *SecurityApplicationsClient) DeleteSecurityApplication(input *DeleteSecurityApplicationInput) error { + return c.deleteResource(input.Name) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_associations.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_associations.go new file mode 100644 index 000000000..0b806f029 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_associations.go @@ -0,0 +1,95 @@ +package compute + +// SecurityAssociationsClient is a client for the Security Association functions of the Compute API. +type SecurityAssociationsClient struct { + ResourceClient +} + +// SecurityAssociations obtains a SecurityAssociationsClient which can be used to access to the +// Security Association functions of the Compute API +func (c *ComputeClient) SecurityAssociations() *SecurityAssociationsClient { + return &SecurityAssociationsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "security association", + ContainerPath: "/secassociation/", + ResourceRootPath: "/secassociation", + }} +} + +// SecurityAssociationInfo describes an existing security association. +type SecurityAssociationInfo struct { + // The three-part name of the Security Association (/Compute-identity_domain/user/object). + Name string `json:"name"` + // The name of the Security List that you want to associate with the instance. + SecList string `json:"seclist"` + // vCable of the instance that you want to associate with the security list. + VCable string `json:"vcable"` + // Uniform Resource Identifier + URI string `json:"uri"` +} + +// CreateSecurityAssociationInput defines a security association to be created. +type CreateSecurityAssociationInput struct { + // The three-part name of the Security Association (/Compute-identity_domain/user/object). + // If you don't specify a name for this object, then the name is generated automatically. + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Optional + Name string `json:"name"` + // The name of the Security list that you want to associate with the instance. + // Required + SecList string `json:"seclist"` + // The name of the vCable of the instance that you want to associate with the security list. + // Required + VCable string `json:"vcable"` +} + +// CreateSecurityAssociation creates a security association between the given VCable and security list. +func (c *SecurityAssociationsClient) CreateSecurityAssociation(createInput *CreateSecurityAssociationInput) (*SecurityAssociationInfo, error) { + if createInput.Name != "" { + createInput.Name = c.getQualifiedName(createInput.Name) + } + createInput.VCable = c.getQualifiedName(createInput.VCable) + createInput.SecList = c.getQualifiedName(createInput.SecList) + + var assocInfo SecurityAssociationInfo + if err := c.createResource(&createInput, &assocInfo); err != nil { + return nil, err + } + + return c.success(&assocInfo) +} + +// GetSecurityAssociationInput describes the security association to get +type GetSecurityAssociationInput struct { + // The three-part name of the Security Association (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// GetSecurityAssociation retrieves the security association with the given name. +func (c *SecurityAssociationsClient) GetSecurityAssociation(getInput *GetSecurityAssociationInput) (*SecurityAssociationInfo, error) { + var assocInfo SecurityAssociationInfo + if err := c.getResource(getInput.Name, &assocInfo); err != nil { + return nil, err + } + + return c.success(&assocInfo) +} + +// DeleteSecurityAssociationInput describes the security association to delete +type DeleteSecurityAssociationInput struct { + // The three-part name of the Security Association (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// DeleteSecurityAssociation deletes the security association with the given name. +func (c *SecurityAssociationsClient) DeleteSecurityAssociation(deleteInput *DeleteSecurityAssociationInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *SecurityAssociationsClient) success(assocInfo *SecurityAssociationInfo) (*SecurityAssociationInfo, error) { + c.unqualify(&assocInfo.Name, &assocInfo.SecList, &assocInfo.VCable) + return assocInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_ip_lists.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_ip_lists.go new file mode 100644 index 000000000..28b313374 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_ip_lists.go @@ -0,0 +1,113 @@ +package compute + +// SecurityIPListsClient is a client for the Security IP List functions of the Compute API. +type SecurityIPListsClient struct { + ResourceClient +} + +// SecurityIPLists obtains a SecurityIPListsClient which can be used to access to the +// Security IP List functions of the Compute API +func (c *ComputeClient) SecurityIPLists() *SecurityIPListsClient { + return &SecurityIPListsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "security ip list", + ContainerPath: "/seciplist/", + ResourceRootPath: "/seciplist", + }} +} + +// SecurityIPListInfo describes an existing security IP list. +type SecurityIPListInfo struct { + // A description of the security IP list. + Description string `json:"description"` + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + // A comma-separated list of the subnets (in CIDR format) or IPv4 addresses for which you want to create this security IP list. + SecIPEntries []string `json:"secipentries"` + // Uniform Resource Identifier + URI string `json:"uri"` +} + +// CreateSecurityIPListInput defines a security IP list to be created. +type CreateSecurityIPListInput struct { + // A description of the security IP list. + // Optional + Description string `json:"description"` + // The three-part name of the object (/Compute-identity_domain/user/object). + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` + // A comma-separated list of the subnets (in CIDR format) or IPv4 addresses for which you want to create this security IP list. + // Required + SecIPEntries []string `json:"secipentries"` +} + +// CreateSecurityIPList creates a security IP list with the given name and entries. +func (c *SecurityIPListsClient) CreateSecurityIPList(createInput *CreateSecurityIPListInput) (*SecurityIPListInfo, error) { + createInput.Name = c.getQualifiedName(createInput.Name) + var listInfo SecurityIPListInfo + if err := c.createResource(createInput, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// GetSecurityIPListInput describes the Security IP List to obtain +type GetSecurityIPListInput struct { + // The three-part name of the object (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// GetSecurityIPList gets the security IP list with the given name. +func (c *SecurityIPListsClient) GetSecurityIPList(getInput *GetSecurityIPListInput) (*SecurityIPListInfo, error) { + var listInfo SecurityIPListInfo + if err := c.getResource(getInput.Name, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// UpdateSecurityIPListInput describes the security ip list to update +type UpdateSecurityIPListInput struct { + // A description of the security IP list. + // Optional + Description string `json:"description"` + // The three-part name of the object (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` + // A comma-separated list of the subnets (in CIDR format) or IPv4 addresses for which you want to create this security IP list. + // Required + SecIPEntries []string `json:"secipentries"` +} + +// UpdateSecurityIPList modifies the entries in the security IP list with the given name. +func (c *SecurityIPListsClient) UpdateSecurityIPList(updateInput *UpdateSecurityIPListInput) (*SecurityIPListInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + var listInfo SecurityIPListInfo + if err := c.updateResource(updateInput.Name, updateInput, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// DeleteSecurityIPListInput describes the security ip list to delete. +type DeleteSecurityIPListInput struct { + // The three-part name of the object (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// DeleteSecurityIPList deletes the security IP list with the given name. +func (c *SecurityIPListsClient) DeleteSecurityIPList(deleteInput *DeleteSecurityIPListInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *SecurityIPListsClient) success(listInfo *SecurityIPListInfo) (*SecurityIPListInfo, error) { + c.unqualify(&listInfo.Name) + return listInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_lists.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_lists.go new file mode 100644 index 000000000..3c6d87364 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_lists.go @@ -0,0 +1,131 @@ +package compute + +// SecurityListsClient is a client for the Security List functions of the Compute API. +type SecurityListsClient struct { + ResourceClient +} + +// SecurityLists obtains a SecurityListsClient which can be used to access to the +// Security List functions of the Compute API +func (c *ComputeClient) SecurityLists() *SecurityListsClient { + return &SecurityListsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "security list", + ContainerPath: "/seclist/", + ResourceRootPath: "/seclist", + }} +} + +type SecurityListPolicy string + +const ( + SecurityListPolicyDeny SecurityListPolicy = "deny" + SecurityListPolicyReject SecurityListPolicy = "reject" + SecurityListPolicyPermit SecurityListPolicy = "permit" +) + +// SecurityListInfo describes an existing security list. +type SecurityListInfo struct { + // Shows the default account for your identity domain. + Account string `json:"account"` + // A description of the security list. + Description string `json:description` + // The three-part name of the security list (/Compute-identity_domain/user/object). + Name string `json:"name"` + // The policy for outbound traffic from the security list. + OutboundCIDRPolicy SecurityListPolicy `json:"outbound_cidr_policy"` + // The policy for inbound traffic to the security list + Policy SecurityListPolicy `json:"policy"` + // Uniform Resource Identifier + URI string `json:"uri"` +} + +// CreateSecurityListInput defines a security list to be created. +type CreateSecurityListInput struct { + // A description of the security list. + // Optional + Description string `json:"description"` + // The three-part name of the Security List (/Compute-identity_domain/user/object). + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` + // The policy for outbound traffic from the security list. + // Optional (defaults to `permit`) + OutboundCIDRPolicy SecurityListPolicy `json:"outbound_cidr_policy"` + // The policy for inbound traffic to the security list. + // Optional (defaults to `deny`) + Policy SecurityListPolicy `json:"policy"` +} + +// CreateSecurityList creates a new security list with the given name, policy and outbound CIDR policy. +func (c *SecurityListsClient) CreateSecurityList(createInput *CreateSecurityListInput) (*SecurityListInfo, error) { + createInput.Name = c.getQualifiedName(createInput.Name) + var listInfo SecurityListInfo + if err := c.createResource(createInput, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// GetSecurityListInput describes the security list you want to get +type GetSecurityListInput struct { + // The three-part name of the Security List (/Compute-identity_domain/user/object). + // Required + Name string `json:name` +} + +// GetSecurityList retrieves the security list with the given name. +func (c *SecurityListsClient) GetSecurityList(getInput *GetSecurityListInput) (*SecurityListInfo, error) { + var listInfo SecurityListInfo + if err := c.getResource(getInput.Name, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// UpdateSecurityListInput defines what to update in a security list +type UpdateSecurityListInput struct { + // A description of the security list. + // Optional + Description string `json:description` + // The three-part name of the Security List (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` + // The policy for outbound traffic from the security list. + // Optional (defaults to `permit`) + OutboundCIDRPolicy SecurityListPolicy `json:"outbound_cidr_policy"` + // The policy for inbound traffic to the security list. + // Optional (defaults to `deny`) + Policy SecurityListPolicy `json:"policy"` +} + +// UpdateSecurityList updates the policy and outbound CIDR pol +func (c *SecurityListsClient) UpdateSecurityList(updateInput *UpdateSecurityListInput) (*SecurityListInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + var listInfo SecurityListInfo + if err := c.updateResource(updateInput.Name, updateInput, &listInfo); err != nil { + return nil, err + } + + return c.success(&listInfo) +} + +// DeleteSecurityListInput describes the security list to destroy +type DeleteSecurityListInput struct { + // The three-part name of the Security List (/Compute-identity_domain/user/object). + // Required + Name string `json:name` +} + +// DeleteSecurityList deletes the security list with the given name. +func (c *SecurityListsClient) DeleteSecurityList(deleteInput *DeleteSecurityListInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *SecurityListsClient) success(listInfo *SecurityListInfo) (*SecurityListInfo, error) { + c.unqualify(&listInfo.Name) + return listInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_protocols.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_protocols.go new file mode 100644 index 000000000..406be9cad --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_protocols.go @@ -0,0 +1,187 @@ +package compute + +const ( + SecurityProtocolDescription = "security protocol" + SecurityProtocolContainerPath = "/network/v1/secprotocol/" + SecurityProtocolResourcePath = "/network/v1/secprotocol" +) + +type SecurityProtocolsClient struct { + ResourceClient +} + +// SecurityProtocols() returns an SecurityProtocolsClient that can be used to access the +// necessary CRUD functions for Security Protocols. +func (c *ComputeClient) SecurityProtocols() *SecurityProtocolsClient { + return &SecurityProtocolsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: SecurityProtocolDescription, + ContainerPath: SecurityProtocolContainerPath, + ResourceRootPath: SecurityProtocolResourcePath, + }, + } +} + +// SecurityProtocolInfo contains the exported fields necessary to hold all the information about an +// Security Protocol +type SecurityProtocolInfo struct { + // List of port numbers or port range strings to match the packet's destination port. + DstPortSet []string `json:"dstPortSet"` + // Protocol used in the data portion of the IP datagram. + IPProtocol string `json:"ipProtocol"` + // List of port numbers or port range strings to match the packet's source port. + SrcPortSet []string `json:"srcPortSet"` + // The name of the Security Protocol + Name string `json:"name"` + // Description of the Security Protocol + Description string `json:"description"` + // Slice of tags associated with the Security Protocol + Tags []string `json:"tags"` + // Uniform Resource Identifier for the Security Protocol + Uri string `json:"uri"` +} + +type CreateSecurityProtocolInput struct { + // The name of the Security Protocol to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Description of the SecurityProtocol + // Optional + Description string `json:"description"` + + // Enter a list of port numbers or port range strings. + //Traffic is enabled by a security rule when a packet's destination port matches the + // ports specified here. + // For TCP, SCTP, and UDP, each port is a destination transport port, between 0 and 65535, + // inclusive. For ICMP, each port is an ICMP type, between 0 and 255, inclusive. + // If no destination ports are specified, all destination ports or ICMP types are allowed. + // Optional + DstPortSet []string `json:"dstPortSet"` + + // The protocol used in the data portion of the IP datagram. + // Specify one of the permitted values or enter a number in the range 0–254 to + // represent the protocol that you want to specify. See Assigned Internet Protocol Numbers. + // Permitted values are: tcp, udp, icmp, igmp, ipip, rdp, esp, ah, gre, icmpv6, ospf, pim, sctp, + // mplsip, all. + // Traffic is enabled by a security rule when the protocol in the packet matches the + // protocol specified here. If no protocol is specified, all protocols are allowed. + // Optional + IPProtocol string `json:"ipProtocol"` + + // Enter a list of port numbers or port range strings. + // Traffic is enabled by a security rule when a packet's source port matches the + // ports specified here. + // For TCP, SCTP, and UDP, each port is a source transport port, + // between 0 and 65535, inclusive. + // For ICMP, each port is an ICMP type, between 0 and 255, inclusive. + // If no source ports are specified, all source ports or ICMP types are allowed. + // Optional + SrcPortSet []string `json:"srcPortSet"` + + // String slice of tags to apply to the Security Protocol object + // Optional + Tags []string `json:"tags"` +} + +// Create a new Security Protocol from an SecurityProtocolsClient and an input struct. +// Returns a populated Info struct for the Security Protocol, and any errors +func (c *SecurityProtocolsClient) CreateSecurityProtocol(input *CreateSecurityProtocolInput) (*SecurityProtocolInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo SecurityProtocolInfo + if err := c.createResource(&input, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type GetSecurityProtocolInput struct { + // The name of the Security Protocol to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated SecurityProtocolInfo struct from an input struct +func (c *SecurityProtocolsClient) GetSecurityProtocol(input *GetSecurityProtocolInput) (*SecurityProtocolInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var ipInfo SecurityProtocolInfo + if err := c.getResource(input.Name, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +// UpdateSecurityProtocolInput defines what to update in a security protocol +type UpdateSecurityProtocolInput struct { + // The name of the Security Protocol to create. Object names can only contain alphanumeric, + // underscore, dash, and period characters. Names are case-sensitive. + // Required + Name string `json:"name"` + + // Description of the SecurityProtocol + // Optional + Description string `json:"description"` + + // Enter a list of port numbers or port range strings. + //Traffic is enabled by a security rule when a packet's destination port matches the + // ports specified here. + // For TCP, SCTP, and UDP, each port is a destination transport port, between 0 and 65535, + // inclusive. For ICMP, each port is an ICMP type, between 0 and 255, inclusive. + // If no destination ports are specified, all destination ports or ICMP types are allowed. + DstPortSet []string `json:"dstPortSet"` + + // The protocol used in the data portion of the IP datagram. + // Specify one of the permitted values or enter a number in the range 0–254 to + // represent the protocol that you want to specify. See Assigned Internet Protocol Numbers. + // Permitted values are: tcp, udp, icmp, igmp, ipip, rdp, esp, ah, gre, icmpv6, ospf, pim, sctp, + // mplsip, all. + // Traffic is enabled by a security rule when the protocol in the packet matches the + // protocol specified here. If no protocol is specified, all protocols are allowed. + IPProtocol string `json:"ipProtocol"` + + // Enter a list of port numbers or port range strings. + // Traffic is enabled by a security rule when a packet's source port matches the + // ports specified here. + // For TCP, SCTP, and UDP, each port is a source transport port, + // between 0 and 65535, inclusive. + // For ICMP, each port is an ICMP type, between 0 and 255, inclusive. + // If no source ports are specified, all source ports or ICMP types are allowed. + SrcPortSet []string `json:"srcPortSet"` + + // String slice of tags to apply to the Security Protocol object + // Optional + Tags []string `json:"tags"` +} + +// UpdateSecurityProtocol update the security protocol +func (c *SecurityProtocolsClient) UpdateSecurityProtocol(updateInput *UpdateSecurityProtocolInput) (*SecurityProtocolInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + var ipInfo SecurityProtocolInfo + if err := c.updateResource(updateInput.Name, updateInput, &ipInfo); err != nil { + return nil, err + } + + return c.success(&ipInfo) +} + +type DeleteSecurityProtocolInput struct { + // The name of the Security Protocol to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *SecurityProtocolsClient) DeleteSecurityProtocol(input *DeleteSecurityProtocolInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the SecurityProtocolInfo struct +func (c *SecurityProtocolsClient) success(info *SecurityProtocolInfo) (*SecurityProtocolInfo, error) { + c.unqualify(&info.Name) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_rules.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_rules.go new file mode 100644 index 000000000..1773ea8b5 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/security_rules.go @@ -0,0 +1,266 @@ +package compute + +const ( + SecurityRuleDescription = "security rules" + SecurityRuleContainerPath = "/network/v1/secrule/" + SecurityRuleResourcePath = "/network/v1/secrule" +) + +type SecurityRuleClient struct { + ResourceClient +} + +// SecurityRules() returns an SecurityRulesClient that can be used to access the +// necessary CRUD functions for Security Rules. +func (c *ComputeClient) SecurityRules() *SecurityRuleClient { + return &SecurityRuleClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: SecurityRuleDescription, + ContainerPath: SecurityRuleContainerPath, + ResourceRootPath: SecurityRuleResourcePath, + }, + } +} + +// SecurityRuleInfo contains the exported fields necessary to hold all the information about a +// Security Rule +type SecurityRuleInfo struct { + // Name of the ACL that contains this rule. + ACL string `json:"acl"` + // Description of the Security Rule + Description string `json:"description"` + // List of IP address prefix set names to match the packet's destination IP address. + DstIpAddressPrefixSets []string `json:"dstIpAddressPrefixSets"` + // Name of virtual NIC set containing the packet's destination virtual NIC. + DstVnicSet string `json:"dstVnicSet"` + // Allows the security rule to be disabled. + Enabled bool `json:"enabledFlag"` + // Direction of the flow; Can be "egress" or "ingress". + FlowDirection string `json:"FlowDirection"` + // The name of the Security Rule + Name string `json:"name"` + // List of security protocol names to match the packet's protocol and port. + SecProtocols []string `json:"secProtocols"` + // List of multipart names of IP address prefix set to match the packet's source IP address. + SrcIpAddressPrefixSets []string `json:"srcIpAddressPrefixSets"` + // Name of virtual NIC set containing the packet's source virtual NIC. + SrcVnicSet string `json:"srcVnicSet"` + // Slice of tags associated with the Security Rule + Tags []string `json:"tags"` + // Uniform Resource Identifier for the Security Rule + Uri string `json:"uri"` +} + +type CreateSecurityRuleInput struct { + //Select the name of the access control list (ACL) that you want to add this + // security rule to. Security rules are applied to vNIC sets by using ACLs. + // Optional + ACL string `json:"acl,omitempty"` + + // Description of the Security Rule + // Optional + Description string `json:"description"` + + // A list of IP address prefix sets to which you want to permit traffic. + // Only packets to IP addresses in the specified IP address prefix sets are permitted. + // When no destination IP address prefix sets are specified, traffic to any + // IP address is permitted. + // Optional + DstIpAddressPrefixSets []string `json:"dstIpAddressPrefixSets"` + + // The vNICset to which you want to permit traffic. Only packets to vNICs in the + // specified vNICset are permitted. When no destination vNICset is specified, traffic + // to any vNIC is permitted. + // Optional + DstVnicSet string `json:"dstVnicSet,omitempty"` + + // Allows the security rule to be enabled or disabled. This parameter is set to + // true by default. Specify false to disable the security rule. + // Optional + Enabled bool `json:"enabledFlag"` + + // Specify the direction of flow of traffic, which is relative to the instances, + // for this security rule. Allowed values are ingress or egress. + // An ingress packet is a packet received by a virtual NIC, for example from + // another virtual NIC or from the public Internet. + // An egress packet is a packet sent by a virtual NIC, for example to another + // virtual NIC or to the public Internet. + // Required + FlowDirection string `json:"flowDirection"` + + // The name of the Security Rule + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. + // Object names are case-sensitive. When you specify the object name, ensure that an object + // of the same type and with the same name doesn't already exist. + // If such an object already exists, another object of the same type and with the same name won't + // be created and the existing object won't be updated. + // Required + Name string `json:"name"` + + // A list of security protocols for which you want to permit traffic. Only packets that + // match the specified protocols and ports are permitted. When no security protocols are + // specified, traffic using any protocol over any port is permitted. + // Optional + SecProtocols []string `json:"secProtocols"` + + // A list of IP address prefix sets from which you want to permit traffic. Only packets + // from IP addresses in the specified IP address prefix sets are permitted. When no source + // IP address prefix sets are specified, traffic from any IP address is permitted. + // Optional + SrcIpAddressPrefixSets []string `json:"srcIpAddressPrefixSets"` + + // The vNICset from which you want to permit traffic. Only packets from vNICs in the + // specified vNICset are permitted. When no source vNICset is specified, traffic from any + // vNIC is permitted. + // Optional + SrcVnicSet string `json:"srcVnicSet,omitempty"` + + // Strings that you can use to tag the security rule. + // Optional + Tags []string `json:"tags"` +} + +// Create a new Security Rule from an SecurityRuleClient and an input struct. +// Returns a populated Info struct for the Security Rule, and any errors +func (c *SecurityRuleClient) CreateSecurityRule(input *CreateSecurityRuleInput) (*SecurityRuleInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.ACL = c.getQualifiedName(input.ACL) + input.SrcVnicSet = c.getQualifiedName(input.SrcVnicSet) + input.DstVnicSet = c.getQualifiedName(input.DstVnicSet) + input.SrcIpAddressPrefixSets = c.getQualifiedList(input.SrcIpAddressPrefixSets) + input.DstIpAddressPrefixSets = c.getQualifiedList(input.DstIpAddressPrefixSets) + input.SecProtocols = c.getQualifiedList(input.SecProtocols) + + var securityRuleInfo SecurityRuleInfo + if err := c.createResource(&input, &securityRuleInfo); err != nil { + return nil, err + } + + return c.success(&securityRuleInfo) +} + +type GetSecurityRuleInput struct { + // The name of the Security Rule to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +// Returns a populated SecurityRuleInfo struct from an input struct +func (c *SecurityRuleClient) GetSecurityRule(input *GetSecurityRuleInput) (*SecurityRuleInfo, error) { + input.Name = c.getQualifiedName(input.Name) + + var securityRuleInfo SecurityRuleInfo + if err := c.getResource(input.Name, &securityRuleInfo); err != nil { + return nil, err + } + + return c.success(&securityRuleInfo) +} + +// UpdateSecurityRuleInput describes a secruity rule to update +type UpdateSecurityRuleInput struct { + //Select the name of the access control list (ACL) that you want to add this + // security rule to. Security rules are applied to vNIC sets by using ACLs. + // Optional + ACL string `json:"acl,omitempty"` + + // Description of the Security Rule + // Optional + Description string `json:"description"` + + // A list of IP address prefix sets to which you want to permit traffic. + // Only packets to IP addresses in the specified IP address prefix sets are permitted. + // When no destination IP address prefix sets are specified, traffic to any + // IP address is permitted. + // Optional + DstIpAddressPrefixSets []string `json:"dstIpAddressPrefixSets"` + + // The vNICset to which you want to permit traffic. Only packets to vNICs in the + // specified vNICset are permitted. When no destination vNICset is specified, traffic + // to any vNIC is permitted. + // Optional + DstVnicSet string `json:"dstVnicSet,omitempty"` + + // Allows the security rule to be enabled or disabled. This parameter is set to + // true by default. Specify false to disable the security rule. + // Optional + Enabled bool `json:"enabledFlag"` + + // Specify the direction of flow of traffic, which is relative to the instances, + // for this security rule. Allowed values are ingress or egress. + // An ingress packet is a packet received by a virtual NIC, for example from + // another virtual NIC or from the public Internet. + // An egress packet is a packet sent by a virtual NIC, for example to another + // virtual NIC or to the public Internet. + // Required + FlowDirection string `json:"flowDirection"` + + // The name of the Security Rule + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. + // Object names are case-sensitive. When you specify the object name, ensure that an object + // of the same type and with the same name doesn't already exist. + // If such an object already exists, another object of the same type and with the same name won't + // be created and the existing object won't be updated. + // Required + Name string `json:"name"` + + // A list of security protocols for which you want to permit traffic. Only packets that + // match the specified protocols and ports are permitted. When no security protocols are + // specified, traffic using any protocol over any port is permitted. + // Optional + SecProtocols []string `json:"secProtocols"` + + // A list of IP address prefix sets from which you want to permit traffic. Only packets + // from IP addresses in the specified IP address prefix sets are permitted. When no source + // IP address prefix sets are specified, traffic from any IP address is permitted. + // Optional + SrcIpAddressPrefixSets []string `json:"srcIpAddressPrefixSets"` + + // The vNICset from which you want to permit traffic. Only packets from vNICs in the + // specified vNICset are permitted. When no source vNICset is specified, traffic from any + // vNIC is permitted. + // Optional + SrcVnicSet string `json:"srcVnicSet,omitempty"` + + // Strings that you can use to tag the security rule. + // Optional + Tags []string `json:"tags"` +} + +// UpdateSecRule modifies the properties of the sec rule with the given name. +func (c *SecurityRuleClient) UpdateSecurityRule(updateInput *UpdateSecurityRuleInput) (*SecurityRuleInfo, error) { + updateInput.Name = c.getQualifiedName(updateInput.Name) + updateInput.ACL = c.getQualifiedName(updateInput.ACL) + updateInput.SrcVnicSet = c.getQualifiedName(updateInput.SrcVnicSet) + updateInput.DstVnicSet = c.getQualifiedName(updateInput.DstVnicSet) + updateInput.SrcIpAddressPrefixSets = c.getQualifiedList(updateInput.SrcIpAddressPrefixSets) + updateInput.DstIpAddressPrefixSets = c.getQualifiedList(updateInput.DstIpAddressPrefixSets) + updateInput.SecProtocols = c.getQualifiedList(updateInput.SecProtocols) + + var securityRuleInfo SecurityRuleInfo + if err := c.updateResource(updateInput.Name, updateInput, &securityRuleInfo); err != nil { + return nil, err + } + + return c.success(&securityRuleInfo) +} + +type DeleteSecurityRuleInput struct { + // The name of the Security Rule to query for. Case-sensitive + // Required + Name string `json:"name"` +} + +func (c *SecurityRuleClient) DeleteSecurityRule(input *DeleteSecurityRuleInput) error { + return c.deleteResource(input.Name) +} + +// Unqualifies any qualified fields in the IPNetworkExchangeInfo struct +func (c *SecurityRuleClient) success(info *SecurityRuleInfo) (*SecurityRuleInfo, error) { + c.unqualify(&info.Name, &info.ACL, &info.SrcVnicSet, &info.DstVnicSet) + info.SrcIpAddressPrefixSets = c.getUnqualifiedList(info.SrcIpAddressPrefixSets) + info.DstIpAddressPrefixSets = c.getUnqualifiedList(info.DstIpAddressPrefixSets) + info.SecProtocols = c.getUnqualifiedList(info.SecProtocols) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/snapshots.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/snapshots.go new file mode 100644 index 000000000..d2a9616e9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/snapshots.go @@ -0,0 +1,217 @@ +package compute + +import ( + "fmt" + "time" +) + +const WaitForSnapshotCompleteTimeout = time.Duration(600 * time.Second) + +// SnapshotsClient is a client for the Snapshot functions of the Compute API. +type SnapshotsClient struct { + ResourceClient +} + +// Snapshots obtains an SnapshotsClient which can be used to access to the +// Snapshot functions of the Compute API +func (c *ComputeClient) Snapshots() *SnapshotsClient { + return &SnapshotsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "Snapshot", + ContainerPath: "/snapshot/", + ResourceRootPath: "/snapshot", + }} +} + +type SnapshotState string + +const ( + SnapshotActive SnapshotState = "active" + SnapshotComplete SnapshotState = "complete" + SnapshotQueued SnapshotState = "queued" + SnapshotError SnapshotState = "error" +) + +type SnapshotDelay string + +const ( + SnapshotDelayShutdown SnapshotDelay = "shutdown" +) + +// SnapshotInfo describes an existing Snapshot. +type Snapshot struct { + // Shows the default account for your identity domain. + Account string `json:"account"` + // Timestamp when this request was created. + CreationTime string `json:"creation_time"` + // Snapshot of the instance is not taken immediately. + Delay SnapshotDelay `json:"delay"` + // A description of the reason this request entered "error" state. + ErrorReason string `json:"error_reason"` + // Name of the instance + Instance string `json:"instance"` + // Name of the machine image generated from the instance snapshot request. + MachineImage string `json:"machineimage"` + // Name of the instance snapshot request. + Name string `json:"name"` + // Not used + Quota string `json:"quota"` + // The state of the request. + State SnapshotState `json:"state"` + // Uniform Resource Identifier + URI string `json:"uri"` +} + +// CreateSnapshotInput defines an Snapshot to be created. +type CreateSnapshotInput struct { + // The name of the account that contains the credentials and access details of + // Oracle Storage Cloud Service. The machine image file is uploaded to the Oracle + // Storage Cloud Service account that you specify. + // Optional + Account string `json:"account,omitempty"` + // Use this option when you want to preserve the custom changes you have made + // to an instance before deleting the instance. The only permitted value is shutdown. + // Snapshot of the instance is not taken immediately. It creates a machine image which + // preserves the changes you have made to the instance, and then the instance is deleted. + // Note: This option has no effect if you shutdown the instance from inside it. Any pending + // snapshot request on that instance goes into error state. You must delete the instance + // (DELETE /instance/{name}). + // Optional + Delay SnapshotDelay `json:"delay,omitempty"` + // Name of the instance that you want to clone. + // Required + Instance string `json:"instance"` + // Specify the name of the machine image created by the snapshot request. + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. + // Object names are case-sensitive. + // If you don't specify a name for this object, then the name is generated automatically. + // Optional + MachineImage string `json:"machineimage,omitempty"` + // Time to wait for snapshot to be completed + Timeout time.Duration `json:"-"` +} + +// CreateSnapshot creates a new Snapshot +func (c *SnapshotsClient) CreateSnapshot(input *CreateSnapshotInput) (*Snapshot, error) { + input.Account = c.getQualifiedACMEName(input.Account) + input.Instance = c.getQualifiedName(input.Instance) + input.MachineImage = c.getQualifiedName(input.MachineImage) + + var snapshotInfo Snapshot + if err := c.createResource(&input, &snapshotInfo); err != nil { + return nil, err + } + + // Call wait for snapshot complete now, as creating the snashot is an eventually consistent operation + getInput := &GetSnapshotInput{ + Name: snapshotInfo.Name, + } + + if input.Timeout == 0 { + input.Timeout = WaitForSnapshotCompleteTimeout + } + + // Wait for snapshot to be complete and return the result + return c.WaitForSnapshotComplete(getInput, input.Timeout) +} + +// GetSnapshotInput describes the snapshot to get +type GetSnapshotInput struct { + // The name of the Snapshot + // Required + Name string `json:name` +} + +// GetSnapshot retrieves the Snapshot with the given name. +func (c *SnapshotsClient) GetSnapshot(getInput *GetSnapshotInput) (*Snapshot, error) { + getInput.Name = c.getQualifiedName(getInput.Name) + var snapshotInfo Snapshot + if err := c.getResource(getInput.Name, &snapshotInfo); err != nil { + return nil, err + } + + return c.success(&snapshotInfo) +} + +// DeleteSnapshotInput describes the snapshot to delete +type DeleteSnapshotInput struct { + // The name of the Snapshot + // Required + Snapshot string + // The name of the machine image + // Required + MachineImage string + // Time to wait for snapshot to be deleted + Timeout time.Duration +} + +// DeleteSnapshot deletes the Snapshot with the given name. +// A machine image gets created with the associated snapshot and needs to be deleted as well. +func (c *SnapshotsClient) DeleteSnapshot(machineImagesClient *MachineImagesClient, input *DeleteSnapshotInput) error { + // Wait for snapshot complete in case delay is active and the corresponding instance needs to be deleted first + getInput := &GetSnapshotInput{ + Name: input.Snapshot, + } + + if input.Timeout == 0 { + input.Timeout = WaitForSnapshotCompleteTimeout + } + + if _, err := c.WaitForSnapshotComplete(getInput, input.Timeout); err != nil { + return fmt.Errorf("Could not delete snapshot: %s", err) + } + + if err := c.deleteResource(input.Snapshot); err != nil { + return fmt.Errorf("Could not delete snapshot: %s", err) + } + + deleteMachineImageRequest := &DeleteMachineImageInput{ + Name: input.MachineImage, + } + if err := machineImagesClient.DeleteMachineImage(deleteMachineImageRequest); err != nil { + return fmt.Errorf("Could not delete machine image associated with snapshot: %s", err) + } + + return nil +} + +// WaitForSnapshotComplete waits for an snapshot to be completely initialized and available. +func (c *SnapshotsClient) WaitForSnapshotComplete(input *GetSnapshotInput, timeout time.Duration) (*Snapshot, error) { + var info *Snapshot + var getErr error + err := c.client.WaitFor("snapshot to be complete", timeout, func() (bool, error) { + info, getErr = c.GetSnapshot(input) + if getErr != nil { + return false, getErr + } + switch s := info.State; s { + case SnapshotError: + return false, fmt.Errorf("Error initializing snapshot: %s", info.ErrorReason) + case SnapshotComplete: + c.client.DebugLogString("Snapshot Complete") + return true, nil + case SnapshotQueued: + c.client.DebugLogString("Snapshot Queuing") + return false, nil + case SnapshotActive: + c.client.DebugLogString("Snapshot Active") + if info.Delay == SnapshotDelayShutdown { + return true, nil + } + return false, nil + default: + c.client.DebugLogString(fmt.Sprintf("Unknown snapshot state: %s, waiting", s)) + return false, nil + } + }) + return info, err +} + +func (c *SnapshotsClient) success(snapshotInfo *Snapshot) (*Snapshot, error) { + c.unqualify(&snapshotInfo.Account) + c.unqualify(&snapshotInfo.Instance) + c.unqualify(&snapshotInfo.MachineImage) + c.unqualify(&snapshotInfo.Name) + return snapshotInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/ssh_keys.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ssh_keys.go new file mode 100644 index 000000000..7e8be20ed --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/ssh_keys.go @@ -0,0 +1,124 @@ +package compute + +// SSHKeysClient is a client for the SSH key functions of the Compute API. +type SSHKeysClient struct { + ResourceClient +} + +// SSHKeys obtains an SSHKeysClient which can be used to access to the +// SSH key functions of the Compute API +func (c *ComputeClient) SSHKeys() *SSHKeysClient { + return &SSHKeysClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "SSH key", + ContainerPath: "/sshkey/", + ResourceRootPath: "/sshkey", + }} +} + +// SSHKeyInfo describes an existing SSH key. +type SSHKey struct { + // Indicates whether the key is enabled (true) or disabled. + Enabled bool `json:"enabled"` + // The SSH public key value. + Key string `json:"key"` + // The three-part name of the SSH Key (/Compute-identity_domain/user/object). + Name string `json:"name"` + // Unique Resource Identifier + URI string `json:"uri"` +} + +// CreateSSHKeyInput defines an SSH key to be created. +type CreateSSHKeyInput struct { + // The three-part name of the SSH Key (/Compute-identity_domain/user/object). + // Object names can contain only alphanumeric characters, hyphens, underscores, and periods. Object names are case-sensitive. + // Required + Name string `json:"name"` + // The SSH public key value. + // Required + Key string `json:"key"` + // Indicates whether the key must be enabled (default) or disabled. Note that disabled keys cannot be associated with instances. + // To explicitly enable the key, specify true. To disable the key, specify false. + // Optional + Enabled bool `json:"enabled"` +} + +// CreateSSHKey creates a new SSH key with the given name, key and enabled flag. +func (c *SSHKeysClient) CreateSSHKey(createInput *CreateSSHKeyInput) (*SSHKey, error) { + var keyInfo SSHKey + // We have to update after create to get the full ssh key into opc + updateSSHKeyInput := UpdateSSHKeyInput{ + Name: createInput.Name, + Key: createInput.Key, + Enabled: createInput.Enabled, + } + + createInput.Name = c.getQualifiedName(createInput.Name) + if err := c.createResource(&createInput, &keyInfo); err != nil { + return nil, err + } + + _, err := c.UpdateSSHKey(&updateSSHKeyInput) + if err != nil { + return nil, err + } + + return c.success(&keyInfo) +} + +// GetSSHKeyInput describes the ssh key to get +type GetSSHKeyInput struct { + // The three-part name of the SSH Key (/Compute-identity_domain/user/object). + Name string `json:name` +} + +// GetSSHKey retrieves the SSH key with the given name. +func (c *SSHKeysClient) GetSSHKey(getInput *GetSSHKeyInput) (*SSHKey, error) { + var keyInfo SSHKey + if err := c.getResource(getInput.Name, &keyInfo); err != nil { + return nil, err + } + + return c.success(&keyInfo) +} + +// UpdateSSHKeyInput defines an SSH key to be updated +type UpdateSSHKeyInput struct { + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + // The SSH public key value. + // Required + Key string `json:"key"` + // Indicates whether the key must be enabled (default) or disabled. Note that disabled keys cannot be associated with instances. + // To explicitly enable the key, specify true. To disable the key, specify false. + // Optional + // TODO/NOTE: isn't this required? + Enabled bool `json:"enabled"` +} + +// UpdateSSHKey updates the key and enabled flag of the SSH key with the given name. +func (c *SSHKeysClient) UpdateSSHKey(updateInput *UpdateSSHKeyInput) (*SSHKey, error) { + var keyInfo SSHKey + updateInput.Name = c.getQualifiedName(updateInput.Name) + if err := c.updateResource(updateInput.Name, updateInput, &keyInfo); err != nil { + return nil, err + } + return c.success(&keyInfo) +} + +// DeleteKeyInput describes the ssh key to delete +type DeleteSSHKeyInput struct { + // The three-part name of the SSH Key (/Compute-identity_domain/user/object). + Name string `json:name` +} + +// DeleteSSHKey deletes the SSH key with the given name. +func (c *SSHKeysClient) DeleteSSHKey(deleteInput *DeleteSSHKeyInput) error { + return c.deleteResource(deleteInput.Name) +} + +func (c *SSHKeysClient) success(keyInfo *SSHKey) (*SSHKey, error) { + c.unqualify(&keyInfo.Name) + return keyInfo, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_attachments.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_attachments.go new file mode 100644 index 000000000..51e18af47 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_attachments.go @@ -0,0 +1,178 @@ +package compute + +import ( + "time" + + "github.com/hashicorp/go-oracle-terraform/client" +) + +const WaitForVolumeAttachmentDeleteTimeout = time.Duration(30 * time.Second) +const WaitForVolumeAttachmentReadyTimeout = time.Duration(30 * time.Second) + +// StorageAttachmentsClient is a client for the Storage Attachment functions of the Compute API. +type StorageAttachmentsClient struct { + ResourceClient +} + +// StorageAttachments obtains a StorageAttachmentsClient which can be used to access to the +// Storage Attachment functions of the Compute API +func (c *ComputeClient) StorageAttachments() *StorageAttachmentsClient { + return &StorageAttachmentsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "storage volume attachment", + ContainerPath: "/storage/attachment/", + ResourceRootPath: "/storage/attachment", + }} +} + +type StorageAttachmentState string + +const ( + Attaching StorageAttachmentState = "attaching" + Attached StorageAttachmentState = "attached" + Detaching StorageAttachmentState = "detaching" + Unavailable StorageAttachmentState = "unavailable" + Unknown StorageAttachmentState = "unknown" +) + +// StorageAttachmentInfo describes an existing storage attachment. +type StorageAttachmentInfo struct { + // Name of this attachment, generated by the server. + Name string `json:"name"` + + // Index number for the volume. The allowed range is 1-10 + // An attachment with index 1 is exposed to the instance as /dev/xvdb, an attachment with index 2 is exposed as /dev/xvdc, and so on. + Index int `json:"index"` + + // Multipart name of the instance attached to the storage volume. + InstanceName string `json:"instance_name"` + + // Multipart name of the volume attached to the instance. + StorageVolumeName string `json:"storage_volume_name"` + + // The State of the Storage Attachment + State StorageAttachmentState `json:"state"` +} + +func (c *StorageAttachmentsClient) success(attachmentInfo *StorageAttachmentInfo) (*StorageAttachmentInfo, error) { + c.unqualify(&attachmentInfo.Name, &attachmentInfo.InstanceName, &attachmentInfo.StorageVolumeName) + return attachmentInfo, nil +} + +type CreateStorageAttachmentInput struct { + // Index number for the volume. The allowed range is 1-10 + // An attachment with index 1 is exposed to the instance as /dev/xvdb, an attachment with index 2 is exposed as /dev/xvdc, and so on. + // Required. + Index int `json:"index"` + + // Multipart name of the instance to which you want to attach the volume. + // Required. + InstanceName string `json:"instance_name"` + + // Multipart name of the volume that you want to attach. + // Required. + StorageVolumeName string `json:"storage_volume_name"` + + // Time to wait for storage volume attachment + Timeout time.Duration `json:"-"` +} + +// CreateStorageAttachment creates a storage attachment attaching the given volume to the given instance at the given index. +func (c *StorageAttachmentsClient) CreateStorageAttachment(input *CreateStorageAttachmentInput) (*StorageAttachmentInfo, error) { + input.InstanceName = c.getQualifiedName(input.InstanceName) + + var attachmentInfo *StorageAttachmentInfo + if err := c.createResource(&input, &attachmentInfo); err != nil { + return nil, err + } + + if input.Timeout == 0 { + input.Timeout = WaitForVolumeAttachmentReadyTimeout + } + + return c.waitForStorageAttachmentToFullyAttach(attachmentInfo.Name, input.Timeout) +} + +// DeleteStorageAttachmentInput represents the body of an API request to delete a Storage Attachment. +type DeleteStorageAttachmentInput struct { + // The three-part name of the Storage Attachment (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` + + // Time to wait for storage volume snapshot + Timeout time.Duration `json:"-"` +} + +// DeleteStorageAttachment deletes the storage attachment with the given name. +func (c *StorageAttachmentsClient) DeleteStorageAttachment(input *DeleteStorageAttachmentInput) error { + if err := c.deleteResource(input.Name); err != nil { + return err + } + + if input.Timeout == 0 { + input.Timeout = WaitForVolumeAttachmentDeleteTimeout + } + + return c.waitForStorageAttachmentToBeDeleted(input.Name, input.Timeout) +} + +// GetStorageAttachmentInput represents the body of an API request to obtain a Storage Attachment. +type GetStorageAttachmentInput struct { + // The three-part name of the Storage Attachment (/Compute-identity_domain/user/object). + // Required + Name string `json:"name"` +} + +// GetStorageAttachment retrieves the storage attachment with the given name. +func (c *StorageAttachmentsClient) GetStorageAttachment(input *GetStorageAttachmentInput) (*StorageAttachmentInfo, error) { + var attachmentInfo *StorageAttachmentInfo + if err := c.getResource(input.Name, &attachmentInfo); err != nil { + return nil, err + } + + return c.success(attachmentInfo) +} + +// waitForStorageAttachmentToFullyAttach waits for the storage attachment with the given name to be fully attached, or times out. +func (c *StorageAttachmentsClient) waitForStorageAttachmentToFullyAttach(name string, timeout time.Duration) (*StorageAttachmentInfo, error) { + var waitResult *StorageAttachmentInfo + + err := c.client.WaitFor("storage attachment to be attached", timeout, func() (bool, error) { + input := &GetStorageAttachmentInput{ + Name: name, + } + info, err := c.GetStorageAttachment(input) + if err != nil { + return false, err + } + + if info != nil { + if info.State == Attached { + waitResult = info + return true, nil + } + } + + return false, nil + }) + + return waitResult, err +} + +// waitForStorageAttachmentToBeDeleted waits for the storage attachment with the given name to be fully deleted, or times out. +func (c *StorageAttachmentsClient) waitForStorageAttachmentToBeDeleted(name string, timeout time.Duration) error { + return c.client.WaitFor("storage attachment to be deleted", timeout, func() (bool, error) { + input := &GetStorageAttachmentInput{ + Name: name, + } + _, err := c.GetStorageAttachment(input) + if err != nil { + if client.WasNotFoundError(err) { + return true, nil + } + return false, err + } + return false, nil + }) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_snapshots.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_snapshots.go new file mode 100644 index 000000000..aa7c4a7a2 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volume_snapshots.go @@ -0,0 +1,251 @@ +package compute + +import ( + "fmt" + "strings" + "time" + + "github.com/hashicorp/go-oracle-terraform/client" +) + +const ( + StorageVolumeSnapshotDescription = "storage volume snapshot" + StorageVolumeSnapshotContainerPath = "/storage/snapshot/" + StorageVolumeSnapshotResourcePath = "/storage/snapshot" + + WaitForSnapshotCreateTimeout = time.Duration(2400 * time.Second) + WaitForSnapshotDeleteTimeout = time.Duration(1500 * time.Second) + + // Collocated Snapshot Property + SnapshotPropertyCollocated = "/oracle/private/storage/snapshot/collocated" +) + +// StorageVolumeSnapshotClient is a client for the Storage Volume Snapshot functions of the Compute API. +type StorageVolumeSnapshotClient struct { + ResourceClient +} + +func (c *ComputeClient) StorageVolumeSnapshots() *StorageVolumeSnapshotClient { + return &StorageVolumeSnapshotClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: StorageVolumeSnapshotDescription, + ContainerPath: StorageVolumeSnapshotContainerPath, + ResourceRootPath: StorageVolumeSnapshotResourcePath, + }, + } +} + +// StorageVolumeSnapshotInfo represents the information retrieved from the service about a storage volume snapshot +type StorageVolumeSnapshotInfo struct { + // Account to use for snapshots + Account string `json:"account"` + + // Description of the snapshot + Description string `json:"description"` + + // The name of the machine image that's used in the boot volume from which this snapshot is taken + MachineImageName string `json:"machineimage_name"` + + // Name of the snapshot + Name string `json:"name"` + + // String indicating whether the parent volume is bootable or not + ParentVolumeBootable string `json:"parent_volume_bootable"` + + // Platform the snapshot is compatible with + Platform string `json:"platform"` + + // String determining whether the snapshot is remote or collocated + Property string `json:"property"` + + // The size of the snapshot in GB + Size string `json:"size"` + + // The ID of the snapshot. Generated by the server + SnapshotID string `json:"snapshot_id"` + + // The timestamp of the storage snapshot + SnapshotTimestamp string `json:"snapshot_timestamp"` + + // Timestamp for when the operation started + StartTimestamp string `json:"start_timestamp"` + + // Status of the snapshot + Status string `json:"status"` + + // Status Detail of the storage snapshot + StatusDetail string `json:"status_detail"` + + // Indicates the time that the current view of the storage volume snapshot was generated. + StatusTimestamp string `json:"status_timestamp"` + + // Array of tags for the snapshot + Tags []string `json:"tags,omitempty"` + + // Uniform Resource Identifier + URI string `json:"uri"` + + // Name of the parent storage volume for the snapshot + Volume string `json:"volume"` +} + +// CreateStorageVolumeSnapshotInput represents the body of an API request to create a new storage volume snapshot +type CreateStorageVolumeSnapshotInput struct { + // Description of the snapshot + // Optional + Description string `json:"description,omitempty"` + + // Name of the snapshot + // Optional, will be generated if not specified + Name string `json:"name,omitempty"` + + // Whether or not the parent volume is bootable + // Optional + ParentVolumeBootable string `json:"parent_volume_bootable,omitempty"` + + // Whether collocated or remote + // Optional, will be remote if unspecified + Property string `json:"property,omitempty"` + + // Array of tags for the snapshot + // Optional + Tags []string `json:"tags,omitempty"` + + // Name of the volume to create the snapshot from + // Required + Volume string `json:"volume"` + + // Timeout to wait for snapshot to be completed. Will use default if unspecified + Timeout time.Duration `json:"-"` +} + +// CreateStorageVolumeSnapshot creates a snapshot based on the supplied information struct +func (c *StorageVolumeSnapshotClient) CreateStorageVolumeSnapshot(input *CreateStorageVolumeSnapshotInput) (*StorageVolumeSnapshotInfo, error) { + if input.Name != "" { + input.Name = c.getQualifiedName(input.Name) + } + input.Volume = c.getQualifiedName(input.Volume) + + var storageSnapshotInfo StorageVolumeSnapshotInfo + if err := c.createResource(&input, &storageSnapshotInfo); err != nil { + return nil, err + } + + if input.Timeout == 0 { + input.Timeout = WaitForSnapshotCreateTimeout + } + + // The name of the snapshot could have been generated. Use the response name as input + return c.waitForStorageSnapshotAvailable(storageSnapshotInfo.Name, input.Timeout) +} + +// GetStorageVolumeSnapshotInput represents the body of an API request to get information on a storage volume snapshot +type GetStorageVolumeSnapshotInput struct { + // Name of the snapshot + Name string `json:"name"` +} + +// GetStorageVolumeSnapshot makes an API request to populate information on a storage volume snapshot +func (c *StorageVolumeSnapshotClient) GetStorageVolumeSnapshot(input *GetStorageVolumeSnapshotInput) (*StorageVolumeSnapshotInfo, error) { + var storageSnapshot StorageVolumeSnapshotInfo + input.Name = c.getQualifiedName(input.Name) + if err := c.getResource(input.Name, &storageSnapshot); err != nil { + if client.WasNotFoundError(err) { + return nil, nil + } + + return nil, err + } + return c.success(&storageSnapshot) +} + +// DeleteStorageVolumeSnapshotInput represents the body of an API request to delete a storage volume snapshot +type DeleteStorageVolumeSnapshotInput struct { + // Name of the snapshot to delete + Name string `json:"name"` + + // Timeout to wait for deletion, will use default if unspecified + Timeout time.Duration `json:"-"` +} + +// DeleteStoragevolumeSnapshot makes an API request to delete a storage volume snapshot +func (c *StorageVolumeSnapshotClient) DeleteStorageVolumeSnapshot(input *DeleteStorageVolumeSnapshotInput) error { + input.Name = c.getQualifiedName(input.Name) + + if err := c.deleteResource(input.Name); err != nil { + return err + } + + if input.Timeout == 0 { + input.Timeout = WaitForSnapshotDeleteTimeout + } + + return c.waitForStorageSnapshotDeleted(input.Name, input.Timeout) +} + +func (c *StorageVolumeSnapshotClient) success(result *StorageVolumeSnapshotInfo) (*StorageVolumeSnapshotInfo, error) { + c.unqualify(&result.Name) + c.unqualify(&result.Volume) + + sizeInGigaBytes, err := sizeInGigaBytes(result.Size) + if err != nil { + return nil, err + } + result.Size = sizeInGigaBytes + + return result, nil +} + +// Waits for a storage snapshot to become available +func (c *StorageVolumeSnapshotClient) waitForStorageSnapshotAvailable(name string, timeout time.Duration) (*StorageVolumeSnapshotInfo, error) { + var result *StorageVolumeSnapshotInfo + + err := c.client.WaitFor( + fmt.Sprintf("storage volume snapshot %s to become available", c.getQualifiedName(name)), + timeout, + func() (bool, error) { + req := &GetStorageVolumeSnapshotInput{ + Name: name, + } + res, err := c.GetStorageVolumeSnapshot(req) + if err != nil { + return false, err + } + + if res != nil { + result = res + if strings.ToLower(result.Status) == "completed" { + return true, nil + } else if strings.ToLower(result.Status) == "error" { + return false, fmt.Errorf("Snapshot '%s' failed to create successfully. Status: %s Status Detail: %s", result.Name, result.Status, result.StatusDetail) + } + } + + return false, nil + }) + + return result, err +} + +// Waits for a storage snapshot to be deleted +func (c *StorageVolumeSnapshotClient) waitForStorageSnapshotDeleted(name string, timeout time.Duration) error { + return c.client.WaitFor( + fmt.Sprintf("storage volume snapshot %s to be deleted", c.getQualifiedName(name)), + timeout, + func() (bool, error) { + req := &GetStorageVolumeSnapshotInput{ + Name: name, + } + res, err := c.GetStorageVolumeSnapshot(req) + if res == nil { + return true, nil + } + + if err != nil { + return false, err + } + + return res == nil, nil + }) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volumes.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volumes.go new file mode 100644 index 000000000..c479638de --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/storage_volumes.go @@ -0,0 +1,389 @@ +package compute + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/hashicorp/go-oracle-terraform/client" +) + +const WaitForVolumeReadyTimeout = time.Duration(600 * time.Second) +const WaitForVolumeDeleteTimeout = time.Duration(600 * time.Second) + +// StorageVolumeClient is a client for the Storage Volume functions of the Compute API. +type StorageVolumeClient struct { + ResourceClient +} + +// StorageVolumes obtains a StorageVolumeClient which can be used to access to the +// Storage Volume functions of the Compute API +func (c *ComputeClient) StorageVolumes() *StorageVolumeClient { + return &StorageVolumeClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "storage volume", + ContainerPath: "/storage/volume/", + ResourceRootPath: "/storage/volume", + }} + +} + +type StorageVolumeKind string + +const ( + StorageVolumeKindDefault StorageVolumeKind = "/oracle/public/storage/default" + StorageVolumeKindLatency StorageVolumeKind = "/oracle/public/storage/latency" + StorageVolumeKindSSD StorageVolumeKind = "/oracle/public/storage/ssd/gpl" +) + +// StorageVolumeInfo represents information retrieved from the service about a Storage Volume. +type StorageVolumeInfo struct { + // Shows the default account for your identity domain. + Account string `json:"account,omitempty"` + + // true indicates that the storage volume can also be used as a boot disk for an instance. + // If you set the value to true, then you must specify values for the `ImageList` and `ImageListEntry` fields. + Bootable bool `json:"bootable,omitempty"` + + // The description of the storage volume. + Description string `json:"description,omitempty"` + + // The hypervisor that this volume is compatible with. + Hypervisor string `json:"hypervisor,omitempty"` + + // Name of machine image to extract onto this volume when created. This information is provided only for bootable storage volumes. + ImageList string `json:"imagelist,omitempty"` + + // Specific imagelist entry version to extract. + ImageListEntry int `json:"imagelist_entry,omitempty"` + + // Three-part name of the machine image. This information is available if the volume is a bootable storage volume. + MachineImage string `json:"machineimage_name,omitempty"` + + // All volumes are managed volumes. Default value is true. + Managed bool `json:"managed,omitempty"` + + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + + // The OS platform this volume is compatible with. + Platform string `json:"platform,omitempty"` + + // The storage-pool property: /oracle/public/storage/latency or /oracle/public/storage/default. + Properties []string `json:"properties,omitempty"` + + // Boolean field indicating whether this volume can be attached as readonly. If set to False the volume will be attached as read-write. + ReadOnly bool `json:"readonly,omitempty"` + + // The size of this storage volume in GB. + Size string `json:"size"` + + // Name of the parent snapshot from which the storage volume is restored or cloned. + Snapshot string `json:"snapshot,omitempty"` + + // Account of the parent snapshot from which the storage volume is restored. + SnapshotAccount string `json:"snapshot_account,omitempty"` + + // Id of the parent snapshot from which the storage volume is restored or cloned. + SnapshotID string `json:"snapshot_id,omitempty"` + + // TODO: this should become a Constant, if/when we have the values + // The current state of the storage volume. + Status string `json:"status,omitempty"` + + // Details about the latest state of the storage volume. + StatusDetail string `json:"status_detail,omitempty"` + + // It indicates the time that the current view of the storage volume was generated. + StatusTimestamp string `json:"status_timestamp,omitempty"` + + // The storage pool from which this volume is allocated. + StoragePool string `json:"storage_pool,omitempty"` + + // Comma-separated strings that tag the storage volume. + Tags []string `json:"tags,omitempty"` + + // Uniform Resource Identifier + URI string `json:"uri,omitempty"` +} + +func (c *StorageVolumeClient) getStorageVolumePath(name string) string { + return c.getObjectPath("/storage/volume", name) +} + +// CreateStorageVolumeInput represents the body of an API request to create a new Storage Volume. +type CreateStorageVolumeInput struct { + // true indicates that the storage volume can also be used as a boot disk for an instance. + // If you set the value to true, then you must specify values for the `ImageList` and `ImageListEntry` fields. + Bootable bool `json:"bootable,omitempty"` + + // The description of the storage volume. + Description string `json:"description,omitempty"` + + // Name of machine image to extract onto this volume when created. This information is provided only for bootable storage volumes. + ImageList string `json:"imagelist,omitempty"` + + // Specific imagelist entry version to extract. + ImageListEntry int `json:"imagelist_entry,omitempty"` + + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + + // The storage-pool property: /oracle/public/storage/latency or /oracle/public/storage/default. + Properties []string `json:"properties,omitempty"` + + // The size of this storage volume in GB. + Size string `json:"size"` + + // Name of the parent snapshot from which the storage volume is restored or cloned. + Snapshot string `json:"snapshot,omitempty"` + + // Account of the parent snapshot from which the storage volume is restored. + SnapshotAccount string `json:"snapshot_account,omitempty"` + + // Id of the parent snapshot from which the storage volume is restored or cloned. + SnapshotID string `json:"snapshot_id,omitempty"` + + // Comma-separated strings that tag the storage volume. + Tags []string `json:"tags,omitempty"` + + // Timeout to wait for storage volume creation. + Timeout time.Duration `json:"-"` +} + +// CreateStorageVolume uses the given CreateStorageVolumeInput to create a new Storage Volume. +func (c *StorageVolumeClient) CreateStorageVolume(input *CreateStorageVolumeInput) (*StorageVolumeInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.ImageList = c.getQualifiedName(input.ImageList) + + sizeInBytes, err := sizeInBytes(input.Size) + if err != nil { + return nil, err + } + input.Size = sizeInBytes + + var storageInfo StorageVolumeInfo + if err := c.createResource(&input, &storageInfo); err != nil { + return nil, err + } + + // Should never be nil, as we set this in the provider; but protect against it anyways. + if input.Timeout == 0 { + input.Timeout = WaitForVolumeReadyTimeout + } + + volume, err := c.waitForStorageVolumeToBecomeAvailable(input.Name, input.Timeout) + if err != nil { + if volume != nil { + deleteInput := &DeleteStorageVolumeInput{ + Name: volume.Name, + } + + if err := c.DeleteStorageVolume(deleteInput); err != nil { + return nil, err + } + } + } + return volume, err +} + +// DeleteStorageVolumeInput represents the body of an API request to delete a Storage Volume. +type DeleteStorageVolumeInput struct { + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + + // Timeout to wait for storage volume deletion + Timeout time.Duration `json:"-"` +} + +// DeleteStorageVolume deletes the specified storage volume. +func (c *StorageVolumeClient) DeleteStorageVolume(input *DeleteStorageVolumeInput) error { + if err := c.deleteResource(input.Name); err != nil { + return err + } + + // Should never be nil, but protect against it anyways + if input.Timeout == 0 { + input.Timeout = WaitForVolumeDeleteTimeout + } + + return c.waitForStorageVolumeToBeDeleted(input.Name, input.Timeout) +} + +// GetStorageVolumeInput represents the body of an API request to obtain a Storage Volume. +type GetStorageVolumeInput struct { + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` +} + +func (c *StorageVolumeClient) success(result *StorageVolumeInfo) (*StorageVolumeInfo, error) { + c.unqualify(&result.ImageList) + c.unqualify(&result.Name) + c.unqualify(&result.Snapshot) + + sizeInMegaBytes, err := sizeInGigaBytes(result.Size) + if err != nil { + return nil, err + } + result.Size = sizeInMegaBytes + + return result, nil +} + +// GetStorageVolume gets Storage Volume information for the specified storage volume. +func (c *StorageVolumeClient) GetStorageVolume(input *GetStorageVolumeInput) (*StorageVolumeInfo, error) { + var storageVolume StorageVolumeInfo + if err := c.getResource(input.Name, &storageVolume); err != nil { + if client.WasNotFoundError(err) { + return nil, nil + } + + return nil, err + } + + return c.success(&storageVolume) +} + +// UpdateStorageVolumeInput represents the body of an API request to update a Storage Volume. +type UpdateStorageVolumeInput struct { + // The description of the storage volume. + Description string `json:"description,omitempty"` + + // Name of machine image to extract onto this volume when created. This information is provided only for bootable storage volumes. + ImageList string `json:"imagelist,omitempty"` + + // Specific imagelist entry version to extract. + ImageListEntry int `json:"imagelist_entry,omitempty"` + + // The three-part name of the object (/Compute-identity_domain/user/object). + Name string `json:"name"` + + // The storage-pool property: /oracle/public/storage/latency or /oracle/public/storage/default. + Properties []string `json:"properties,omitempty"` + + // The size of this storage volume in GB. + Size string `json:"size"` + + // Name of the parent snapshot from which the storage volume is restored or cloned. + Snapshot string `json:"snapshot,omitempty"` + + // Account of the parent snapshot from which the storage volume is restored. + SnapshotAccount string `json:"snapshot_account,omitempty"` + + // Id of the parent snapshot from which the storage volume is restored or cloned. + SnapshotID string `json:"snapshot_id,omitempty"` + + // Comma-separated strings that tag the storage volume. + Tags []string `json:"tags,omitempty"` + + // Time to wait for storage volume ready + Timeout time.Duration `json:"-"` +} + +// UpdateStorageVolume updates the specified storage volume, optionally modifying size, description and tags. +func (c *StorageVolumeClient) UpdateStorageVolume(input *UpdateStorageVolumeInput) (*StorageVolumeInfo, error) { + input.Name = c.getQualifiedName(input.Name) + input.ImageList = c.getQualifiedName(input.ImageList) + + sizeInBytes, err := sizeInBytes(input.Size) + if err != nil { + return nil, err + } + input.Size = sizeInBytes + + path := c.getStorageVolumePath(input.Name) + _, err = c.executeRequest("PUT", path, input) + if err != nil { + return nil, err + } + + if input.Timeout == 0 { + input.Timeout = WaitForVolumeReadyTimeout + } + + volumeInfo, err := c.waitForStorageVolumeToBecomeAvailable(input.Name, input.Timeout) + if err != nil { + return nil, err + } + + return volumeInfo, nil +} + +// waitForStorageVolumeToBecomeAvailable waits until a new Storage Volume is available (i.e. has finished initialising or updating). +func (c *StorageVolumeClient) waitForStorageVolumeToBecomeAvailable(name string, timeout time.Duration) (*StorageVolumeInfo, error) { + var waitResult *StorageVolumeInfo + + err := c.client.WaitFor( + fmt.Sprintf("storage volume %s to become available", c.getQualifiedName(name)), + timeout, + func() (bool, error) { + getRequest := &GetStorageVolumeInput{ + Name: name, + } + result, err := c.GetStorageVolume(getRequest) + + if err != nil { + return false, err + } + + if result != nil { + waitResult = result + if strings.ToLower(waitResult.Status) == "online" { + return true, nil + } + if strings.ToLower(waitResult.Status) == "error" { + return false, fmt.Errorf("Error Creating Storage Volume: %s", waitResult.StatusDetail) + } + } + + return false, nil + }) + + return waitResult, err +} + +// waitForStorageVolumeToBeDeleted waits until the specified storage volume has been deleted. +func (c *StorageVolumeClient) waitForStorageVolumeToBeDeleted(name string, timeout time.Duration) error { + return c.client.WaitFor( + fmt.Sprintf("storage volume %s to be deleted", c.getQualifiedName(name)), + timeout, + func() (bool, error) { + getRequest := &GetStorageVolumeInput{ + Name: name, + } + result, err := c.GetStorageVolume(getRequest) + if result == nil { + return true, nil + } + + if err != nil { + return false, err + } + + return result == nil, nil + }) +} + +func sizeInGigaBytes(input string) (string, error) { + sizeInBytes, err := strconv.Atoi(input) + if err != nil { + return "", err + } + sizeInKB := sizeInBytes / 1024 + sizeInMB := sizeInKB / 1024 + sizeInGb := sizeInMB / 1024 + return strconv.Itoa(sizeInGb), nil +} + +func sizeInBytes(input string) (string, error) { + sizeInGB, err := strconv.Atoi(input) + if err != nil { + return "", err + } + sizeInMB := sizeInGB * 1024 + sizeInKB := sizeInMB * 1024 + sizeInBytes := sizeInKB * 1024 + return strconv.Itoa(sizeInBytes), nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/test_utils.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/test_utils.go new file mode 100644 index 000000000..5667e097e --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/test_utils.go @@ -0,0 +1,119 @@ +package compute + +import ( + "bytes" + "encoding/json" + "log" + "net/http" + "net/http/httptest" + "net/url" + "os" + "testing" + "time" + + "github.com/hashicorp/go-oracle-terraform/opc" +) + +const ( + _ClientTestUser = "test-user" + _ClientTestDomain = "test-domain" +) + +func newAuthenticatingServer(handler func(w http.ResponseWriter, r *http.Request)) *httptest.Server { + return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if os.Getenv("ORACLE_LOG") != "" { + log.Printf("[DEBUG] Received request: %s, %s\n", r.Method, r.URL) + } + + if r.URL.Path == "/authenticate/" { + http.SetCookie(w, &http.Cookie{Name: "testAuthCookie", Value: "cookie value"}) + // w.WriteHeader(200) + } else { + handler(w, r) + } + })) +} + +func getTestClient(c *opc.Config) (*ComputeClient, error) { + // Build up config with default values if omitted + if c.APIEndpoint == nil { + if os.Getenv("OPC_ENDPOINT") == "" { + panic("OPC_ENDPOINT not set in environment") + } + endpoint, err := url.Parse(os.Getenv("OPC_ENDPOINT")) + if err != nil { + return nil, err + } + c.APIEndpoint = endpoint + } + + if c.IdentityDomain == nil { + domain := os.Getenv("OPC_IDENTITY_DOMAIN") + c.IdentityDomain = &domain + } + + if c.Username == nil { + username := os.Getenv("OPC_USERNAME") + c.Username = &username + } + + if c.Password == nil { + password := os.Getenv("OPC_PASSWORD") + c.Password = &password + } + + if c.HTTPClient == nil { + c.HTTPClient = &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + TLSHandshakeTimeout: 120 * time.Second}, + } + } + + return NewComputeClient(c) +} + +func getBlankTestClient() (*ComputeClient, *httptest.Server, error) { + server := newAuthenticatingServer(func(w http.ResponseWriter, r *http.Request) { + }) + + endpoint, err := url.Parse(server.URL) + if err != nil { + server.Close() + return nil, nil, err + } + + client, err := getTestClient(&opc.Config{ + IdentityDomain: opc.String(_ClientTestDomain), + Username: opc.String(_ClientTestUser), + APIEndpoint: endpoint, + }) + if err != nil { + server.Close() + return nil, nil, err + } + return client, server, nil +} + +// Returns a stub client with default values, and a custom API Endpoint +func getStubClient(endpoint *url.URL) (*ComputeClient, error) { + domain := "test" + username := "test" + password := "test" + config := &opc.Config{ + IdentityDomain: &domain, + Username: &username, + Password: &password, + APIEndpoint: endpoint, + } + return getTestClient(config) +} + +func unmarshalRequestBody(t *testing.T, r *http.Request, target interface{}) { + buf := new(bytes.Buffer) + buf.ReadFrom(r.Body) + err := json.Unmarshal(buf.Bytes(), target) + if err != nil { + t.Fatalf("Error marshalling request: %s", err) + } +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic.go new file mode 100644 index 000000000..44509d7c9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic.go @@ -0,0 +1,52 @@ +package compute + +type VirtNICsClient struct { + ResourceClient +} + +func (c *ComputeClient) VirtNICs() *VirtNICsClient { + return &VirtNICsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "Virtual NIC", + ContainerPath: "/network/v1/vnic/", + ResourceRootPath: "/network/v1/vnic", + }, + } +} + +type VirtualNIC struct { + // Description of the object. + Description string `json:"description"` + // MAC address of this VNIC. + MACAddress string `json:"macAddress"` + // The three-part name (/Compute-identity_domain/user/object) of the Virtual NIC. + Name string `json:"name"` + // Tags associated with the object. + Tags []string `json:"tags"` + // True if the VNIC is of type "transit". + TransitFlag bool `json:"transitFlag"` + // Uniform Resource Identifier + Uri string `json:"uri"` +} + +// Can only GET a virtual NIC, not update, create, or delete +type GetVirtualNICInput struct { + // The three-part name (/Compute-identity_domain/user/object) of the Virtual NIC. + // Required + Name string `json:"name"` +} + +func (c *VirtNICsClient) GetVirtualNIC(input *GetVirtualNICInput) (*VirtualNIC, error) { + var virtNIC VirtualNIC + input.Name = c.getQualifiedName(input.Name) + if err := c.getResource(input.Name, &virtNIC); err != nil { + return nil, err + } + return c.success(&virtNIC) +} + +func (c *VirtNICsClient) success(info *VirtualNIC) (*VirtualNIC, error) { + c.unqualify(&info.Name) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic_sets.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic_sets.go new file mode 100644 index 000000000..85762e52f --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/virtual_nic_sets.go @@ -0,0 +1,154 @@ +package compute + +type VirtNICSetsClient struct { + ResourceClient +} + +func (c *ComputeClient) VirtNICSets() *VirtNICSetsClient { + return &VirtNICSetsClient{ + ResourceClient: ResourceClient{ + ComputeClient: c, + ResourceDescription: "Virtual NIC Set", + ContainerPath: "/network/v1/vnicset/", + ResourceRootPath: "/network/v1/vnicset", + }, + } +} + +// Describes an existing virtual nic set +type VirtualNICSet struct { + // List of ACLs applied to the VNICs in the set. + AppliedACLs []string `json:"appliedAcls"` + // Description of the VNIC Set. + Description string `json:"description"` + // Name of the VNIC set. + Name string `json:"name"` + // The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set. + Tags []string `json:"tags"` + // Uniform Resource Identifier + Uri string `json:"uri"` + // List of VNICs associated with this VNIC set. + VirtualNICs []string `json:"vnics"` +} + +type CreateVirtualNICSetInput struct { + // List of ACLs applied to the VNICs in the set. + // Optional + AppliedACLs []string `json:"appliedAcls"` + // Description of the object. + // Optional + Description string `json:"description"` + // The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set. + // Object names can contain only alphanumeric, underscore (_), dash (-), and period (.) characters. Object names are case-sensitive. + // Required + Name string `json:"name"` + // Tags associated with this VNIC set. + // Optional + Tags []string `json:"tags"` + // List of VNICs associated with this VNIC set. + // Optional + VirtualNICs []string `json:"vnics"` +} + +func (c *VirtNICSetsClient) CreateVirtualNICSet(input *CreateVirtualNICSetInput) (*VirtualNICSet, error) { + input.Name = c.getQualifiedName(input.Name) + input.AppliedACLs = c.getQualifiedAcls(input.AppliedACLs) + qualifiedNics := c.getQualifiedList(input.VirtualNICs) + if len(qualifiedNics) != 0 { + input.VirtualNICs = qualifiedNics + } + + var virtNicSet VirtualNICSet + if err := c.createResource(input, &virtNicSet); err != nil { + return nil, err + } + + return c.success(&virtNicSet) +} + +type GetVirtualNICSetInput struct { + // The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set. + // Required + Name string `json:"name"` +} + +func (c *VirtNICSetsClient) GetVirtualNICSet(input *GetVirtualNICSetInput) (*VirtualNICSet, error) { + var virtNicSet VirtualNICSet + // Qualify Name + input.Name = c.getQualifiedName(input.Name) + if err := c.getResource(input.Name, &virtNicSet); err != nil { + return nil, err + } + + return c.success(&virtNicSet) +} + +type UpdateVirtualNICSetInput struct { + // List of ACLs applied to the VNICs in the set. + // Optional + AppliedACLs []string `json:"appliedAcls"` + // Description of the object. + // Optional + Description string `json:"description"` + // The three-part name (/Compute-identity_domain/user/object) of the virtual NIC set. + // Object names can contain only alphanumeric, underscore (_), dash (-), and period (.) characters. Object names are case-sensitive. + // Required + Name string `json:"name"` + // Tags associated with this VNIC set. + // Optional + Tags []string `json:"tags"` + // List of VNICs associated with this VNIC set. + // Optional + VirtualNICs []string `json:"vnics"` +} + +func (c *VirtNICSetsClient) UpdateVirtualNICSet(input *UpdateVirtualNICSetInput) (*VirtualNICSet, error) { + input.Name = c.getQualifiedName(input.Name) + input.AppliedACLs = c.getQualifiedAcls(input.AppliedACLs) + // Qualify VirtualNICs + qualifiedVNICs := c.getQualifiedList(input.VirtualNICs) + if len(qualifiedVNICs) != 0 { + input.VirtualNICs = qualifiedVNICs + } + + var virtNICSet VirtualNICSet + if err := c.updateResource(input.Name, input, &virtNICSet); err != nil { + return nil, err + } + + return c.success(&virtNICSet) +} + +type DeleteVirtualNICSetInput struct { + // The name of the virtual NIC set. + // Required + Name string `json:"name"` +} + +func (c *VirtNICSetsClient) DeleteVirtualNICSet(input *DeleteVirtualNICSetInput) error { + input.Name = c.getQualifiedName(input.Name) + return c.deleteResource(input.Name) +} + +func (c *VirtNICSetsClient) getQualifiedAcls(acls []string) []string { + qualifiedAcls := []string{} + for _, acl := range acls { + qualifiedAcls = append(qualifiedAcls, c.getQualifiedName(acl)) + } + return qualifiedAcls +} + +func (c *VirtNICSetsClient) unqualifyAcls(acls []string) []string { + unqualifiedAcls := []string{} + for _, acl := range acls { + unqualifiedAcls = append(unqualifiedAcls, c.getUnqualifiedName(acl)) + } + return unqualifiedAcls +} + +func (c *VirtNICSetsClient) success(info *VirtualNICSet) (*VirtualNICSet, error) { + c.unqualify(&info.Name) + info.AppliedACLs = c.unqualifyAcls(info.AppliedACLs) + info.VirtualNICs = c.getUnqualifiedList(info.VirtualNICs) + return info, nil +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/opc/config.go b/vendor/github.com/hashicorp/go-oracle-terraform/opc/config.go new file mode 100644 index 000000000..5c144a66d --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/opc/config.go @@ -0,0 +1,22 @@ +package opc + +import ( + "net/http" + "net/url" +) + +type Config struct { + Username *string + Password *string + IdentityDomain *string + APIEndpoint *url.URL + MaxRetries *int + LogLevel LogLevelType + Logger Logger + HTTPClient *http.Client + UserAgent *string +} + +func NewConfig() *Config { + return &Config{} +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/opc/convert.go b/vendor/github.com/hashicorp/go-oracle-terraform/opc/convert.go new file mode 100644 index 000000000..52fa08902 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/opc/convert.go @@ -0,0 +1,9 @@ +package opc + +func String(v string) *string { + return &v +} + +func Int(v int) *int { + return &v +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/opc/errors.go b/vendor/github.com/hashicorp/go-oracle-terraform/opc/errors.go new file mode 100644 index 000000000..6b12c10d9 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/opc/errors.go @@ -0,0 +1,12 @@ +package opc + +import "fmt" + +type OracleError struct { + StatusCode int + Message string +} + +func (e OracleError) Error() string { + return fmt.Sprintf("%d: %s", e.StatusCode, e.Message) +} diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/opc/logger.go b/vendor/github.com/hashicorp/go-oracle-terraform/opc/logger.go new file mode 100644 index 000000000..f9714a7a8 --- /dev/null +++ b/vendor/github.com/hashicorp/go-oracle-terraform/opc/logger.go @@ -0,0 +1,70 @@ +package opc + +import ( + "io" + "io/ioutil" + "log" + "os" +) + +const ( + LogOff LogLevelType = 0 + LogDebug LogLevelType = 1 +) + +type LogLevelType uint + +// Logger interface. Should be satisfied by Terraform's logger as well as the Default logger +type Logger interface { + Log(...interface{}) +} + +type LoggerFunc func(...interface{}) + +func (f LoggerFunc) Log(args ...interface{}) { + f(args...) +} + +// Returns a default logger if one isn't specified during configuration +func NewDefaultLogger() Logger { + logWriter, err := LogOutput() + if err != nil { + log.Fatalf("Error setting up log writer: %s", err) + } + return &defaultLogger{ + logger: log.New(logWriter, "", log.LstdFlags), + } +} + +// Default logger to satisfy the logger interface +type defaultLogger struct { + logger *log.Logger +} + +func (l defaultLogger) Log(args ...interface{}) { + l.logger.Println(args...) +} + +func LogOutput() (logOutput io.Writer, err error) { + // Default to nil + logOutput = ioutil.Discard + + logLevel := LogLevel() + if logLevel == LogOff { + return + } + + // Logging is on, set output to STDERR + logOutput = os.Stderr + return +} + +// Gets current Log Level from the ORACLE_LOG env var +func LogLevel() LogLevelType { + envLevel := os.Getenv("ORACLE_LOG") + if envLevel == "" { + return LogOff + } else { + return LogDebug + } +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 2f8f8307d..fe36fe283 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -796,6 +796,24 @@ "revision": "5a9a298c54339d2296d2f1135eae55a3a8f5e8c2", "revisionTime": "2018-01-11T20:31:13Z" }, + { + "checksumSHA1": "hjQfXn32Tvuu6IJACOTsMzm+AbA=", + "path": "github.com/hashicorp/go-oracle-terraform/client", + "revision": "5a9a298c54339d2296d2f1135eae55a3a8f5e8c2", + "revisionTime": "2018-01-11T20:31:13Z" + }, + { + "checksumSHA1": "RhoE7zmHsn5zoXbx5AsAUUQI72E=", + "path": "github.com/hashicorp/go-oracle-terraform/compute", + "revision": "5a9a298c54339d2296d2f1135eae55a3a8f5e8c2", + "revisionTime": "2018-01-11T20:31:13Z" + }, + { + "checksumSHA1": "NuObCk0/ybL3w++EnltgrB1GQRc=", + "path": "github.com/hashicorp/go-oracle-terraform/opc", + "revision": "5a9a298c54339d2296d2f1135eae55a3a8f5e8c2", + "revisionTime": "2018-01-11T20:31:13Z" + }, { "checksumSHA1": "ErJHGU6AVPZM9yoY/xV11TwSjQs=", "path": "github.com/hashicorp/go-retryablehttp", From 75ee66f934aa5aff050628a22802c28979617c5a Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 12 Jan 2018 16:06:03 -0800 Subject: [PATCH 05/61] add stubbed out steps --- .../classic/step_create_IP_reservation.go | 24 +++++++++++++ .../oracle/classic/step_create_instance.go | 34 +++++++++++++++++++ builder/oracle/classic/step_instance_info.go | 24 +++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 builder/oracle/classic/step_create_IP_reservation.go create mode 100644 builder/oracle/classic/step_create_instance.go create mode 100644 builder/oracle/classic/step_instance_info.go diff --git a/builder/oracle/classic/step_create_IP_reservation.go b/builder/oracle/classic/step_create_IP_reservation.go new file mode 100644 index 000000000..a719999dd --- /dev/null +++ b/builder/oracle/classic/step_create_IP_reservation.go @@ -0,0 +1,24 @@ +package classic + +import ( + "github.com/hashicorp/packer/packer" + "github.com/mitchellh/multistep" +) + +type stepCreateIPReservation struct{} + +func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + ui.Say("Creating IP reservation...") + const endpoint_path = "/ip/reservation/" // POST + + // $ opc compute ip-reservations add \ + // /Compute-mydomain/user@example.com/master-instance-ip \ + // /oracle/public/ippool + + // account /Compute-mydomain/default + // ip 129.144.27.172 + // name /Compute-mydomain/user@example.com/master-instance-ip + // ... + +} diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go new file mode 100644 index 000000000..6678979bf --- /dev/null +++ b/builder/oracle/classic/step_create_instance.go @@ -0,0 +1,34 @@ +package classic + +import ( + "github.com/hashicorp/packer/packer" + "github.com/mitchellh/multistep" +) + +type stepCreateIPReservation struct{} + +func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + ui.Say("Creating Instance...") + const endpoint_path = "/launchplan/" // POST + // master-instance.json + + // { + // "instances": [{ + // "shape": "oc3", + // "sshkeys": ["/Compute-mydomain/user@example.com/my_sshkey"], + // "name": "Compute-mydomain/user@example.com/master-instance", + // "label": "master-instance", + // "imagelist": "/Compute-mydomain/user@example.com/Ubuntu.16.04-LTS.amd64.20170330", + // "networking": { + // "eth0": { + // "nat": "ipreservation:/Compute-mydomain/user@example.com/master-instance-ip" + // } + // } + // }] + // } + // command line call + // $ opc compute launch-plans add --request-body=./master-instance.json + // ... + +} diff --git a/builder/oracle/classic/step_instance_info.go b/builder/oracle/classic/step_instance_info.go new file mode 100644 index 000000000..a2f935a99 --- /dev/null +++ b/builder/oracle/classic/step_instance_info.go @@ -0,0 +1,24 @@ +package classic + +import ( + "github.com/hashicorp/packer/packer" + "github.com/mitchellh/multistep" +) + +type stepCreateIPReservation struct{} + +func (s *stepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction { + ui := state.Get("ui").(packer.Ui) + ui.Say("Getting Instance Info...") + endpoint_path := "/instance/%s", instanceName // GET + + // $ opc compute ip-reservations add \ + // /Compute-mydomain/user@example.com/master-instance-ip \ + // /oracle/public/ippool + + // account /Compute-mydomain/default + // ip 129.144.27.172 + // name /Compute-mydomain/user@example.com/master-instance-ip + // ... + +} From 4fe89be32ad368915570edd2e5cdc09b4feef406 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 12 Jan 2018 16:18:55 -0800 Subject: [PATCH 06/61] fleshing out steps --- .../oracle/classic/step_create_instance.go | 46 ++++++++++++------- builder/oracle/classic/step_instance_info.go | 16 ++----- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 6678979bf..4b9d33c0a 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -1,34 +1,48 @@ package classic import ( + "fmt" + "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" ) -type stepCreateIPReservation struct{} +type stepCreateInstance struct{} func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) ui.Say("Creating Instance...") const endpoint_path = "/launchplan/" // POST // master-instance.json - - // { - // "instances": [{ - // "shape": "oc3", - // "sshkeys": ["/Compute-mydomain/user@example.com/my_sshkey"], - // "name": "Compute-mydomain/user@example.com/master-instance", - // "label": "master-instance", - // "imagelist": "/Compute-mydomain/user@example.com/Ubuntu.16.04-LTS.amd64.20170330", - // "networking": { - // "eth0": { - // "nat": "ipreservation:/Compute-mydomain/user@example.com/master-instance-ip" - // } - // } - // }] - // } + ` + { + "instances": [{ + "shape": "oc3", + "sshkeys": ["/Compute-mydomain/user@example.com/my_sshkey"], + "name": "Compute-mydomain/user@example.com/master-instance", + "label": "master-instance", + "imagelist": "/Compute-mydomain/user@example.com/Ubuntu.16.04-LTS.amd64.20170330", + "networking": { + "eth0": { + "nat": "ipreservation:/Compute-mydomain/user@example.com/master-instance-ip" + } + } + }] + } + ` // command line call // $ opc compute launch-plans add --request-body=./master-instance.json // ... + instanceID, err := client.CreateInstance(publicKey) + if err != nil { + err = fmt.Errorf("Problem creating instance: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + + state.Put("instance_id", instanceID) + + ui.Say(fmt.Sprintf("Created instance (%s).", instanceID)) } diff --git a/builder/oracle/classic/step_instance_info.go b/builder/oracle/classic/step_instance_info.go index a2f935a99..9ec01cdbc 100644 --- a/builder/oracle/classic/step_instance_info.go +++ b/builder/oracle/classic/step_instance_info.go @@ -5,20 +5,14 @@ import ( "github.com/mitchellh/multistep" ) -type stepCreateIPReservation struct{} +type stepInstanceInfo struct{} func (s *stepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) ui.Say("Getting Instance Info...") - endpoint_path := "/instance/%s", instanceName // GET + ui := state.Get("ui").(packer.Ui) + instanceID := state.Get("instance_id").(string) + endpoint_path := "/instance/%s", instanceID // GET - // $ opc compute ip-reservations add \ - // /Compute-mydomain/user@example.com/master-instance-ip \ - // /oracle/public/ippool - - // account /Compute-mydomain/default - // ip 129.144.27.172 - // name /Compute-mydomain/user@example.com/master-instance-ip - // ... + // https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsa/op-instance-%7Bname%7D-get.html } From a66dfe19721f31a55c7767911cc2ff8e0ae1d8ed Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 12 Jan 2018 16:48:35 -0800 Subject: [PATCH 07/61] fleshing out step_create_instance --- .../oracle/classic/step_create_instance.go | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 4b9d33c0a..dce6b0d78 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -2,34 +2,55 @@ package classic import ( "fmt" + "text/template" "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" ) +type instanceOptions struct { + Username string + IdentityDomain string + SshKey string + Shape string + ImageList string + InstanceIP string +} + +var instanceTemplate = template.Must(template.New("instanceRequestBody").Parse(` +{ + "instances": [{ + "shape": "{{.Shape}}", + "sshkeys": ["/Compute-{{.IdentityDomain}}/{{Username}}/{{.SshKey}}"], + "name": "Compute-{{.IdentityDomain}}/{{Username}}/packer-instance", + "label": "packer-instance", + "imagelist": "/Compute-{{.IdentityDomain}}/{{Username}}/{{.ImageList}}", + "networking": { + "eth0": { + "nat": "ipreservation:/Compute-{{.IdentityDomain}}/{{Username}}/{{.InstanceIP}}" + } + } + }] +} +`)) + type stepCreateInstance struct{} func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - ui.Say("Creating Instance...") + config := state.Get("config").(Config) const endpoint_path = "/launchplan/" // POST - // master-instance.json - ` - { - "instances": [{ - "shape": "oc3", - "sshkeys": ["/Compute-mydomain/user@example.com/my_sshkey"], - "name": "Compute-mydomain/user@example.com/master-instance", - "label": "master-instance", - "imagelist": "/Compute-mydomain/user@example.com/Ubuntu.16.04-LTS.amd64.20170330", - "networking": { - "eth0": { - "nat": "ipreservation:/Compute-mydomain/user@example.com/master-instance-ip" - } - } - }] - } - ` + + ui.Say("Creating Instance...") + + // generate launch plan definition for this instance + err = instanceTemplate.Execute(&buffer, instanceOptions{ + Username: config.Username, + IdentityDomain: config.IdentityDomain, + SshKey: config.SshKey, + Shape: config.Shape, + ImageList: config.ImageList, + }) // command line call // $ opc compute launch-plans add --request-body=./master-instance.json // ... From 0117f537211d1c8716e9515f6a49770ed5eca1f1 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 12 Jan 2018 16:51:59 -0800 Subject: [PATCH 08/61] add error message --- builder/oracle/classic/step_create_instance.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index dce6b0d78..e6fbb8798 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -51,6 +51,10 @@ func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAc Shape: config.Shape, ImageList: config.ImageList, }) + if err != nil { + fmt.Printf("Error creating launch plan definition: %s", err) + return "", err + } // command line call // $ opc compute launch-plans add --request-body=./master-instance.json // ... From 7d72870179895a397acc3c5bff7ba355909ff8f1 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 16 Jan 2018 12:09:42 -0800 Subject: [PATCH 09/61] add buffer to read template into --- builder/oracle/classic/step_create_instance.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index e6fbb8798..e461dd5ab 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -1,6 +1,7 @@ package classic import ( + "bytes" "fmt" "text/template" @@ -44,6 +45,7 @@ func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAc ui.Say("Creating Instance...") // generate launch plan definition for this instance + var buffer bytes.Buffer err = instanceTemplate.Execute(&buffer, instanceOptions{ Username: config.Username, IdentityDomain: config.IdentityDomain, @@ -55,10 +57,8 @@ func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAc fmt.Printf("Error creating launch plan definition: %s", err) return "", err } - // command line call - // $ opc compute launch-plans add --request-body=./master-instance.json - // ... - + // for API docs see + // https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsa/op-launchplan--post.html instanceID, err := client.CreateInstance(publicKey) if err != nil { err = fmt.Errorf("Problem creating instance: %s", err) From 007e8f7c14203ef81b23e82f0ead74e4f185a187 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 16 Jan 2018 14:28:24 -0800 Subject: [PATCH 10/61] finish stubbing out step_create_IP_reservation --- builder/oracle/classic/builder.go | 3 +- .../classic/step_create_IP_reservation.go | 32 ++++++++++++------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index b8af96345..4867ff34e 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/go-oracle-terraform/opc" "github.com/hashicorp/packer/common" + "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" ) @@ -58,7 +59,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the steps steps := []multistep.Step{ - /* &ocommon.StepKeyPair{ Debug: b.config.PackerDebug, DebugKeyPath: fmt.Sprintf("oci_classic_%s.pem", b.config.PackerBuildName), @@ -75,7 +75,6 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &common.StepProvision{}, &stepSnapshot{}, - */ } // Run the steps diff --git a/builder/oracle/classic/step_create_IP_reservation.go b/builder/oracle/classic/step_create_IP_reservation.go index a719999dd..415b3ddac 100644 --- a/builder/oracle/classic/step_create_IP_reservation.go +++ b/builder/oracle/classic/step_create_IP_reservation.go @@ -1,6 +1,9 @@ package classic import ( + "log" + + "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" ) @@ -10,15 +13,22 @@ type stepCreateIPReservation struct{} func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) ui.Say("Creating IP reservation...") - const endpoint_path = "/ip/reservation/" // POST - - // $ opc compute ip-reservations add \ - // /Compute-mydomain/user@example.com/master-instance-ip \ - // /oracle/public/ippool - - // account /Compute-mydomain/default - // ip 129.144.27.172 - // name /Compute-mydomain/user@example.com/master-instance-ip - // ... - + client := state.Get("client", client).(*compute.ComputeClient) + iprClient := client.IPReservations() + if err != nil { + log.Printf("Error getting IPReservations Client: %s", err) + return multistep.ActionHalt + } + // TODO: add optional Name and Tags + IPInput := &iprClient.CreateIPReservationInput{ + ParentPool: compute.PublicReservationPool, + Permanent: true, + } + ipRes, err := iprClient.CreateIPReservation(createIPReservation) + if err != nil { + log.Printf("Error creating IP Reservation: %s", err) + return multistep.ActionHalt + } + log.Printf("debug: ipRes is %#v", ipRes) + return multistep.ActionContinue } From 8aa716cd4cb559dd0ec26a8f1a3be37ba3b7eaee Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 16 Jan 2018 16:55:39 -0800 Subject: [PATCH 11/61] stub out step_snapshot --- .../classic/step_create_IP_reservation.go | 1 + .../oracle/classic/step_create_instance.go | 70 ++++++------------- builder/oracle/classic/step_snapshot.go | 39 +++++++++++ 3 files changed, 61 insertions(+), 49 deletions(-) create mode 100644 builder/oracle/classic/step_snapshot.go diff --git a/builder/oracle/classic/step_create_IP_reservation.go b/builder/oracle/classic/step_create_IP_reservation.go index 415b3ddac..797063b1c 100644 --- a/builder/oracle/classic/step_create_IP_reservation.go +++ b/builder/oracle/classic/step_create_IP_reservation.go @@ -29,6 +29,7 @@ func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAc log.Printf("Error creating IP Reservation: %s", err) return multistep.ActionHalt } + state.Put("instance_ip", ipRes.IP) log.Printf("debug: ipRes is %#v", ipRes) return multistep.ActionContinue } diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index e461dd5ab..f94d6d0b7 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -1,65 +1,37 @@ package classic import ( - "bytes" "fmt" - "text/template" + "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/packer" "github.com/mitchellh/multistep" ) -type instanceOptions struct { - Username string - IdentityDomain string - SshKey string - Shape string - ImageList string - InstanceIP string -} - -var instanceTemplate = template.Must(template.New("instanceRequestBody").Parse(` -{ - "instances": [{ - "shape": "{{.Shape}}", - "sshkeys": ["/Compute-{{.IdentityDomain}}/{{Username}}/{{.SshKey}}"], - "name": "Compute-{{.IdentityDomain}}/{{Username}}/packer-instance", - "label": "packer-instance", - "imagelist": "/Compute-{{.IdentityDomain}}/{{Username}}/{{.ImageList}}", - "networking": { - "eth0": { - "nat": "ipreservation:/Compute-{{.IdentityDomain}}/{{Username}}/{{.InstanceIP}}" - } - } - }] -} -`)) - type stepCreateInstance struct{} -func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAction { - ui := state.Get("ui").(packer.Ui) - config := state.Get("config").(Config) - const endpoint_path = "/launchplan/" // POST - +func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction { ui.Say("Creating Instance...") - // generate launch plan definition for this instance - var buffer bytes.Buffer - err = instanceTemplate.Execute(&buffer, instanceOptions{ - Username: config.Username, - IdentityDomain: config.IdentityDomain, - SshKey: config.SshKey, - Shape: config.Shape, - ImageList: config.ImageList, - }) - if err != nil { - fmt.Printf("Error creating launch plan definition: %s", err) - return "", err + // get variables from state + ui := state.Get("ui").(packer.Ui) + config := state.Get("config").(Config) + client := state.Get("client").(*compute.ComputeClient) + sshPublicKey := state.Get("publicKey").(string) + + // get instances client + instanceClient := client.Instances() + + // Instances Input + input := &compute.CreateInstanceInput{ + Name: config.ImageName, + Shape: config.Shape, + ImageList: config.ImageList, + SSHKeys: []string{}, + Attributes: map[string]interface{}{}, } - // for API docs see - // https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsa/op-launchplan--post.html - instanceID, err := client.CreateInstance(publicKey) + + instanceInfo, err := instanceClient.CreateInstance(input) if err != nil { err = fmt.Errorf("Problem creating instance: %s", err) ui.Error(err.Error()) @@ -67,7 +39,7 @@ func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAc return multistep.ActionHalt } - state.Put("instance_id", instanceID) + state.Put("instance_id", instanceInfo.ID) ui.Say(fmt.Sprintf("Created instance (%s).", instanceID)) } diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go new file mode 100644 index 000000000..582966a83 --- /dev/null +++ b/builder/oracle/classic/step_snapshot.go @@ -0,0 +1,39 @@ +package classic + +import ( + "fmt" + + "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/packer" + "github.com/mitchellh/multistep" +) + +type stepSnapshot struct{} + +func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { + // get variables from state + ui := state.Get("ui").(packer.Ui) + ui.Say("Creating Snapshot...") + config := state.Get("config").(Config) + client := state.Get("client").(*compute.ComputeClient) + instanceID := state.Get("instance_id").(string) + + // get instances client + snapshotClient := client.SnapshotsClient() + + // Instances Input + createSnapshotInput := &CreateSnapshotInput{ + Instance: instanceID, + MachineImage: config.ImageName, + } + + snap, err := snapshotClient.CreateSnapshot(input) + if err != nil { + err = fmt.Errorf("Problem creating snapshot: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + + ui.Say(fmt.Sprintf("Created snapshot (%s).", snap.Name)) +} From 46c3113613355d7f9ba928014eaa1dc66d5c7014 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 17 Jan 2018 13:07:41 -0800 Subject: [PATCH 12/61] it compiles :) --- builder/oracle/classic/builder.go | 5 +- builder/oracle/classic/config.go | 1 + .../classic/step_create_IP_reservation.go | 14 +- .../oracle/classic/step_create_instance.go | 13 +- builder/oracle/classic/step_instance_info.go | 18 -- builder/oracle/classic/step_snapshot.go | 11 +- builder/oracle/oci/builder.go | 4 +- builder/oracle/oci/ssh.go | 45 ---- command/plugin.go | 198 +++++++++--------- 9 files changed, 131 insertions(+), 178 deletions(-) delete mode 100644 builder/oracle/classic/step_instance_info.go delete mode 100644 builder/oracle/oci/ssh.go diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 4867ff34e..4501456d2 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/go-oracle-terraform/opc" + ocommon "github.com/hashicorp/packer/builder/oracle/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/packer" @@ -68,8 +69,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &stepCreateInstance{}, &communicator.StepConnect{ Config: &b.config.Comm, - Host: commHost, - SSHConfig: SSHConfig( + Host: ocommon.CommHost, + SSHConfig: ocommon.SSHConfig( b.config.Comm.SSHUsername, b.config.Comm.SSHPassword), }, diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 050f78d91..10ec44c54 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -24,6 +24,7 @@ type Config struct { apiEndpointURL *url.URL // Image + ImageName string `mapstructure:"image_name"` Shape string `mapstructure:"shape"` ImageList string `json:"image_list"` diff --git a/builder/oracle/classic/step_create_IP_reservation.go b/builder/oracle/classic/step_create_IP_reservation.go index 797063b1c..f3f898eaa 100644 --- a/builder/oracle/classic/step_create_IP_reservation.go +++ b/builder/oracle/classic/step_create_IP_reservation.go @@ -13,18 +13,14 @@ type stepCreateIPReservation struct{} func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) ui.Say("Creating IP reservation...") - client := state.Get("client", client).(*compute.ComputeClient) + client := state.Get("client").(*compute.ComputeClient) iprClient := client.IPReservations() - if err != nil { - log.Printf("Error getting IPReservations Client: %s", err) - return multistep.ActionHalt - } // TODO: add optional Name and Tags - IPInput := &iprClient.CreateIPReservationInput{ + IPInput := &compute.CreateIPReservationInput{ ParentPool: compute.PublicReservationPool, Permanent: true, } - ipRes, err := iprClient.CreateIPReservation(createIPReservation) + ipRes, err := iprClient.CreateIPReservation(IPInput) if err != nil { log.Printf("Error creating IP Reservation: %s", err) return multistep.ActionHalt @@ -33,3 +29,7 @@ func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAc log.Printf("debug: ipRes is %#v", ipRes) return multistep.ActionContinue } + +func (s *stepCreateIPReservation) Cleanup(state multistep.StateBag) { + // Nothing to do +} diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index f94d6d0b7..7de599190 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -11,10 +11,9 @@ import ( type stepCreateInstance struct{} func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction { - ui.Say("Creating Instance...") - // get variables from state ui := state.Get("ui").(packer.Ui) + ui.Say("Creating Instance...") config := state.Get("config").(Config) client := state.Get("client").(*compute.ComputeClient) sshPublicKey := state.Get("publicKey").(string) @@ -27,7 +26,7 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction Name: config.ImageName, Shape: config.Shape, ImageList: config.ImageList, - SSHKeys: []string{}, + SSHKeys: []string{sshPublicKey}, Attributes: map[string]interface{}{}, } @@ -40,6 +39,10 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction } state.Put("instance_id", instanceInfo.ID) - - ui.Say(fmt.Sprintf("Created instance (%s).", instanceID)) + ui.Say(fmt.Sprintf("Created instance (%s).", instanceInfo.ID)) + return multistep.ActionContinue +} + +func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { + // Nothing to do } diff --git a/builder/oracle/classic/step_instance_info.go b/builder/oracle/classic/step_instance_info.go deleted file mode 100644 index 9ec01cdbc..000000000 --- a/builder/oracle/classic/step_instance_info.go +++ /dev/null @@ -1,18 +0,0 @@ -package classic - -import ( - "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" -) - -type stepInstanceInfo struct{} - -func (s *stepInstanceInfo) Run(state multistep.StateBag) multistep.StepAction { - ui.Say("Getting Instance Info...") - ui := state.Get("ui").(packer.Ui) - instanceID := state.Get("instance_id").(string) - endpoint_path := "/instance/%s", instanceID // GET - - // https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsa/op-instance-%7Bname%7D-get.html - -} diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index 582966a83..2df8976a1 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -19,15 +19,15 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { instanceID := state.Get("instance_id").(string) // get instances client - snapshotClient := client.SnapshotsClient() + snapshotClient := client.Snapshots() // Instances Input - createSnapshotInput := &CreateSnapshotInput{ + snapshotInput := &compute.CreateSnapshotInput{ Instance: instanceID, MachineImage: config.ImageName, } - snap, err := snapshotClient.CreateSnapshot(input) + snap, err := snapshotClient.CreateSnapshot(snapshotInput) if err != nil { err = fmt.Errorf("Problem creating snapshot: %s", err) ui.Error(err.Error()) @@ -36,4 +36,9 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { } ui.Say(fmt.Sprintf("Created snapshot (%s).", snap.Name)) + return multistep.ActionContinue +} + +func (s *stepSnapshot) Cleanup(state multistep.StateBag) { + // Nothing to do } diff --git a/builder/oracle/oci/builder.go b/builder/oracle/oci/builder.go index 07d41b756..612fdf4f6 100644 --- a/builder/oracle/oci/builder.go +++ b/builder/oracle/oci/builder.go @@ -60,8 +60,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &stepInstanceInfo{}, &communicator.StepConnect{ Config: &b.config.Comm, - Host: commHost, - SSHConfig: SSHConfig( + Host: ocommon.CommHost, + SSHConfig: ocommon.SSHConfig( b.config.Comm.SSHUsername, b.config.Comm.SSHPassword), }, diff --git a/builder/oracle/oci/ssh.go b/builder/oracle/oci/ssh.go deleted file mode 100644 index 5424b94b4..000000000 --- a/builder/oracle/oci/ssh.go +++ /dev/null @@ -1,45 +0,0 @@ -package oci - -import ( - "fmt" - - packerssh "github.com/hashicorp/packer/communicator/ssh" - "github.com/hashicorp/packer/helper/multistep" - "golang.org/x/crypto/ssh" -) - -func commHost(state multistep.StateBag) (string, error) { - ipAddress := state.Get("instance_ip").(string) - return ipAddress, nil -} - -// SSHConfig returns a function that can be used for the SSH communicator -// config for connecting to the instance created over SSH using the private key -// or password. -func SSHConfig(username, password string) func(state multistep.StateBag) (*ssh.ClientConfig, error) { - return func(state multistep.StateBag) (*ssh.ClientConfig, error) { - privateKey, hasKey := state.GetOk("privateKey") - if hasKey { - - signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string))) - if err != nil { - return nil, fmt.Errorf("Error setting up SSH config: %s", err) - } - return &ssh.ClientConfig{ - User: username, - Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - }, nil - - } - - return &ssh.ClientConfig{ - User: username, - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - Auth: []ssh.AuthMethod{ - ssh.Password(password), - ssh.KeyboardInteractive(packerssh.PasswordKeyboardInteractive(password)), - }, - }, nil - } -} diff --git a/command/plugin.go b/command/plugin.go index d9e7ea577..4f4228fc3 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -14,65 +14,67 @@ import ( "github.com/hashicorp/packer/packer/plugin" alicloudecsbuilder "github.com/hashicorp/packer/builder/alicloud/ecs" + alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-import" amazonchrootbuilder "github.com/hashicorp/packer/builder/amazon/chroot" amazonebsbuilder "github.com/hashicorp/packer/builder/amazon/ebs" amazonebssurrogatebuilder "github.com/hashicorp/packer/builder/amazon/ebssurrogate" amazonebsvolumebuilder "github.com/hashicorp/packer/builder/amazon/ebsvolume" - amazoninstancebuilder "github.com/hashicorp/packer/builder/amazon/instance" - azurearmbuilder "github.com/hashicorp/packer/builder/azure/arm" - cloudstackbuilder "github.com/hashicorp/packer/builder/cloudstack" - digitaloceanbuilder "github.com/hashicorp/packer/builder/digitalocean" - dockerbuilder "github.com/hashicorp/packer/builder/docker" - filebuilder "github.com/hashicorp/packer/builder/file" - googlecomputebuilder "github.com/hashicorp/packer/builder/googlecompute" - hypervisobuilder "github.com/hashicorp/packer/builder/hyperv/iso" - hypervvmcxbuilder "github.com/hashicorp/packer/builder/hyperv/vmcx" - lxcbuilder "github.com/hashicorp/packer/builder/lxc" - lxdbuilder "github.com/hashicorp/packer/builder/lxd" - nullbuilder "github.com/hashicorp/packer/builder/null" - oneandonebuilder "github.com/hashicorp/packer/builder/oneandone" - openstackbuilder "github.com/hashicorp/packer/builder/openstack" - oracleocibuilder "github.com/hashicorp/packer/builder/oracle/oci" - parallelsisobuilder "github.com/hashicorp/packer/builder/parallels/iso" - parallelspvmbuilder "github.com/hashicorp/packer/builder/parallels/pvm" - profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks" - qemubuilder "github.com/hashicorp/packer/builder/qemu" - tritonbuilder "github.com/hashicorp/packer/builder/triton" - virtualboxisobuilder "github.com/hashicorp/packer/builder/virtualbox/iso" - virtualboxovfbuilder "github.com/hashicorp/packer/builder/virtualbox/ovf" - vmwareisobuilder "github.com/hashicorp/packer/builder/vmware/iso" - vmwarevmxbuilder "github.com/hashicorp/packer/builder/vmware/vmx" - alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-import" amazonimportpostprocessor "github.com/hashicorp/packer/post-processor/amazon-import" + amazoninstancebuilder "github.com/hashicorp/packer/builder/amazon/instance" + ansiblelocalprovisioner "github.com/hashicorp/packer/provisioner/ansible-local" + ansibleprovisioner "github.com/hashicorp/packer/provisioner/ansible" artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice" atlaspostprocessor "github.com/hashicorp/packer/post-processor/atlas" + azurearmbuilder "github.com/hashicorp/packer/builder/azure/arm" checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum" + chefclientprovisioner "github.com/hashicorp/packer/provisioner/chef-client" + chefsoloprovisioner "github.com/hashicorp/packer/provisioner/chef-solo" + cloudstackbuilder "github.com/hashicorp/packer/builder/cloudstack" compresspostprocessor "github.com/hashicorp/packer/post-processor/compress" + convergeprovisioner "github.com/hashicorp/packer/provisioner/converge" + digitaloceanbuilder "github.com/hashicorp/packer/builder/digitalocean" + dockerbuilder "github.com/hashicorp/packer/builder/docker" dockerimportpostprocessor "github.com/hashicorp/packer/post-processor/docker-import" dockerpushpostprocessor "github.com/hashicorp/packer/post-processor/docker-push" dockersavepostprocessor "github.com/hashicorp/packer/post-processor/docker-save" dockertagpostprocessor "github.com/hashicorp/packer/post-processor/docker-tag" - googlecomputeexportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-export" - manifestpostprocessor "github.com/hashicorp/packer/post-processor/manifest" - shelllocalpostprocessor "github.com/hashicorp/packer/post-processor/shell-local" - vagrantpostprocessor "github.com/hashicorp/packer/post-processor/vagrant" - vagrantcloudpostprocessor "github.com/hashicorp/packer/post-processor/vagrant-cloud" - vspherepostprocessor "github.com/hashicorp/packer/post-processor/vsphere" - vspheretemplatepostprocessor "github.com/hashicorp/packer/post-processor/vsphere-template" - ansibleprovisioner "github.com/hashicorp/packer/provisioner/ansible" - ansiblelocalprovisioner "github.com/hashicorp/packer/provisioner/ansible-local" - chefclientprovisioner "github.com/hashicorp/packer/provisioner/chef-client" - chefsoloprovisioner "github.com/hashicorp/packer/provisioner/chef-solo" - convergeprovisioner "github.com/hashicorp/packer/provisioner/converge" + filebuilder "github.com/hashicorp/packer/builder/file" fileprovisioner "github.com/hashicorp/packer/provisioner/file" + googlecomputebuilder "github.com/hashicorp/packer/builder/googlecompute" + googlecomputeexportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-export" + hypervisobuilder "github.com/hashicorp/packer/builder/hyperv/iso" + hypervvmcxbuilder "github.com/hashicorp/packer/builder/hyperv/vmcx" + lxcbuilder "github.com/hashicorp/packer/builder/lxc" + lxdbuilder "github.com/hashicorp/packer/builder/lxd" + manifestpostprocessor "github.com/hashicorp/packer/post-processor/manifest" + nullbuilder "github.com/hashicorp/packer/builder/null" + oneandonebuilder "github.com/hashicorp/packer/builder/oneandone" + openstackbuilder "github.com/hashicorp/packer/builder/openstack" + oracleclassicbuilder "github.com/hashicorp/packer/builder/oracle/classic" + oracleocibuilder "github.com/hashicorp/packer/builder/oracle/oci" + parallelsisobuilder "github.com/hashicorp/packer/builder/parallels/iso" + parallelspvmbuilder "github.com/hashicorp/packer/builder/parallels/pvm" powershellprovisioner "github.com/hashicorp/packer/provisioner/powershell" + profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks" puppetmasterlessprovisioner "github.com/hashicorp/packer/provisioner/puppet-masterless" puppetserverprovisioner "github.com/hashicorp/packer/provisioner/puppet-server" + qemubuilder "github.com/hashicorp/packer/builder/qemu" saltmasterlessprovisioner "github.com/hashicorp/packer/provisioner/salt-masterless" - shellprovisioner "github.com/hashicorp/packer/provisioner/shell" + shelllocalpostprocessor "github.com/hashicorp/packer/post-processor/shell-local" shelllocalprovisioner "github.com/hashicorp/packer/provisioner/shell-local" + shellprovisioner "github.com/hashicorp/packer/provisioner/shell" + tritonbuilder "github.com/hashicorp/packer/builder/triton" + vagrantcloudpostprocessor "github.com/hashicorp/packer/post-processor/vagrant-cloud" + vagrantpostprocessor "github.com/hashicorp/packer/post-processor/vagrant" + virtualboxisobuilder "github.com/hashicorp/packer/builder/virtualbox/iso" + virtualboxovfbuilder "github.com/hashicorp/packer/builder/virtualbox/ovf" + vmwareisobuilder "github.com/hashicorp/packer/builder/vmware/iso" + vmwarevmxbuilder "github.com/hashicorp/packer/builder/vmware/vmx" + vspherepostprocessor "github.com/hashicorp/packer/post-processor/vsphere" + vspheretemplatepostprocessor "github.com/hashicorp/packer/post-processor/vsphere-template" windowsrestartprovisioner "github.com/hashicorp/packer/provisioner/windows-restart" windowsshellprovisioner "github.com/hashicorp/packer/provisioner/windows-shell" + ) type PluginCommand struct { @@ -80,74 +82,78 @@ type PluginCommand struct { } var Builders = map[string]packer.Builder{ - "alicloud-ecs": new(alicloudecsbuilder.Builder), - "amazon-chroot": new(amazonchrootbuilder.Builder), - "amazon-ebs": new(amazonebsbuilder.Builder), - "amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder), - "amazon-ebsvolume": new(amazonebsvolumebuilder.Builder), - "amazon-instance": new(amazoninstancebuilder.Builder), - "azure-arm": new(azurearmbuilder.Builder), - "cloudstack": new(cloudstackbuilder.Builder), - "digitalocean": new(digitaloceanbuilder.Builder), - "docker": new(dockerbuilder.Builder), - "file": new(filebuilder.Builder), - "googlecompute": new(googlecomputebuilder.Builder), - "hyperv-iso": new(hypervisobuilder.Builder), - "hyperv-vmcx": new(hypervvmcxbuilder.Builder), - "lxc": new(lxcbuilder.Builder), - "lxd": new(lxdbuilder.Builder), - "null": new(nullbuilder.Builder), - "oneandone": new(oneandonebuilder.Builder), - "openstack": new(openstackbuilder.Builder), - "oracle-oci": new(oracleocibuilder.Builder), - "parallels-iso": new(parallelsisobuilder.Builder), - "parallels-pvm": new(parallelspvmbuilder.Builder), - "profitbricks": new(profitbricksbuilder.Builder), - "qemu": new(qemubuilder.Builder), - "triton": new(tritonbuilder.Builder), - "virtualbox-iso": new(virtualboxisobuilder.Builder), - "virtualbox-ovf": new(virtualboxovfbuilder.Builder), - "vmware-iso": new(vmwareisobuilder.Builder), - "vmware-vmx": new(vmwarevmxbuilder.Builder), + "alicloud-ecs": new(alicloudecsbuilder.Builder), + "amazon-chroot": new(amazonchrootbuilder.Builder), + "amazon-ebs": new(amazonebsbuilder.Builder), + "amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder), + "amazon-ebsvolume": new(amazonebsvolumebuilder.Builder), + "amazon-instance": new(amazoninstancebuilder.Builder), + "azure-arm": new(azurearmbuilder.Builder), + "cloudstack": new(cloudstackbuilder.Builder), + "digitalocean": new(digitaloceanbuilder.Builder), + "docker": new(dockerbuilder.Builder), + "file": new(filebuilder.Builder), + "googlecompute": new(googlecomputebuilder.Builder), + "hyperv-iso": new(hypervisobuilder.Builder), + "hyperv-vmcx": new(hypervvmcxbuilder.Builder), + "lxc": new(lxcbuilder.Builder), + "lxd": new(lxdbuilder.Builder), + "null": new(nullbuilder.Builder), + "oneandone": new(oneandonebuilder.Builder), + "openstack": new(openstackbuilder.Builder), + "oracle-classic": new(oracleclassicbuilder.Builder), + "oracle-oci": new(oracleocibuilder.Builder), + "parallels-iso": new(parallelsisobuilder.Builder), + "parallels-pvm": new(parallelspvmbuilder.Builder), + "profitbricks": new(profitbricksbuilder.Builder), + "qemu": new(qemubuilder.Builder), + "triton": new(tritonbuilder.Builder), + "virtualbox-iso": new(virtualboxisobuilder.Builder), + "virtualbox-ovf": new(virtualboxovfbuilder.Builder), + "vmware-iso": new(vmwareisobuilder.Builder), + "vmware-vmx": new(vmwarevmxbuilder.Builder), } + var Provisioners = map[string]packer.Provisioner{ - "ansible": new(ansibleprovisioner.Provisioner), - "ansible-local": new(ansiblelocalprovisioner.Provisioner), - "chef-client": new(chefclientprovisioner.Provisioner), - "chef-solo": new(chefsoloprovisioner.Provisioner), - "converge": new(convergeprovisioner.Provisioner), - "file": new(fileprovisioner.Provisioner), - "powershell": new(powershellprovisioner.Provisioner), - "puppet-masterless": new(puppetmasterlessprovisioner.Provisioner), - "puppet-server": new(puppetserverprovisioner.Provisioner), + "ansible": new(ansibleprovisioner.Provisioner), + "ansible-local": new(ansiblelocalprovisioner.Provisioner), + "chef-client": new(chefclientprovisioner.Provisioner), + "chef-solo": new(chefsoloprovisioner.Provisioner), + "converge": new(convergeprovisioner.Provisioner), + "file": new(fileprovisioner.Provisioner), + "powershell": new(powershellprovisioner.Provisioner), + "puppet-masterless": new(puppetmasterlessprovisioner.Provisioner), + "puppet-server": new(puppetserverprovisioner.Provisioner), "salt-masterless": new(saltmasterlessprovisioner.Provisioner), - "shell": new(shellprovisioner.Provisioner), - "shell-local": new(shelllocalprovisioner.Provisioner), + "shell": new(shellprovisioner.Provisioner), + "shell-local": new(shelllocalprovisioner.Provisioner), "windows-restart": new(windowsrestartprovisioner.Provisioner), - "windows-shell": new(windowsshellprovisioner.Provisioner), + "windows-shell": new(windowsshellprovisioner.Provisioner), } + var PostProcessors = map[string]packer.PostProcessor{ - "alicloud-import": new(alicloudimportpostprocessor.PostProcessor), - "amazon-import": new(amazonimportpostprocessor.PostProcessor), - "artifice": new(artificepostprocessor.PostProcessor), - "atlas": new(atlaspostprocessor.PostProcessor), - "checksum": new(checksumpostprocessor.PostProcessor), - "compress": new(compresspostprocessor.PostProcessor), - "docker-import": new(dockerimportpostprocessor.PostProcessor), - "docker-push": new(dockerpushpostprocessor.PostProcessor), - "docker-save": new(dockersavepostprocessor.PostProcessor), - "docker-tag": new(dockertagpostprocessor.PostProcessor), - "googlecompute-export": new(googlecomputeexportpostprocessor.PostProcessor), - "manifest": new(manifestpostprocessor.PostProcessor), - "shell-local": new(shelllocalpostprocessor.PostProcessor), - "vagrant": new(vagrantpostprocessor.PostProcessor), - "vagrant-cloud": new(vagrantcloudpostprocessor.PostProcessor), - "vsphere": new(vspherepostprocessor.PostProcessor), - "vsphere-template": new(vspheretemplatepostprocessor.PostProcessor), + "alicloud-import": new(alicloudimportpostprocessor.PostProcessor), + "amazon-import": new(amazonimportpostprocessor.PostProcessor), + "artifice": new(artificepostprocessor.PostProcessor), + "atlas": new(atlaspostprocessor.PostProcessor), + "checksum": new(checksumpostprocessor.PostProcessor), + "compress": new(compresspostprocessor.PostProcessor), + "docker-import": new(dockerimportpostprocessor.PostProcessor), + "docker-push": new(dockerpushpostprocessor.PostProcessor), + "docker-save": new(dockersavepostprocessor.PostProcessor), + "docker-tag": new(dockertagpostprocessor.PostProcessor), + "googlecompute-export": new(googlecomputeexportpostprocessor.PostProcessor), + "manifest": new(manifestpostprocessor.PostProcessor), + "shell-local": new(shelllocalpostprocessor.PostProcessor), + "vagrant": new(vagrantpostprocessor.PostProcessor), + "vagrant-cloud": new(vagrantcloudpostprocessor.PostProcessor), + "vsphere": new(vspherepostprocessor.PostProcessor), + "vsphere-template": new(vspheretemplatepostprocessor.PostProcessor), } + var pluginRegexp = regexp.MustCompile("packer-(builder|post-processor|provisioner)-(.+)") func (c *PluginCommand) Run(args []string) int { From 8b420944c5cd86b224c4ef9041560370c15c45c9 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 17 Jan 2018 16:25:38 -0800 Subject: [PATCH 13/61] debugs --- builder/oracle/classic/config.go | 2 +- builder/oracle/classic/step_create_instance.go | 2 +- builder/oracle/classic/step_snapshot.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 10ec44c54..8cdd9aff6 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -26,7 +26,7 @@ type Config struct { // Image ImageName string `mapstructure:"image_name"` Shape string `mapstructure:"shape"` - ImageList string `json:"image_list"` + ImageList string `mapstructure:"image_list"` ctx interpolate.Context } diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 7de599190..c13338928 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -14,7 +14,7 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction // get variables from state ui := state.Get("ui").(packer.Ui) ui.Say("Creating Instance...") - config := state.Get("config").(Config) + config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) sshPublicKey := state.Get("publicKey").(string) diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index 2df8976a1..e83df4394 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -14,7 +14,7 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { // get variables from state ui := state.Get("ui").(packer.Ui) ui.Say("Creating Snapshot...") - config := state.Get("config").(Config) + config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) instanceID := state.Get("instance_id").(string) From a8a0072049fb46b2e676e9e3d87471a53ab37fe0 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 17 Jan 2018 16:33:35 -0800 Subject: [PATCH 14/61] oops need to add this moved file to git --- builder/oracle/common/ssh.go | 45 ++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 builder/oracle/common/ssh.go diff --git a/builder/oracle/common/ssh.go b/builder/oracle/common/ssh.go new file mode 100644 index 000000000..aa2780128 --- /dev/null +++ b/builder/oracle/common/ssh.go @@ -0,0 +1,45 @@ +package common + +import ( + "fmt" + + packerssh "github.com/hashicorp/packer/communicator/ssh" + "github.com/mitchellh/multistep" + "golang.org/x/crypto/ssh" +) + +func CommHost(state multistep.StateBag) (string, error) { + ipAddress := state.Get("instance_ip").(string) + return ipAddress, nil +} + +// SSHConfig returns a function that can be used for the SSH communicator +// config for connecting to the instance created over SSH using the private key +// or password. +func SSHConfig(username, password string) func(state multistep.StateBag) (*ssh.ClientConfig, error) { + return func(state multistep.StateBag) (*ssh.ClientConfig, error) { + privateKey, hasKey := state.GetOk("privateKey") + if hasKey { + + signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string))) + if err != nil { + return nil, fmt.Errorf("Error setting up SSH config: %s", err) + } + return &ssh.ClientConfig{ + User: username, + Auth: []ssh.AuthMethod{ssh.PublicKeys(signer)}, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + }, nil + + } + + return &ssh.ClientConfig{ + User: username, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + Auth: []ssh.AuthMethod{ + ssh.Password(password), + ssh.KeyboardInteractive(packerssh.PasswordKeyboardInteractive(password)), + }, + }, nil + } +} From 6556a851dcce4f5e494dfaaa7588ea0d5ee696a5 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 18 Jan 2018 13:44:00 -0800 Subject: [PATCH 15/61] fix ssh key handling --- builder/amazon/common/step_key_pair.go | 3 +- builder/oracle/classic/builder.go | 3 +- .../oracle/classic/step_create_instance.go | 39 ++++++++++++++++--- vendor/vendor.json | 6 +++ 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/builder/amazon/common/step_key_pair.go b/builder/amazon/common/step_key_pair.go index 927101fcf..55f5aace7 100644 --- a/builder/amazon/common/step_key_pair.go +++ b/builder/amazon/common/step_key_pair.go @@ -6,6 +6,7 @@ import ( "io/ioutil" "os" "runtime" + "strings" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/packer/helper/multistep" @@ -36,7 +37,7 @@ func (s *StepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep } state.Put("keyPair", s.KeyPairName) - state.Put("privateKey", string(privateKeyBytes)) + state.Put("privateKey", strings.TrimSpace(string(privateKeyBytes))) return multistep.ActionContinue } diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 4501456d2..b7773ee19 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -3,6 +3,7 @@ package classic import ( "fmt" "log" + "strings" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-oracle-terraform/compute" @@ -63,7 +64,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &ocommon.StepKeyPair{ Debug: b.config.PackerDebug, DebugKeyPath: fmt.Sprintf("oci_classic_%s.pem", b.config.PackerBuildName), - PrivateKeyFile: b.config.Comm.SSHPrivateKey, + PrivateKeyFile: strings.TrimSpace(b.config.Comm.SSHPrivateKey), }, &stepCreateIPReservation{}, &stepCreateInstance{}, diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index c13338928..fd91ea930 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -2,6 +2,7 @@ package classic import ( "fmt" + "strings" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/packer" @@ -16,18 +17,44 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction ui.Say("Creating Instance...") config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) - sshPublicKey := state.Get("publicKey").(string) + sshPublicKey := strings.TrimSpace(state.Get("publicKey").(string)) + + // Load the dynamically-generated SSH key into the Oracle Compute cloud. + sshKeyName := fmt.Sprintf("/Compute-%s/%s/packer_dynamic_key", config.IdentityDomain, config.Username) + + sshKeysClient := client.SSHKeys() + sshKeysInput := compute.CreateSSHKeyInput{ + Name: sshKeyName, + Key: sshPublicKey, + Enabled: true, + } + keyInfo, err := sshKeysClient.CreateSSHKey(&sshKeysInput) + if err != nil { + // Key already exists; update key instead + if strings.Contains(err.Error(), "packer_dynamic_key already exists") { + updateKeysInput := compute.UpdateSSHKeyInput{ + Name: sshKeyName, + Key: sshPublicKey, + Enabled: true, + } + keyInfo, err = sshKeysClient.UpdateSSHKey(&updateKeysInput) + } else { + err = fmt.Errorf("Problem adding Public SSH key through Oracle's API: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + } // get instances client instanceClient := client.Instances() // Instances Input input := &compute.CreateInstanceInput{ - Name: config.ImageName, - Shape: config.Shape, - ImageList: config.ImageList, - SSHKeys: []string{sshPublicKey}, - Attributes: map[string]interface{}{}, + Name: config.ImageName, + Shape: config.Shape, + ImageList: config.ImageList, + SSHKeys: []string{keyInfo.Name}, } instanceInfo, err := instanceClient.CreateInstance(input) diff --git a/vendor/vendor.json b/vendor/vendor.json index fe36fe283..256c2e1d2 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -785,6 +785,12 @@ "path": "github.com/hashicorp/go-cleanhttp", "revision": "875fb671b3ddc66f8e2f0acc33829c8cb989a38d" }, + { + "checksumSHA1": "1hPCerVn1bWA+9vaAGqnIkRtSFc=", + "path": "github.com/hashicorp/go-getter", + "revision": "cb2c2774e9771bd9568d843be4e59298786550fd", + "revisionTime": "2017-12-20T21:10:09Z" + }, { "checksumSHA1": "lrSl49G23l6NhfilxPM0XFs5rZo=", "path": "github.com/hashicorp/go-multierror", From f208a071a4d4ce941b0fde6a2bea3b5c8d994276 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 18 Jan 2018 15:52:31 -0800 Subject: [PATCH 16/61] fix communicator --- builder/oracle/classic/config.go | 6 ++++++ builder/oracle/classic/step_create_instance.go | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 8cdd9aff6..733d2252c 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/config" + "github.com/hashicorp/packer/packer" "github.com/hashicorp/packer/template/interpolate" ) @@ -48,5 +49,10 @@ func NewConfig(raws ...interface{}) (*Config, error) { return nil, fmt.Errorf("Error parsing API Endpoint: %s", err) } + var errs *packer.MultiError + if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { + errs = packer.MultiErrorAppend(errs, es...) + } + return c, nil } diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index fd91ea930..1456da99e 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -20,7 +20,7 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction sshPublicKey := strings.TrimSpace(state.Get("publicKey").(string)) // Load the dynamically-generated SSH key into the Oracle Compute cloud. - sshKeyName := fmt.Sprintf("/Compute-%s/%s/packer_dynamic_key", config.IdentityDomain, config.Username) + sshKeyName := fmt.Sprintf("/Compute-%s/%s/packer_generated_key", config.IdentityDomain, config.Username) sshKeysClient := client.SSHKeys() sshKeysInput := compute.CreateSSHKeyInput{ @@ -31,7 +31,7 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction keyInfo, err := sshKeysClient.CreateSSHKey(&sshKeysInput) if err != nil { // Key already exists; update key instead - if strings.Contains(err.Error(), "packer_dynamic_key already exists") { + if strings.Contains(err.Error(), "packer_generated_key already exists") { updateKeysInput := compute.UpdateSSHKeyInput{ Name: sshKeyName, Key: sshPublicKey, From 69ba710c2a1661eab9f26239d445b2666864dfb0 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 19 Jan 2018 15:37:06 -0800 Subject: [PATCH 17/61] PROGRESS! Now it only fails on the snapshot step --- builder/oracle/classic/builder.go | 3 +- .../classic/step_create_IP_reservation.go | 3 ++ .../oracle/classic/step_create_instance.go | 33 +++++++++++++++---- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index b7773ee19..4501456d2 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -3,7 +3,6 @@ package classic import ( "fmt" "log" - "strings" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-oracle-terraform/compute" @@ -64,7 +63,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe &ocommon.StepKeyPair{ Debug: b.config.PackerDebug, DebugKeyPath: fmt.Sprintf("oci_classic_%s.pem", b.config.PackerBuildName), - PrivateKeyFile: strings.TrimSpace(b.config.Comm.SSHPrivateKey), + PrivateKeyFile: b.config.Comm.SSHPrivateKey, }, &stepCreateIPReservation{}, &stepCreateInstance{}, diff --git a/builder/oracle/classic/step_create_IP_reservation.go b/builder/oracle/classic/step_create_IP_reservation.go index f3f898eaa..f74dd6bb1 100644 --- a/builder/oracle/classic/step_create_IP_reservation.go +++ b/builder/oracle/classic/step_create_IP_reservation.go @@ -1,6 +1,7 @@ package classic import ( + "fmt" "log" "github.com/hashicorp/go-oracle-terraform/compute" @@ -13,12 +14,14 @@ type stepCreateIPReservation struct{} func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) ui.Say("Creating IP reservation...") + config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) iprClient := client.IPReservations() // TODO: add optional Name and Tags IPInput := &compute.CreateIPReservationInput{ ParentPool: compute.PublicReservationPool, Permanent: true, + Name: fmt.Sprintf("ipres_%s", config.ImageName), } ipRes, err := iprClient.CreateIPReservation(IPInput) if err != nil { diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 1456da99e..1cbfcbcc6 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -2,6 +2,7 @@ package classic import ( "fmt" + "log" "strings" "github.com/hashicorp/go-oracle-terraform/compute" @@ -17,9 +18,13 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction ui.Say("Creating Instance...") config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) + + // SSH KEY CONFIGURATION + + // grab packer-generated key from statebag context. sshPublicKey := strings.TrimSpace(state.Get("publicKey").(string)) - // Load the dynamically-generated SSH key into the Oracle Compute cloud. + // form API call to add key to compute cloud sshKeyName := fmt.Sprintf("/Compute-%s/%s/packer_generated_key", config.IdentityDomain, config.Username) sshKeysClient := client.SSHKeys() @@ -28,9 +33,11 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction Key: sshPublicKey, Enabled: true, } + + // Load the packer-generated SSH key into the Oracle Compute cloud. keyInfo, err := sshKeysClient.CreateSSHKey(&sshKeysInput) if err != nil { - // Key already exists; update key instead + // Key already exists; update key instead of creating it if strings.Contains(err.Error(), "packer_generated_key already exists") { updateKeysInput := compute.UpdateSSHKeyInput{ Name: sshKeyName, @@ -46,15 +53,29 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction } } + // NETWORKING INFO CONFIGURATION + ipAddName := fmt.Sprintf("ipres_%s", config.ImageName) + log.Printf("MEGAN ipADDName is %s", ipAddName) + secListName := "Megan_packer_test" + + netInfo := compute.NetworkingInfo{ + Nat: []string{ipAddName}, + SecLists: []string{secListName}, + } + fmt.Sprintf("Megan netInfo is %#v", netInfo) + + // INSTANCE LAUNCH + // get instances client instanceClient := client.Instances() // Instances Input input := &compute.CreateInstanceInput{ - Name: config.ImageName, - Shape: config.Shape, - ImageList: config.ImageList, - SSHKeys: []string{keyInfo.Name}, + Name: config.ImageName, + Shape: config.Shape, + ImageList: config.ImageList, + SSHKeys: []string{keyInfo.Name}, + Networking: map[string]compute.NetworkingInfo{"eth0": netInfo}, } instanceInfo, err := instanceClient.CreateInstance(input) From 256382547b0f708a6bb0769d783085d4d253d1db Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 19 Jan 2018 16:00:12 -0800 Subject: [PATCH 18/61] snapshot step works --- builder/oracle/classic/step_snapshot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index e83df4394..52c2557c6 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -23,7 +23,7 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { // Instances Input snapshotInput := &compute.CreateSnapshotInput{ - Instance: instanceID, + Instance: fmt.Sprintf("%s/%s", config.ImageName, instanceID), MachineImage: config.ImageName, } From f6c60aac787b54441053fcda79f47ca73cc31c9d Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 19 Jan 2018 16:08:42 -0800 Subject: [PATCH 19/61] clean up instance --- .../oracle/classic/step_create_instance.go | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 1cbfcbcc6..8c3edf3a3 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -92,5 +92,30 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction } func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { - // Nothing to do + // terminate instance + ui := state.Get("ui").(packer.Ui) + client := state.Get("client").(*compute.ComputeClient) + imID := state.Get("instance_id").(string) + + ui.Say(fmt.Sprintf("Terminating instance (%s)...", id)) + + instanceClient := client.Instances() + // Instances Input + input := &compute.DeleteInstanceInput{ + Name: config.ImageName, + ID: imID, + } + + instanceInfo, err := instanceClient.DeleteInstance(input) + if err != nil { + err = fmt.Errorf("Problem destroying instance: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return + } + + // TODO wait for instance state to change to deleted? + + ui.Say("Terminated instance.") + return } From 89159f3a87a8c7b00dd9c13f0a713b186fc71cd0 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 19 Jan 2018 16:13:36 -0800 Subject: [PATCH 20/61] fix bugs in cleanup --- builder/oracle/classic/step_create_instance.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 8c3edf3a3..f33e5d0e5 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -95,9 +95,10 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { // terminate instance ui := state.Get("ui").(packer.Ui) client := state.Get("client").(*compute.ComputeClient) + config := state.Get("config").(*Config) imID := state.Get("instance_id").(string) - ui.Say(fmt.Sprintf("Terminating instance (%s)...", id)) + ui.Say(fmt.Sprintf("Terminating instance (%s)...", imID)) instanceClient := client.Instances() // Instances Input @@ -106,7 +107,7 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { ID: imID, } - instanceInfo, err := instanceClient.DeleteInstance(input) + err := instanceClient.DeleteInstance(input) if err != nil { err = fmt.Errorf("Problem destroying instance: %s", err) ui.Error(err.Error()) From 53ff257cf0d20f57937d546e988504017e60a58d Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 22 Jan 2018 16:54:09 -0800 Subject: [PATCH 21/61] it LLIIIIIIIIIVES --- builder/oracle/classic/builder.go | 2 + .../oracle/classic/step_create_instance.go | 49 ++----------------- 2 files changed, 7 insertions(+), 44 deletions(-) diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 4501456d2..7e8c07f53 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -66,6 +66,8 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe PrivateKeyFile: b.config.Comm.SSHPrivateKey, }, &stepCreateIPReservation{}, + &stepAddKeysToAPI{}, + &stepSecurity{}, &stepCreateInstance{}, &communicator.StepConnect{ Config: &b.config.Comm, diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index f33e5d0e5..bf89a51e3 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -3,7 +3,6 @@ package classic import ( "fmt" "log" - "strings" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/packer" @@ -18,53 +17,16 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction ui.Say("Creating Instance...") config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) + keyName := state.Get("key_name").(string) - // SSH KEY CONFIGURATION - - // grab packer-generated key from statebag context. - sshPublicKey := strings.TrimSpace(state.Get("publicKey").(string)) - - // form API call to add key to compute cloud - sshKeyName := fmt.Sprintf("/Compute-%s/%s/packer_generated_key", config.IdentityDomain, config.Username) - - sshKeysClient := client.SSHKeys() - sshKeysInput := compute.CreateSSHKeyInput{ - Name: sshKeyName, - Key: sshPublicKey, - Enabled: true, - } - - // Load the packer-generated SSH key into the Oracle Compute cloud. - keyInfo, err := sshKeysClient.CreateSSHKey(&sshKeysInput) - if err != nil { - // Key already exists; update key instead of creating it - if strings.Contains(err.Error(), "packer_generated_key already exists") { - updateKeysInput := compute.UpdateSSHKeyInput{ - Name: sshKeyName, - Key: sshPublicKey, - Enabled: true, - } - keyInfo, err = sshKeysClient.UpdateSSHKey(&updateKeysInput) - } else { - err = fmt.Errorf("Problem adding Public SSH key through Oracle's API: %s", err) - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt - } - } - - // NETWORKING INFO CONFIGURATION ipAddName := fmt.Sprintf("ipres_%s", config.ImageName) - log.Printf("MEGAN ipADDName is %s", ipAddName) - secListName := "Megan_packer_test" + // secListName := "Megan_packer_test" // hack to get working; fix b4 release + secListName := state.Get("security_list").(string) netInfo := compute.NetworkingInfo{ Nat: []string{ipAddName}, SecLists: []string{secListName}, } - fmt.Sprintf("Megan netInfo is %#v", netInfo) - - // INSTANCE LAUNCH // get instances client instanceClient := client.Instances() @@ -74,7 +36,7 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction Name: config.ImageName, Shape: config.Shape, ImageList: config.ImageList, - SSHKeys: []string{keyInfo.Name}, + SSHKeys: []string{keyName}, Networking: map[string]compute.NetworkingInfo{"eth0": netInfo}, } @@ -106,6 +68,7 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { Name: config.ImageName, ID: imID, } + log.Printf("instance destroy input is %#v", input) err := instanceClient.DeleteInstance(input) if err != nil { @@ -114,9 +77,7 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { state.Put("error", err) return } - // TODO wait for instance state to change to deleted? - ui.Say("Terminated instance.") return } From 531cb2244d8749c80b1ee07fad2595b175b01133 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 22 Jan 2018 16:54:49 -0800 Subject: [PATCH 22/61] add separated out steps --- .../oracle/classic/step_add_keys_to_api.go | 58 +++++++++++++++ builder/oracle/classic/step_security.go | 70 +++++++++++++++++++ 2 files changed, 128 insertions(+) create mode 100644 builder/oracle/classic/step_add_keys_to_api.go create mode 100644 builder/oracle/classic/step_security.go diff --git a/builder/oracle/classic/step_add_keys_to_api.go b/builder/oracle/classic/step_add_keys_to_api.go new file mode 100644 index 000000000..f28a9bce9 --- /dev/null +++ b/builder/oracle/classic/step_add_keys_to_api.go @@ -0,0 +1,58 @@ +package classic + +import ( + "fmt" + "strings" + + "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/packer" + "github.com/mitchellh/multistep" +) + +type stepAddKeysToAPI struct{} + +func (s *stepAddKeysToAPI) Run(state multistep.StateBag) multistep.StepAction { + // get variables from state + ui := state.Get("ui").(packer.Ui) + ui.Say("Adding SSH keys to API...") + config := state.Get("config").(*Config) + client := state.Get("client").(*compute.ComputeClient) + + // grab packer-generated key from statebag context. + sshPublicKey := strings.TrimSpace(state.Get("publicKey").(string)) + + // form API call to add key to compute cloud + sshKeyName := fmt.Sprintf("/Compute-%s/%s/packer_generated_key", config.IdentityDomain, config.Username) + + sshKeysClient := client.SSHKeys() + sshKeysInput := compute.CreateSSHKeyInput{ + Name: sshKeyName, + Key: sshPublicKey, + Enabled: true, + } + + // Load the packer-generated SSH key into the Oracle Compute cloud. + keyInfo, err := sshKeysClient.CreateSSHKey(&sshKeysInput) + if err != nil { + // Key already exists; update key instead of creating it + if strings.Contains(err.Error(), "packer_generated_key already exists") { + updateKeysInput := compute.UpdateSSHKeyInput{ + Name: sshKeyName, + Key: sshPublicKey, + Enabled: true, + } + keyInfo, err = sshKeysClient.UpdateSSHKey(&updateKeysInput) + } else { + err = fmt.Errorf("Problem adding Public SSH key through Oracle's API: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + } + state.Put("key_name", keyInfo.Name) + return multistep.ActionContinue +} + +func (s *stepAddKeysToAPI) Cleanup(state multistep.StateBag) { + // Nothing to do +} diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go new file mode 100644 index 000000000..c907ceb7f --- /dev/null +++ b/builder/oracle/classic/step_security.go @@ -0,0 +1,70 @@ +package classic + +import ( + "fmt" + "log" + "strings" + + "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/packer" + "github.com/mitchellh/multistep" +) + +type stepSecurity struct{} + +func (s *stepSecurity) Run(state multistep.StateBag) multistep.StepAction { + // TODO create overrides that allow savvy users to add the image to their + // own security lists instead of ours + ui := state.Get("ui").(packer.Ui) + ui.Say("Configuring security lists and rules...") + config := state.Get("config").(*Config) + client := state.Get("client").(*compute.ComputeClient) + + secListName := fmt.Sprintf("/Compute-%s/%s/Packer_SSH_Allow", + config.IdentityDomain, config.Username) + secListClient := client.SecurityLists() + secListInput := compute.CreateSecurityListInput{ + Description: "Packer-generated security list to give packer ssh access", + Name: secListName, + } + _, err := secListClient.CreateSecurityList(&secListInput) + if err != nil { + if !strings.Contains(err.Error(), "already exists") { + err = fmt.Errorf("Error creating security security IP List to"+ + " allow Packer to connect to Oracle instance via SSH: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + } + secListURI := fmt.Sprintf("%s/seclist/Compute-%s/%s/Packer_SSH_Allow", + config.APIEndpoint, config.IdentityDomain, config.Username) + log.Printf("Megan secListURI is %s", secListURI) + // DOCS NOTE: user must have Compute_Operations role + // Create security rule that allows Packer to connect via SSH + + secRulesClient := client.SecRules() + secRulesInput := compute.CreateSecRuleInput{ + Action: "PERMIT", + Application: "/oracle/public/ssh", + Description: "Packer-generated security rule to allow ssh", + DestinationList: fmt.Sprintf("seclist:%s", secListName), + Name: "Packer-allow-SSH-Rule", + SourceList: "seciplist:/oracle/public/public-internet", + } + + _, err = secRulesClient.CreateSecRule(&secRulesInput) + if err != nil { + err = fmt.Errorf("Error creating security rule to allow Packer to connect to Oracle instance via SSH: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + + state.Put("security_list", secListName) + return multistep.ActionContinue +} + +func (s *stepSecurity) Cleanup(state multistep.StateBag) { + // Nothing to do +} From 7d23cfae0af760cbe6f1a4481387d81216c0a38e Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Tue, 23 Jan 2018 11:07:04 -0800 Subject: [PATCH 23/61] allow user to add a security list for SSH access; add cleanup for packer-generated rules and lists --- builder/oracle/classic/config.go | 9 ++++ builder/oracle/classic/step_security.go | 59 +++++++++++++++++-------- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 733d2252c..eb3fd6876 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -28,6 +28,11 @@ type Config struct { ImageName string `mapstructure:"image_name"` Shape string `mapstructure:"shape"` ImageList string `mapstructure:"image_list"` + // Optional. Describes what computers are allowed to reach your instance + // via SSH. This whitelist must contain the computer you're running Packer + // from. It defaults to public-internet, meaning that you can SSH into your + // instance from anywhere as long as you have the right keys + SSHSourceList string `mapstructure:"ssh_source_list"` ctx interpolate.Context } @@ -48,6 +53,10 @@ func NewConfig(raws ...interface{}) (*Config, error) { if err != nil { return nil, fmt.Errorf("Error parsing API Endpoint: %s", err) } + // set default source list + if c.SSHSourceList == "" { + c.SSHSourceList = "seciplist:/oracle/public/public-internet" + } var errs *packer.MultiError if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index c907ceb7f..d82f2bc41 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -13,15 +13,15 @@ import ( type stepSecurity struct{} func (s *stepSecurity) Run(state multistep.StateBag) multistep.StepAction { - // TODO create overrides that allow savvy users to add the image to their - // own security lists instead of ours ui := state.Get("ui").(packer.Ui) - ui.Say("Configuring security lists and rules...") + + ui.Say("Configuring security lists and rules to enable SSH access...") + config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) - secListName := fmt.Sprintf("/Compute-%s/%s/Packer_SSH_Allow", - config.IdentityDomain, config.Username) + secListName := fmt.Sprintf("/Compute-%s/%s/Packer_SSH_Allow_%s", + config.IdentityDomain, config.Username, config.ImageName) secListClient := client.SecurityLists() secListInput := compute.CreateSecurityListInput{ Description: "Packer-generated security list to give packer ssh access", @@ -30,41 +30,64 @@ func (s *stepSecurity) Run(state multistep.StateBag) multistep.StepAction { _, err := secListClient.CreateSecurityList(&secListInput) if err != nil { if !strings.Contains(err.Error(), "already exists") { - err = fmt.Errorf("Error creating security security IP List to"+ + err = fmt.Errorf("Error creating security List to"+ " allow Packer to connect to Oracle instance via SSH: %s", err) ui.Error(err.Error()) state.Put("error", err) return multistep.ActionHalt } } - secListURI := fmt.Sprintf("%s/seclist/Compute-%s/%s/Packer_SSH_Allow", - config.APIEndpoint, config.IdentityDomain, config.Username) - log.Printf("Megan secListURI is %s", secListURI) // DOCS NOTE: user must have Compute_Operations role // Create security rule that allows Packer to connect via SSH - secRulesClient := client.SecRules() secRulesInput := compute.CreateSecRuleInput{ Action: "PERMIT", Application: "/oracle/public/ssh", Description: "Packer-generated security rule to allow ssh", DestinationList: fmt.Sprintf("seclist:%s", secListName), - Name: "Packer-allow-SSH-Rule", - SourceList: "seciplist:/oracle/public/public-internet", + Name: fmt.Sprintf("Packer-allow-SSH-Rule_%s", config.ImageName), + SourceList: config.SSHSourceList, } + secRuleName := fmt.Sprintf("/Compute-%s/%s/Packer-allow-SSH-Rule_%s", + config.IdentityDomain, config.Username, config.ImageName) _, err = secRulesClient.CreateSecRule(&secRulesInput) if err != nil { - err = fmt.Errorf("Error creating security rule to allow Packer to connect to Oracle instance via SSH: %s", err) - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt + log.Printf(err.Error()) + if !strings.Contains(err.Error(), "already exists") { + err = fmt.Errorf("Error creating security rule to"+ + " allow Packer to connect to Oracle instance via SSH: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } } - + state.Put("security_rule_name", secRuleName) state.Put("security_list", secListName) return multistep.ActionContinue } func (s *stepSecurity) Cleanup(state multistep.StateBag) { - // Nothing to do + client := state.Get("client").(*compute.ComputeClient) + ui := state.Get("ui").(packer.Ui) + ui.Say("Deleting the packer-generated security rules and lists...") + // delete security list that Packer generated + secListName := state.Get("security_list").(string) + secListClient := client.SecurityLists() + input := compute.DeleteSecurityListInput{Name: secListName} + err := secListClient.DeleteSecurityList(&input) + if err != nil { + ui.Say(fmt.Sprintf("Error deleting the packer-generated security list %s; "+ + "please delete manually. (error : %s)", secListName, err.Error())) + } + // delete security rules that Packer generated + secRuleName := state.Get("security_rule_name").(string) + secRulesClient := client.SecRules() + ruleInput := compute.DeleteSecRuleInput{Name: secRuleName} + err = secRulesClient.DeleteSecRule(&ruleInput) + if err != nil { + ui.Say(fmt.Sprintf("Error deleting the packer-generated security rule %s; "+ + "please delete manually. (error: %s)", secRuleName, err.Error())) + } + return } From b9a90b9261708d66ca974ab60596435c093190f7 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 23 Jan 2018 14:16:51 -0800 Subject: [PATCH 24/61] Check for error when creating ip reso --- ...ate_IP_reservation.go => step_create_ip_reservation.go} | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) rename builder/oracle/classic/{step_create_IP_reservation.go => step_create_ip_reservation.go} (87%) diff --git a/builder/oracle/classic/step_create_IP_reservation.go b/builder/oracle/classic/step_create_ip_reservation.go similarity index 87% rename from builder/oracle/classic/step_create_IP_reservation.go rename to builder/oracle/classic/step_create_ip_reservation.go index f74dd6bb1..733c07e70 100644 --- a/builder/oracle/classic/step_create_IP_reservation.go +++ b/builder/oracle/classic/step_create_ip_reservation.go @@ -24,8 +24,11 @@ func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAc Name: fmt.Sprintf("ipres_%s", config.ImageName), } ipRes, err := iprClient.CreateIPReservation(IPInput) + if err != nil { - log.Printf("Error creating IP Reservation: %s", err) + err := fmt.Errorf("Error creating IP Reservation: %s", err) + state.Put("error", err) + ui.Error(err.Error()) return multistep.ActionHalt } state.Put("instance_ip", ipRes.IP) @@ -34,5 +37,5 @@ func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAc } func (s *stepCreateIPReservation) Cleanup(state multistep.StateBag) { - // Nothing to do + // TODO: delete ip reservation } From 44befb08576804e64a768bcb69aca1694bb668d4 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 23 Jan 2018 17:33:03 -0800 Subject: [PATCH 25/61] rename --- .../oracle/classic/{step_add_keys_to_api.go => step_add_keys.go} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename builder/oracle/classic/{step_add_keys_to_api.go => step_add_keys.go} (100%) diff --git a/builder/oracle/classic/step_add_keys_to_api.go b/builder/oracle/classic/step_add_keys.go similarity index 100% rename from builder/oracle/classic/step_add_keys_to_api.go rename to builder/oracle/classic/step_add_keys.go From 603881d9902e105d14a90565cedceb18b734de0e Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 23 Jan 2018 18:13:51 -0800 Subject: [PATCH 26/61] add oci/classic artifact --- builder/oracle/classic/artifact.go | 28 ++++++++++++++++++++----- builder/oracle/classic/builder.go | 16 ++++++-------- builder/oracle/classic/step_snapshot.go | 1 + 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/builder/oracle/classic/artifact.go b/builder/oracle/classic/artifact.go index ff01b186f..bf1fed0ba 100644 --- a/builder/oracle/classic/artifact.go +++ b/builder/oracle/classic/artifact.go @@ -1,7 +1,15 @@ package classic -// Artifact is an artifact implementation that contains a built Custom Image. +import ( + "fmt" + + "github.com/hashicorp/go-oracle-terraform/compute" +) + +// Artifact is an artifact implementation that contains a Snapshot. type Artifact struct { + Snapshot *compute.Snapshot + driver *compute.ComputeClient } // BuilderId uniquely identifies the builder. @@ -15,13 +23,17 @@ func (a *Artifact) Files() []string { return nil } -// Id returns the OCID of the associated Image. func (a *Artifact) Id() string { - return "" + return a.Snapshot.Name } func (a *Artifact) String() string { - return "" + return fmt.Sprintf("A Snapshot was created: \n"+ + "Name: %s\n"+ + "Instance: %s\n"+ + "MachineImage: %s\n"+ + "URI: %s", + a.Snapshot.Name, a.Snapshot.Instance, a.Snapshot.MachineImage, a.Snapshot.URI) } func (a *Artifact) State(name string) interface{} { @@ -30,5 +42,11 @@ func (a *Artifact) State(name string) interface{} { // Destroy deletes the custom image associated with the artifact. func (a *Artifact) Destroy() error { - return nil + client := a.driver.Snapshots() + mic := a.driver.MachineImages() + input := &compute.DeleteSnapshotInput{ + Snapshot: a.Snapshot.Name, + MachineImage: a.Snapshot.MachineImage, + } + return client.DeleteSnapshot(mic, input) } diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 7e8c07f53..7847ef179 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -89,17 +89,13 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, rawErr.(error) } - /* - // Build the artifact and return it - artifact := &Artifact{ - Image: state.Get("image").(client.Image), - Region: b.config.AccessCfg.Region, - driver: driver, - } + // Build the artifact and return it + artifact := &Artifact{ + Snapshot: state.Get("snapshot").(*compute.Snapshot), + driver: client, + } - return artifact, nil - */ - return nil, nil + return artifact, nil } // Cancel terminates a running build. diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index 52c2557c6..2e4e9d7a3 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -35,6 +35,7 @@ func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { return multistep.ActionHalt } + state.Put("snapshot", snap) ui.Say(fmt.Sprintf("Created snapshot (%s).", snap.Name)) return multistep.ActionContinue } From 76ea73c5b208fd37d809782d5c12b5f44abb793d Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 23 Jan 2018 18:14:10 -0800 Subject: [PATCH 27/61] I don't think we need to delete this artifact right now --- builder/oracle/classic/artifact.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/builder/oracle/classic/artifact.go b/builder/oracle/classic/artifact.go index bf1fed0ba..fb7861b04 100644 --- a/builder/oracle/classic/artifact.go +++ b/builder/oracle/classic/artifact.go @@ -42,11 +42,5 @@ func (a *Artifact) State(name string) interface{} { // Destroy deletes the custom image associated with the artifact. func (a *Artifact) Destroy() error { - client := a.driver.Snapshots() - mic := a.driver.MachineImages() - input := &compute.DeleteSnapshotInput{ - Snapshot: a.Snapshot.Name, - MachineImage: a.Snapshot.MachineImage, - } - return client.DeleteSnapshot(mic, input) + return nil } From 1fffbacdd3197b17aac4ebed79ebc53b18847b88 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 25 Jan 2018 09:17:30 -0800 Subject: [PATCH 28/61] fix ordering of deleting security rules and lists --- .../oracle/classic/step_create_instance.go | 1 - builder/oracle/classic/step_security.go | 23 +++++++++++-------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index bf89a51e3..88c108466 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -20,7 +20,6 @@ func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction keyName := state.Get("key_name").(string) ipAddName := fmt.Sprintf("ipres_%s", config.ImageName) - // secListName := "Megan_packer_test" // hack to get working; fix b4 release secListName := state.Get("security_list").(string) netInfo := compute.NetworkingInfo{ diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index d82f2bc41..cc965caeb 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -71,23 +71,26 @@ func (s *stepSecurity) Cleanup(state multistep.StateBag) { client := state.Get("client").(*compute.ComputeClient) ui := state.Get("ui").(packer.Ui) ui.Say("Deleting the packer-generated security rules and lists...") - // delete security list that Packer generated - secListName := state.Get("security_list").(string) - secListClient := client.SecurityLists() - input := compute.DeleteSecurityListInput{Name: secListName} - err := secListClient.DeleteSecurityList(&input) - if err != nil { - ui.Say(fmt.Sprintf("Error deleting the packer-generated security list %s; "+ - "please delete manually. (error : %s)", secListName, err.Error())) - } + // delete security rules that Packer generated secRuleName := state.Get("security_rule_name").(string) secRulesClient := client.SecRules() ruleInput := compute.DeleteSecRuleInput{Name: secRuleName} - err = secRulesClient.DeleteSecRule(&ruleInput) + err := secRulesClient.DeleteSecRule(&ruleInput) if err != nil { ui.Say(fmt.Sprintf("Error deleting the packer-generated security rule %s; "+ "please delete manually. (error: %s)", secRuleName, err.Error())) } + + // delete security list that Packer generated + secListName := state.Get("security_list").(string) + secListClient := client.SecurityLists() + input := compute.DeleteSecurityListInput{Name: secListName} + err = secListClient.DeleteSecurityList(&input) + if err != nil { + ui.Say(fmt.Sprintf("Error deleting the packer-generated security list %s; "+ + "please delete manually. (error : %s)", secListName, err.Error())) + } + return } From 00db189c9c4b6872d7a0baf3031fbc9f2a3d1555 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 25 Jan 2018 14:00:26 -0800 Subject: [PATCH 29/61] add docs page --- .../oracle/classic/step_create_instance.go | 1 - .../docs/builders/oracle-classic.html.md | 98 +++++++++++++++++++ website/source/layouts/docs.erb | 3 + 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 website/source/docs/builders/oracle-classic.html.md diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 88c108466..8a96823aa 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -62,7 +62,6 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { ui.Say(fmt.Sprintf("Terminating instance (%s)...", imID)) instanceClient := client.Instances() - // Instances Input input := &compute.DeleteInstanceInput{ Name: config.ImageName, ID: imID, diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md new file mode 100644 index 000000000..08d3d10b1 --- /dev/null +++ b/website/source/docs/builders/oracle-classic.html.md @@ -0,0 +1,98 @@ +--- +description: + The oracle-classic builder is able to create new custom images for use with Oracle + Compute Cloud. +layout: docs +page_title: 'Oracle Classic - Builders' +sidebar_current: 'docs-builders-oracle-classic' +--- + +# Oracle Compute Cloud (Classic) Builder + +Type: `oracle-classic` + +The `oracle-classic` Packer builder is able to create custom images for use +with [Oracle Compute Cloud](https://cloud.oracle.com/compute-classic). The builder +takes a base image, runs any provisioning necessary on the base image after +launching it, and finally snapshots it creating a reusable custom image. + +It is recommended that you familiarise yourself with the +[Key Concepts and Terminology](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsg/terminology.html) +prior to using this builder if you have not done so already. + +The builder _does not_ manage images. Once it creates an image, it is up to you +to use it or delete it. + +## Authorization + +This builder authenticates API calls to Compute Classic using basic +authentication (user name and password). +To read more, see the [authentication documentation](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsa/Authentication.html) + +## Configuration Reference + +There are many configuration options available for the `oracle-classic` builder. +This builder currently only works with the SSH communicator. + +### Required + + - `api_endpoint` (string) - This is your custom API endpoint for sending + requests. Instructions for determining your API endpoint can be found + [here](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsa/SendRequests.html) + + - `identity_domain` (string) - This is your customer-specific identity domain + as generated by Oracle. If you don't know what your identity domain is, ask + your account administrator. For a little more information, see the Oracle + [documentation](https://docs.oracle.com/en/cloud/get-started/subscriptions-cloud/ocuid/identity-domain-overview.html#GUID-7969F881-5F4D-443E-B86C-9044C8085B8A). + + - `image_list` (string) - This is what image you want to use as your base image. + See the [documentation](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsg/listing-machine-images.html) + for more details. To see what public image lists are available, you can use + the CLI, as described [here](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stopc/image-lists-stclr-and-nmcli.html#GUID-DB7E75FE-F752-4FF7-AB70-3C8DCDFCA0FA) + + - `password` (string) - Your account password. + + - `shape` (string) - The template that determines the number of + CPUs, amount of memory, and other resources allocated to a newly created + instance. For more information about shapes, see the documentation [here](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsg/machine-images-and-shapes.html). + + - `username` (string) - Your account username. + +### Optional + + - `ssh_username` (string) - The username that Packer will use to SSH into the + instance; defaults to `opc`, the default oracle user, which has sudo + priveliges. If you have already configured users on your machine, you may + prompt Packer to use one of those instead. For more detail, see the + [documentation](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsg/accessing-oracle-linux-instance-using-ssh.html). + + - `image_name` (string) - The name to assign to the resulting custom image. + +## Basic Example + +Here is a basic example. Note that account specific configuration has been +obfuscated; you will need to add a working `username`, `password`, +`identity_domain`, and `api_endpoint` in order for the example to work. + +``` {.json} +{ + "builders": [ + { + "type": "oracle-classic", + "username": "myuser@myaccount.com", + "password": "supersecretpasswordhere", + "identity_domain": "#######", + "api_endpoint": "https://api-###.compute.###.oraclecloud.com/", + "image_list": "/oracle/public/OL_7.2_UEKR4_x86_64", + "shape": "oc3", + "image_name": "Packer_Builder_Test_{{timestamp}}" + } + ], + "provisioners": [ + { + "type": "shell", + "inline": ["echo hello"] + } + ] +} +``` diff --git a/website/source/layouts/docs.erb b/website/source/layouts/docs.erb index 9aa17ccc4..739b33733 100644 --- a/website/source/layouts/docs.erb +++ b/website/source/layouts/docs.erb @@ -131,6 +131,9 @@ > OpenStack + > + Oracle Classic + > Oracle OCI From 7d85b31b29fc116cc5c0945b11b568f3865efad2 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 25 Jan 2018 14:01:34 -0800 Subject: [PATCH 30/61] make fmt --- command/plugin.go | 176 ++++++++++++++++++++++------------------------ 1 file changed, 86 insertions(+), 90 deletions(-) diff --git a/command/plugin.go b/command/plugin.go index 4f4228fc3..91b052340 100644 --- a/command/plugin.go +++ b/command/plugin.go @@ -14,39 +14,21 @@ import ( "github.com/hashicorp/packer/packer/plugin" alicloudecsbuilder "github.com/hashicorp/packer/builder/alicloud/ecs" - alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-import" amazonchrootbuilder "github.com/hashicorp/packer/builder/amazon/chroot" amazonebsbuilder "github.com/hashicorp/packer/builder/amazon/ebs" amazonebssurrogatebuilder "github.com/hashicorp/packer/builder/amazon/ebssurrogate" amazonebsvolumebuilder "github.com/hashicorp/packer/builder/amazon/ebsvolume" - amazonimportpostprocessor "github.com/hashicorp/packer/post-processor/amazon-import" amazoninstancebuilder "github.com/hashicorp/packer/builder/amazon/instance" - ansiblelocalprovisioner "github.com/hashicorp/packer/provisioner/ansible-local" - ansibleprovisioner "github.com/hashicorp/packer/provisioner/ansible" - artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice" - atlaspostprocessor "github.com/hashicorp/packer/post-processor/atlas" azurearmbuilder "github.com/hashicorp/packer/builder/azure/arm" - checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum" - chefclientprovisioner "github.com/hashicorp/packer/provisioner/chef-client" - chefsoloprovisioner "github.com/hashicorp/packer/provisioner/chef-solo" cloudstackbuilder "github.com/hashicorp/packer/builder/cloudstack" - compresspostprocessor "github.com/hashicorp/packer/post-processor/compress" - convergeprovisioner "github.com/hashicorp/packer/provisioner/converge" digitaloceanbuilder "github.com/hashicorp/packer/builder/digitalocean" dockerbuilder "github.com/hashicorp/packer/builder/docker" - dockerimportpostprocessor "github.com/hashicorp/packer/post-processor/docker-import" - dockerpushpostprocessor "github.com/hashicorp/packer/post-processor/docker-push" - dockersavepostprocessor "github.com/hashicorp/packer/post-processor/docker-save" - dockertagpostprocessor "github.com/hashicorp/packer/post-processor/docker-tag" filebuilder "github.com/hashicorp/packer/builder/file" - fileprovisioner "github.com/hashicorp/packer/provisioner/file" googlecomputebuilder "github.com/hashicorp/packer/builder/googlecompute" - googlecomputeexportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-export" hypervisobuilder "github.com/hashicorp/packer/builder/hyperv/iso" hypervvmcxbuilder "github.com/hashicorp/packer/builder/hyperv/vmcx" lxcbuilder "github.com/hashicorp/packer/builder/lxc" lxdbuilder "github.com/hashicorp/packer/builder/lxd" - manifestpostprocessor "github.com/hashicorp/packer/post-processor/manifest" nullbuilder "github.com/hashicorp/packer/builder/null" oneandonebuilder "github.com/hashicorp/packer/builder/oneandone" openstackbuilder "github.com/hashicorp/packer/builder/openstack" @@ -54,27 +36,44 @@ import ( oracleocibuilder "github.com/hashicorp/packer/builder/oracle/oci" parallelsisobuilder "github.com/hashicorp/packer/builder/parallels/iso" parallelspvmbuilder "github.com/hashicorp/packer/builder/parallels/pvm" - powershellprovisioner "github.com/hashicorp/packer/provisioner/powershell" profitbricksbuilder "github.com/hashicorp/packer/builder/profitbricks" - puppetmasterlessprovisioner "github.com/hashicorp/packer/provisioner/puppet-masterless" - puppetserverprovisioner "github.com/hashicorp/packer/provisioner/puppet-server" qemubuilder "github.com/hashicorp/packer/builder/qemu" - saltmasterlessprovisioner "github.com/hashicorp/packer/provisioner/salt-masterless" - shelllocalpostprocessor "github.com/hashicorp/packer/post-processor/shell-local" - shelllocalprovisioner "github.com/hashicorp/packer/provisioner/shell-local" - shellprovisioner "github.com/hashicorp/packer/provisioner/shell" tritonbuilder "github.com/hashicorp/packer/builder/triton" - vagrantcloudpostprocessor "github.com/hashicorp/packer/post-processor/vagrant-cloud" - vagrantpostprocessor "github.com/hashicorp/packer/post-processor/vagrant" virtualboxisobuilder "github.com/hashicorp/packer/builder/virtualbox/iso" virtualboxovfbuilder "github.com/hashicorp/packer/builder/virtualbox/ovf" vmwareisobuilder "github.com/hashicorp/packer/builder/vmware/iso" vmwarevmxbuilder "github.com/hashicorp/packer/builder/vmware/vmx" + alicloudimportpostprocessor "github.com/hashicorp/packer/post-processor/alicloud-import" + amazonimportpostprocessor "github.com/hashicorp/packer/post-processor/amazon-import" + artificepostprocessor "github.com/hashicorp/packer/post-processor/artifice" + atlaspostprocessor "github.com/hashicorp/packer/post-processor/atlas" + checksumpostprocessor "github.com/hashicorp/packer/post-processor/checksum" + compresspostprocessor "github.com/hashicorp/packer/post-processor/compress" + dockerimportpostprocessor "github.com/hashicorp/packer/post-processor/docker-import" + dockerpushpostprocessor "github.com/hashicorp/packer/post-processor/docker-push" + dockersavepostprocessor "github.com/hashicorp/packer/post-processor/docker-save" + dockertagpostprocessor "github.com/hashicorp/packer/post-processor/docker-tag" + googlecomputeexportpostprocessor "github.com/hashicorp/packer/post-processor/googlecompute-export" + manifestpostprocessor "github.com/hashicorp/packer/post-processor/manifest" + shelllocalpostprocessor "github.com/hashicorp/packer/post-processor/shell-local" + vagrantpostprocessor "github.com/hashicorp/packer/post-processor/vagrant" + vagrantcloudpostprocessor "github.com/hashicorp/packer/post-processor/vagrant-cloud" vspherepostprocessor "github.com/hashicorp/packer/post-processor/vsphere" vspheretemplatepostprocessor "github.com/hashicorp/packer/post-processor/vsphere-template" + ansibleprovisioner "github.com/hashicorp/packer/provisioner/ansible" + ansiblelocalprovisioner "github.com/hashicorp/packer/provisioner/ansible-local" + chefclientprovisioner "github.com/hashicorp/packer/provisioner/chef-client" + chefsoloprovisioner "github.com/hashicorp/packer/provisioner/chef-solo" + convergeprovisioner "github.com/hashicorp/packer/provisioner/converge" + fileprovisioner "github.com/hashicorp/packer/provisioner/file" + powershellprovisioner "github.com/hashicorp/packer/provisioner/powershell" + puppetmasterlessprovisioner "github.com/hashicorp/packer/provisioner/puppet-masterless" + puppetserverprovisioner "github.com/hashicorp/packer/provisioner/puppet-server" + saltmasterlessprovisioner "github.com/hashicorp/packer/provisioner/salt-masterless" + shellprovisioner "github.com/hashicorp/packer/provisioner/shell" + shelllocalprovisioner "github.com/hashicorp/packer/provisioner/shell-local" windowsrestartprovisioner "github.com/hashicorp/packer/provisioner/windows-restart" windowsshellprovisioner "github.com/hashicorp/packer/provisioner/windows-shell" - ) type PluginCommand struct { @@ -82,78 +81,75 @@ type PluginCommand struct { } var Builders = map[string]packer.Builder{ - "alicloud-ecs": new(alicloudecsbuilder.Builder), - "amazon-chroot": new(amazonchrootbuilder.Builder), - "amazon-ebs": new(amazonebsbuilder.Builder), - "amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder), - "amazon-ebsvolume": new(amazonebsvolumebuilder.Builder), - "amazon-instance": new(amazoninstancebuilder.Builder), - "azure-arm": new(azurearmbuilder.Builder), - "cloudstack": new(cloudstackbuilder.Builder), - "digitalocean": new(digitaloceanbuilder.Builder), - "docker": new(dockerbuilder.Builder), - "file": new(filebuilder.Builder), - "googlecompute": new(googlecomputebuilder.Builder), - "hyperv-iso": new(hypervisobuilder.Builder), - "hyperv-vmcx": new(hypervvmcxbuilder.Builder), - "lxc": new(lxcbuilder.Builder), - "lxd": new(lxdbuilder.Builder), - "null": new(nullbuilder.Builder), - "oneandone": new(oneandonebuilder.Builder), - "openstack": new(openstackbuilder.Builder), - "oracle-classic": new(oracleclassicbuilder.Builder), - "oracle-oci": new(oracleocibuilder.Builder), - "parallels-iso": new(parallelsisobuilder.Builder), - "parallels-pvm": new(parallelspvmbuilder.Builder), - "profitbricks": new(profitbricksbuilder.Builder), - "qemu": new(qemubuilder.Builder), - "triton": new(tritonbuilder.Builder), - "virtualbox-iso": new(virtualboxisobuilder.Builder), - "virtualbox-ovf": new(virtualboxovfbuilder.Builder), - "vmware-iso": new(vmwareisobuilder.Builder), - "vmware-vmx": new(vmwarevmxbuilder.Builder), + "alicloud-ecs": new(alicloudecsbuilder.Builder), + "amazon-chroot": new(amazonchrootbuilder.Builder), + "amazon-ebs": new(amazonebsbuilder.Builder), + "amazon-ebssurrogate": new(amazonebssurrogatebuilder.Builder), + "amazon-ebsvolume": new(amazonebsvolumebuilder.Builder), + "amazon-instance": new(amazoninstancebuilder.Builder), + "azure-arm": new(azurearmbuilder.Builder), + "cloudstack": new(cloudstackbuilder.Builder), + "digitalocean": new(digitaloceanbuilder.Builder), + "docker": new(dockerbuilder.Builder), + "file": new(filebuilder.Builder), + "googlecompute": new(googlecomputebuilder.Builder), + "hyperv-iso": new(hypervisobuilder.Builder), + "hyperv-vmcx": new(hypervvmcxbuilder.Builder), + "lxc": new(lxcbuilder.Builder), + "lxd": new(lxdbuilder.Builder), + "null": new(nullbuilder.Builder), + "oneandone": new(oneandonebuilder.Builder), + "openstack": new(openstackbuilder.Builder), + "oracle-classic": new(oracleclassicbuilder.Builder), + "oracle-oci": new(oracleocibuilder.Builder), + "parallels-iso": new(parallelsisobuilder.Builder), + "parallels-pvm": new(parallelspvmbuilder.Builder), + "profitbricks": new(profitbricksbuilder.Builder), + "qemu": new(qemubuilder.Builder), + "triton": new(tritonbuilder.Builder), + "virtualbox-iso": new(virtualboxisobuilder.Builder), + "virtualbox-ovf": new(virtualboxovfbuilder.Builder), + "vmware-iso": new(vmwareisobuilder.Builder), + "vmware-vmx": new(vmwarevmxbuilder.Builder), } - var Provisioners = map[string]packer.Provisioner{ - "ansible": new(ansibleprovisioner.Provisioner), - "ansible-local": new(ansiblelocalprovisioner.Provisioner), - "chef-client": new(chefclientprovisioner.Provisioner), - "chef-solo": new(chefsoloprovisioner.Provisioner), - "converge": new(convergeprovisioner.Provisioner), - "file": new(fileprovisioner.Provisioner), - "powershell": new(powershellprovisioner.Provisioner), - "puppet-masterless": new(puppetmasterlessprovisioner.Provisioner), - "puppet-server": new(puppetserverprovisioner.Provisioner), + "ansible": new(ansibleprovisioner.Provisioner), + "ansible-local": new(ansiblelocalprovisioner.Provisioner), + "chef-client": new(chefclientprovisioner.Provisioner), + "chef-solo": new(chefsoloprovisioner.Provisioner), + "converge": new(convergeprovisioner.Provisioner), + "file": new(fileprovisioner.Provisioner), + "powershell": new(powershellprovisioner.Provisioner), + "puppet-masterless": new(puppetmasterlessprovisioner.Provisioner), + "puppet-server": new(puppetserverprovisioner.Provisioner), "salt-masterless": new(saltmasterlessprovisioner.Provisioner), - "shell": new(shellprovisioner.Provisioner), - "shell-local": new(shelllocalprovisioner.Provisioner), + "shell": new(shellprovisioner.Provisioner), + "shell-local": new(shelllocalprovisioner.Provisioner), "windows-restart": new(windowsrestartprovisioner.Provisioner), - "windows-shell": new(windowsshellprovisioner.Provisioner), + "windows-shell": new(windowsshellprovisioner.Provisioner), } - var PostProcessors = map[string]packer.PostProcessor{ - "alicloud-import": new(alicloudimportpostprocessor.PostProcessor), - "amazon-import": new(amazonimportpostprocessor.PostProcessor), - "artifice": new(artificepostprocessor.PostProcessor), - "atlas": new(atlaspostprocessor.PostProcessor), - "checksum": new(checksumpostprocessor.PostProcessor), - "compress": new(compresspostprocessor.PostProcessor), - "docker-import": new(dockerimportpostprocessor.PostProcessor), - "docker-push": new(dockerpushpostprocessor.PostProcessor), - "docker-save": new(dockersavepostprocessor.PostProcessor), - "docker-tag": new(dockertagpostprocessor.PostProcessor), - "googlecompute-export": new(googlecomputeexportpostprocessor.PostProcessor), - "manifest": new(manifestpostprocessor.PostProcessor), - "shell-local": new(shelllocalpostprocessor.PostProcessor), - "vagrant": new(vagrantpostprocessor.PostProcessor), - "vagrant-cloud": new(vagrantcloudpostprocessor.PostProcessor), - "vsphere": new(vspherepostprocessor.PostProcessor), - "vsphere-template": new(vspheretemplatepostprocessor.PostProcessor), + "alicloud-import": new(alicloudimportpostprocessor.PostProcessor), + "amazon-import": new(amazonimportpostprocessor.PostProcessor), + "artifice": new(artificepostprocessor.PostProcessor), + "atlas": new(atlaspostprocessor.PostProcessor), + "checksum": new(checksumpostprocessor.PostProcessor), + "compress": new(compresspostprocessor.PostProcessor), + "docker-import": new(dockerimportpostprocessor.PostProcessor), + "docker-push": new(dockerpushpostprocessor.PostProcessor), + "docker-save": new(dockersavepostprocessor.PostProcessor), + "docker-tag": new(dockertagpostprocessor.PostProcessor), + "googlecompute-export": new(googlecomputeexportpostprocessor.PostProcessor), + "manifest": new(manifestpostprocessor.PostProcessor), + "shell-local": new(shelllocalpostprocessor.PostProcessor), + "vagrant": new(vagrantpostprocessor.PostProcessor), + "vagrant-cloud": new(vagrantcloudpostprocessor.PostProcessor), + "vsphere": new(vspherepostprocessor.PostProcessor), + "vsphere-template": new(vspheretemplatepostprocessor.PostProcessor), } - var pluginRegexp = regexp.MustCompile("packer-(builder|post-processor|provisioner)-(.+)") func (c *PluginCommand) Run(args []string) int { From dd2384483bb37fcb51e15b0ffb16cfe7bf844811 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 25 Jan 2018 14:42:39 -0800 Subject: [PATCH 31/61] add context to steps --- builder/oracle/classic/step_add_keys.go | 2 +- builder/oracle/classic/step_create_instance.go | 2 +- builder/oracle/classic/step_create_ip_reservation.go | 2 +- builder/oracle/classic/step_security.go | 2 +- builder/oracle/classic/step_snapshot.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/builder/oracle/classic/step_add_keys.go b/builder/oracle/classic/step_add_keys.go index f28a9bce9..accb613de 100644 --- a/builder/oracle/classic/step_add_keys.go +++ b/builder/oracle/classic/step_add_keys.go @@ -11,7 +11,7 @@ import ( type stepAddKeysToAPI struct{} -func (s *stepAddKeysToAPI) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepAddKeysToAPI) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { // get variables from state ui := state.Get("ui").(packer.Ui) ui.Say("Adding SSH keys to API...") diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 8a96823aa..65b7d8736 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -11,7 +11,7 @@ import ( type stepCreateInstance struct{} -func (s *stepCreateInstance) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { // get variables from state ui := state.Get("ui").(packer.Ui) ui.Say("Creating Instance...") diff --git a/builder/oracle/classic/step_create_ip_reservation.go b/builder/oracle/classic/step_create_ip_reservation.go index 733c07e70..d57aa41ea 100644 --- a/builder/oracle/classic/step_create_ip_reservation.go +++ b/builder/oracle/classic/step_create_ip_reservation.go @@ -11,7 +11,7 @@ import ( type stepCreateIPReservation struct{} -func (s *stepCreateIPReservation) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepCreateIPReservation) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) ui.Say("Creating IP reservation...") config := state.Get("config").(*Config) diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index cc965caeb..c3184f36d 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -12,7 +12,7 @@ import ( type stepSecurity struct{} -func (s *stepSecurity) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepSecurity) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) ui.Say("Configuring security lists and rules to enable SSH access...") diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index 2e4e9d7a3..5fa838e0e 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -10,7 +10,7 @@ import ( type stepSnapshot struct{} -func (s *stepSnapshot) Run(state multistep.StateBag) multistep.StepAction { +func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { // get variables from state ui := state.Get("ui").(packer.Ui) ui.Say("Creating Snapshot...") From 6dc0bd759ad1eae8ffeec67a3fe916d7f1cf9ae6 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 25 Jan 2018 14:43:55 -0800 Subject: [PATCH 32/61] import context --- builder/oracle/classic/step_add_keys.go | 1 + builder/oracle/classic/step_create_instance.go | 1 + builder/oracle/classic/step_create_ip_reservation.go | 1 + builder/oracle/classic/step_security.go | 1 + builder/oracle/classic/step_snapshot.go | 1 + 5 files changed, 5 insertions(+) diff --git a/builder/oracle/classic/step_add_keys.go b/builder/oracle/classic/step_add_keys.go index accb613de..4e9c46d61 100644 --- a/builder/oracle/classic/step_add_keys.go +++ b/builder/oracle/classic/step_add_keys.go @@ -1,6 +1,7 @@ package classic import ( + "context" "fmt" "strings" diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 65b7d8736..a0183832d 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -1,6 +1,7 @@ package classic import ( + "context" "fmt" "log" diff --git a/builder/oracle/classic/step_create_ip_reservation.go b/builder/oracle/classic/step_create_ip_reservation.go index d57aa41ea..034cf5925 100644 --- a/builder/oracle/classic/step_create_ip_reservation.go +++ b/builder/oracle/classic/step_create_ip_reservation.go @@ -1,6 +1,7 @@ package classic import ( + "context" "fmt" "log" diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index c3184f36d..5cfc0e0d5 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -1,6 +1,7 @@ package classic import ( + "context" "fmt" "log" "strings" diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index 5fa838e0e..4d1c531a7 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -1,6 +1,7 @@ package classic import ( + "context" "fmt" "github.com/hashicorp/go-oracle-terraform/compute" From 4dc42942f5597a2155ac221b9c59fe7f53285ccd Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 25 Jan 2018 14:45:09 -0800 Subject: [PATCH 33/61] fix multistep path --- builder/oracle/classic/builder.go | 2 +- builder/oracle/classic/step_add_keys.go | 2 +- builder/oracle/classic/step_create_instance.go | 2 +- builder/oracle/classic/step_create_ip_reservation.go | 2 +- builder/oracle/classic/step_security.go | 2 +- builder/oracle/classic/step_snapshot.go | 2 +- builder/oracle/common/ssh.go | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 7847ef179..fd084b366 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -10,8 +10,8 @@ import ( ocommon "github.com/hashicorp/packer/builder/oracle/common" "github.com/hashicorp/packer/common" "github.com/hashicorp/packer/helper/communicator" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" ) // BuilderId uniquely identifies the builder diff --git a/builder/oracle/classic/step_add_keys.go b/builder/oracle/classic/step_add_keys.go index 4e9c46d61..e8013cf88 100644 --- a/builder/oracle/classic/step_add_keys.go +++ b/builder/oracle/classic/step_add_keys.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" ) type stepAddKeysToAPI struct{} diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index a0183832d..9438431bb 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -6,8 +6,8 @@ import ( "log" "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" ) type stepCreateInstance struct{} diff --git a/builder/oracle/classic/step_create_ip_reservation.go b/builder/oracle/classic/step_create_ip_reservation.go index 034cf5925..f24de7766 100644 --- a/builder/oracle/classic/step_create_ip_reservation.go +++ b/builder/oracle/classic/step_create_ip_reservation.go @@ -6,8 +6,8 @@ import ( "log" "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" ) type stepCreateIPReservation struct{} diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index 5cfc0e0d5..1e00b2c7b 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" ) type stepSecurity struct{} diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index 4d1c531a7..30a7d32c8 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -5,8 +5,8 @@ import ( "fmt" "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" - "github.com/mitchellh/multistep" ) type stepSnapshot struct{} diff --git a/builder/oracle/common/ssh.go b/builder/oracle/common/ssh.go index aa2780128..8e448e592 100644 --- a/builder/oracle/common/ssh.go +++ b/builder/oracle/common/ssh.go @@ -4,7 +4,7 @@ import ( "fmt" packerssh "github.com/hashicorp/packer/communicator/ssh" - "github.com/mitchellh/multistep" + "github.com/hashicorp/packer/helper/multistep" "golang.org/x/crypto/ssh" ) From 7f21ca546ddc0198af14738c46ac7c2e95b92a43 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 25 Jan 2018 14:47:30 -0800 Subject: [PATCH 34/61] we're not using go-getter --- vendor/vendor.json | 6 ------ 1 file changed, 6 deletions(-) diff --git a/vendor/vendor.json b/vendor/vendor.json index 256c2e1d2..fe36fe283 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -785,12 +785,6 @@ "path": "github.com/hashicorp/go-cleanhttp", "revision": "875fb671b3ddc66f8e2f0acc33829c8cb989a38d" }, - { - "checksumSHA1": "1hPCerVn1bWA+9vaAGqnIkRtSFc=", - "path": "github.com/hashicorp/go-getter", - "revision": "cb2c2774e9771bd9568d843be4e59298786550fd", - "revisionTime": "2017-12-20T21:10:09Z" - }, { "checksumSHA1": "lrSl49G23l6NhfilxPM0XFs5rZo=", "path": "github.com/hashicorp/go-multierror", From 0fad49e897ea9446cacffb0d2c80a27ec8acd495 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 25 Jan 2018 15:05:36 -0800 Subject: [PATCH 35/61] simplify --- builder/oracle/classic/step_create_instance.go | 1 - builder/oracle/classic/step_security.go | 2 -- 2 files changed, 3 deletions(-) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 9438431bb..2f8493fd1 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -78,5 +78,4 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { } // TODO wait for instance state to change to deleted? ui.Say("Terminated instance.") - return } diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index 1e00b2c7b..8b1c48a4c 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -92,6 +92,4 @@ func (s *stepSecurity) Cleanup(state multistep.StateBag) { ui.Say(fmt.Sprintf("Error deleting the packer-generated security list %s; "+ "please delete manually. (error : %s)", secListName, err.Error())) } - - return } From 77277ebc98d0337d4c9507d957eda925c0e9a20b Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 25 Jan 2018 16:12:03 -0800 Subject: [PATCH 36/61] add logging behind "PACKER_OCI_CLASSIC_LOGGING" env var --- builder/oracle/classic/builder.go | 3 +++ builder/oracle/classic/log.go | 14 ++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 builder/oracle/classic/log.go diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index fd084b366..7c682b875 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -3,6 +3,7 @@ package classic import ( "fmt" "log" + "os" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/go-oracle-terraform/compute" @@ -35,6 +36,7 @@ func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { + loggingEnabled := os.Getenv("PACKER_OCI_CLASSIC_LOGGING")) != "" httpClient := cleanhttp.DefaultClient() config := &opc.Config{ Username: opc.String(b.config.Username), @@ -42,6 +44,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe IdentityDomain: opc.String(b.config.IdentityDomain), APIEndpoint: b.config.apiEndpointURL, LogLevel: opc.LogDebug, + Logger: &Logger{loggingEnabled}, // Logger: # Leave blank to use the default logger, or provide your own HTTPClient: httpClient, } diff --git a/builder/oracle/classic/log.go b/builder/oracle/classic/log.go new file mode 100644 index 000000000..7389dc057 --- /dev/null +++ b/builder/oracle/classic/log.go @@ -0,0 +1,14 @@ +package classic + +import "log" + +type Logger struct { + Enabled bool +} + +func (l *Logger) Log(input ...interface{}) { + if !l.Enabled { + return + } + log.Println(input...) +} From dbf5d52c43bfe83bbd3a6fc05b4e41032d407cf1 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 25 Jan 2018 16:20:14 -0800 Subject: [PATCH 37/61] update mapstructure library --- .../mitchellh/mapstructure/README.md | 2 +- .../mitchellh/mapstructure/decode_hooks.go | 19 +- .../mitchellh/mapstructure/mapstructure.go | 266 ++++++++++++++---- vendor/vendor.json | 5 +- 4 files changed, 231 insertions(+), 61 deletions(-) diff --git a/vendor/github.com/mitchellh/mapstructure/README.md b/vendor/github.com/mitchellh/mapstructure/README.md index 659d6885f..7ecc785e4 100644 --- a/vendor/github.com/mitchellh/mapstructure/README.md +++ b/vendor/github.com/mitchellh/mapstructure/README.md @@ -1,4 +1,4 @@ -# mapstructure +# mapstructure [![Godoc](https://godoc.org/github.com/mitchell/mapstructure?status.svg)](https://godoc.org/github.com/mitchell/mapstructure) mapstructure is a Go library for decoding generic map values to structures and vice versa, while providing helpful error handling. diff --git a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go index aa91f76ce..afcfd5eed 100644 --- a/vendor/github.com/mitchellh/mapstructure/decode_hooks.go +++ b/vendor/github.com/mitchellh/mapstructure/decode_hooks.go @@ -38,12 +38,6 @@ func DecodeHookExec( raw DecodeHookFunc, from reflect.Type, to reflect.Type, data interface{}) (interface{}, error) { - // Build our arguments that reflect expects - argVals := make([]reflect.Value, 3) - argVals[0] = reflect.ValueOf(from) - argVals[1] = reflect.ValueOf(to) - argVals[2] = reflect.ValueOf(data) - switch f := typedDecodeHook(raw).(type) { case DecodeHookFuncType: return f(from, to, data) @@ -72,7 +66,10 @@ func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { } // Modify the from kind to be correct with the new data - f = reflect.ValueOf(data).Type() + f = nil + if val := reflect.ValueOf(data); val.IsValid() { + f = val.Type() + } } return data, nil @@ -118,6 +115,11 @@ func StringToTimeDurationHookFunc() DecodeHookFunc { } } +// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to +// the decoder. +// +// Note that this is significantly different from the WeaklyTypedInput option +// of the DecoderConfig. func WeaklyTypedHook( f reflect.Kind, t reflect.Kind, @@ -129,9 +131,8 @@ func WeaklyTypedHook( case reflect.Bool: if dataVal.Bool() { return "1", nil - } else { - return "0", nil } + return "0", nil case reflect.Float32: return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil case reflect.Int: diff --git a/vendor/github.com/mitchellh/mapstructure/mapstructure.go b/vendor/github.com/mitchellh/mapstructure/mapstructure.go index 40be5116d..39ec1e943 100644 --- a/vendor/github.com/mitchellh/mapstructure/mapstructure.go +++ b/vendor/github.com/mitchellh/mapstructure/mapstructure.go @@ -1,5 +1,5 @@ -// The mapstructure package exposes functionality to convert an -// abitrary map[string]interface{} into a native Go structure. +// Package mapstructure exposes functionality to convert an arbitrary +// map[string]interface{} into a native Go structure. // // The Go structure can be arbitrarily complex, containing slices, // other structs, etc. and the decoder will properly decode nested @@ -8,6 +8,7 @@ package mapstructure import ( + "encoding/json" "errors" "fmt" "reflect" @@ -31,7 +32,12 @@ import ( // both. type DecodeHookFunc interface{} +// DecodeHookFuncType is a DecodeHookFunc which has complete information about +// the source and target types. type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface{}, error) + +// DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the +// source and target types. type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) // DecoderConfig is the configuration that is used to create a new decoder @@ -67,6 +73,10 @@ type DecoderConfig struct { // FALSE, false, False. Anything else is an error) // - empty array = empty map and vice versa // - negative numbers to overflowed uint values (base 10) + // - slice of maps to a merged map + // - single values are converted to slices if required. Each + // element is weakly decoded. For example: "4" can become []int{4} + // if the target type is an int slice. // WeaklyTypedInput bool @@ -200,7 +210,7 @@ func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error d.config.DecodeHook, dataVal.Type(), val.Type(), data) if err != nil { - return err + return fmt.Errorf("error decoding '%s': %s", name, err) } } @@ -227,6 +237,10 @@ func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error err = d.decodePtr(name, data, val) case reflect.Slice: err = d.decodeSlice(name, data, val) + case reflect.Array: + err = d.decodeArray(name, data, val) + case reflect.Func: + err = d.decodeFunc(name, data, val) default: // If we reached this point then we weren't able to decode it return fmt.Errorf("%s: unsupported type: %s", name, dataKind) @@ -245,6 +259,10 @@ func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error // value to "data" of that type. func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error { dataVal := reflect.ValueOf(data) + if !dataVal.IsValid() { + dataVal = reflect.Zero(val.Type()) + } + dataValType := dataVal.Type() if !dataValType.AssignableTo(val.Type()) { return fmt.Errorf( @@ -276,12 +294,22 @@ func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64)) - case dataKind == reflect.Slice && d.config.WeaklyTypedInput: + case dataKind == reflect.Slice && d.config.WeaklyTypedInput, + dataKind == reflect.Array && d.config.WeaklyTypedInput: dataType := dataVal.Type() elemKind := dataType.Elem().Kind() - switch { - case elemKind == reflect.Uint8: - val.SetString(string(dataVal.Interface().([]uint8))) + switch elemKind { + case reflect.Uint8: + var uints []uint8 + if dataKind == reflect.Array { + uints = make([]uint8, dataVal.Len(), dataVal.Len()) + for i := range uints { + uints[i] = dataVal.Index(i).Interface().(uint8) + } + } else { + uints = dataVal.Interface().([]uint8) + } + val.SetString(string(uints)) default: converted = false } @@ -301,6 +329,7 @@ func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error { dataVal := reflect.ValueOf(data) dataKind := getKind(dataVal) + dataType := dataVal.Type() switch { case dataKind == reflect.Int: @@ -322,6 +351,14 @@ func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) er } else { return fmt.Errorf("cannot parse '%s' as int: %s", name, err) } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := jn.Int64() + if err != nil { + return fmt.Errorf( + "error decoding json.Number into %s: %s", name, err) + } + val.SetInt(i) default: return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s'", @@ -408,6 +445,7 @@ func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) e func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error { dataVal := reflect.ValueOf(data) dataKind := getKind(dataVal) + dataType := dataVal.Type() switch { case dataKind == reflect.Int: @@ -415,7 +453,7 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) case dataKind == reflect.Uint: val.SetFloat(float64(dataVal.Uint())) case dataKind == reflect.Float32: - val.SetFloat(float64(dataVal.Float())) + val.SetFloat(dataVal.Float()) case dataKind == reflect.Bool && d.config.WeaklyTypedInput: if dataVal.Bool() { val.SetFloat(1) @@ -429,6 +467,14 @@ func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) } else { return fmt.Errorf("cannot parse '%s' as float: %s", name, err) } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := jn.Float64() + if err != nil { + return fmt.Errorf( + "error decoding json.Number into %s: %s", name, err) + } + val.SetFloat(i) default: return fmt.Errorf( "'%s' expected type '%s', got unconvertible type '%s'", @@ -456,15 +502,30 @@ func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) er // Check input type dataVal := reflect.Indirect(reflect.ValueOf(data)) if dataVal.Kind() != reflect.Map { - // Accept empty array/slice instead of an empty map in weakly typed mode - if d.config.WeaklyTypedInput && - (dataVal.Kind() == reflect.Slice || dataVal.Kind() == reflect.Array) && - dataVal.Len() == 0 { - val.Set(valMap) - return nil - } else { - return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) + // In weak mode, we accept a slice of maps as an input... + if d.config.WeaklyTypedInput { + switch dataVal.Kind() { + case reflect.Array, reflect.Slice: + // Special case for BC reasons (covered by tests) + if dataVal.Len() == 0 { + val.Set(valMap) + return nil + } + + for i := 0; i < dataVal.Len(); i++ { + err := d.decode( + fmt.Sprintf("%s[%d]", name, i), + dataVal.Index(i).Interface(), val) + if err != nil { + return err + } + } + + return nil + } } + + return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) } // Accumulate errors @@ -507,7 +568,12 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) er // into that. Then set the value of the pointer to this type. valType := val.Type() valElemType := valType.Elem() - realVal := reflect.New(valElemType) + + realVal := val + if realVal.IsNil() || d.config.ZeroFields { + realVal = reflect.New(valElemType) + } + if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil { return err } @@ -516,6 +582,19 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) er return nil } +func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error { + // Create an element of the concrete (non pointer) type and decode + // into that. Then set the value of the pointer to this type. + dataVal := reflect.Indirect(reflect.ValueOf(data)) + if val.Type() != dataVal.Type() { + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s'", + name, val.Type(), dataVal.Type()) + } + val.Set(dataVal) + return nil +} + func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) dataValKind := dataVal.Kind() @@ -523,26 +602,44 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) valElemType := valType.Elem() sliceType := reflect.SliceOf(valElemType) - // Check input type - if dataValKind != reflect.Array && dataValKind != reflect.Slice { - // Accept empty map instead of array/slice in weakly typed mode - if d.config.WeaklyTypedInput && dataVal.Kind() == reflect.Map && dataVal.Len() == 0 { - val.Set(reflect.MakeSlice(sliceType, 0, 0)) - return nil - } else { + valSlice := val + if valSlice.IsNil() || d.config.ZeroFields { + // Check input type + if dataValKind != reflect.Array && dataValKind != reflect.Slice { + if d.config.WeaklyTypedInput { + switch { + // Empty maps turn into empty slices + case dataValKind == reflect.Map: + if dataVal.Len() == 0 { + val.Set(reflect.MakeSlice(sliceType, 0, 0)) + return nil + } + + // All other types we try to convert to the slice type + // and "lift" it into it. i.e. a string becomes a string slice. + default: + // Just re-try this function with data as a slice. + return d.decodeSlice(name, []interface{}{data}, val) + } + } + return fmt.Errorf( "'%s': source data must be an array or slice, got %s", name, dataValKind) - } - } - // Make a new slice to hold our result, same size as the original data. - valSlice := reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) + } + + // Make a new slice to hold our result, same size as the original data. + valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) + } // Accumulate any errors errors := make([]string, 0) for i := 0; i < dataVal.Len(); i++ { currentData := dataVal.Index(i).Interface() + for valSlice.Len() <= i { + valSlice = reflect.Append(valSlice, reflect.Zero(valElemType)) + } currentField := valSlice.Index(i) fieldName := fmt.Sprintf("%s[%d]", name, i) @@ -562,6 +659,73 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) return nil } +func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataValKind := dataVal.Kind() + valType := val.Type() + valElemType := valType.Elem() + arrayType := reflect.ArrayOf(valType.Len(), valElemType) + + valArray := val + + if valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields { + // Check input type + if dataValKind != reflect.Array && dataValKind != reflect.Slice { + if d.config.WeaklyTypedInput { + switch { + // Empty maps turn into empty arrays + case dataValKind == reflect.Map: + if dataVal.Len() == 0 { + val.Set(reflect.Zero(arrayType)) + return nil + } + + // All other types we try to convert to the array type + // and "lift" it into it. i.e. a string becomes a string array. + default: + // Just re-try this function with data as a slice. + return d.decodeArray(name, []interface{}{data}, val) + } + } + + return fmt.Errorf( + "'%s': source data must be an array or slice, got %s", name, dataValKind) + + } + if dataVal.Len() > arrayType.Len() { + return fmt.Errorf( + "'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len()) + + } + + // Make a new array to hold our result, same size as the original data. + valArray = reflect.New(arrayType).Elem() + } + + // Accumulate any errors + errors := make([]string, 0) + + for i := 0; i < dataVal.Len(); i++ { + currentData := dataVal.Index(i).Interface() + currentField := valArray.Index(i) + + fieldName := fmt.Sprintf("%s[%d]", name, i) + if err := d.decode(fieldName, currentData, currentField); err != nil { + errors = appendErrors(errors, err) + } + } + + // Finally, set the value to the array we built up + val.Set(valArray) + + // If there were errors, we return those + if len(errors) > 0 { + return &Error{errors} + } + + return nil +} + func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error { dataVal := reflect.Indirect(reflect.ValueOf(data)) @@ -601,23 +765,20 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) // Compile the list of all the fields that we're going to be decoding // from all the structs. - fields := make(map[*reflect.StructField]reflect.Value) + type field struct { + field reflect.StructField + val reflect.Value + } + fields := []field{} for len(structs) > 0 { structVal := structs[0] structs = structs[1:] structType := structVal.Type() + for i := 0; i < structType.NumField(); i++ { fieldType := structType.Field(i) - - if fieldType.Anonymous { - fieldKind := fieldType.Type.Kind() - if fieldKind != reflect.Struct { - errors = appendErrors(errors, - fmt.Errorf("%s: unsupported type: %s", fieldType.Name, fieldKind)) - continue - } - } + fieldKind := fieldType.Type.Kind() // If "squash" is specified in the tag, we squash the field down. squash := false @@ -630,19 +791,26 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) } if squash { - structs = append(structs, val.FieldByName(fieldType.Name)) + if fieldKind != reflect.Struct { + errors = appendErrors(errors, + fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldKind)) + } else { + structs = append(structs, structVal.FieldByName(fieldType.Name)) + } continue } // Normal struct field, store it away - fields[&fieldType] = structVal.Field(i) + fields = append(fields, field{fieldType, structVal.Field(i)}) } } - for fieldType, field := range fields { - fieldName := fieldType.Name + // for fieldType, field := range fields { + for _, f := range fields { + field, fieldValue := f.field, f.val + fieldName := field.Name - tagValue := fieldType.Tag.Get(d.config.TagName) + tagValue := field.Tag.Get(d.config.TagName) tagValue = strings.SplitN(tagValue, ",", 2)[0] if tagValue != "" { fieldName = tagValue @@ -653,7 +821,7 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) if !rawMapVal.IsValid() { // Do a slower search by iterating over each key and // doing case-insensitive search. - for dataValKey, _ := range dataValKeys { + for dataValKey := range dataValKeys { mK, ok := dataValKey.Interface().(string) if !ok { // Not a string key @@ -677,14 +845,14 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) // Delete the key we're using from the unused map so we stop tracking delete(dataValKeysUnused, rawMapKey.Interface()) - if !field.IsValid() { + if !fieldValue.IsValid() { // This should never happen panic("field is not valid") } // If we can't set the field, then it is unexported or something, // and we just continue onwards. - if !field.CanSet() { + if !fieldValue.CanSet() { continue } @@ -694,14 +862,14 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) fieldName = fmt.Sprintf("%s.%s", name, fieldName) } - if err := d.decode(fieldName, rawMapVal.Interface(), field); err != nil { + if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil { errors = appendErrors(errors, err) } } if d.config.ErrorUnused && len(dataValKeysUnused) > 0 { keys := make([]string, 0, len(dataValKeysUnused)) - for rawKey, _ := range dataValKeysUnused { + for rawKey := range dataValKeysUnused { keys = append(keys, rawKey.(string)) } sort.Strings(keys) @@ -716,7 +884,7 @@ func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) // Add the unused keys to the list of unused keys if we're tracking metadata if d.config.Metadata != nil { - for rawKey, _ := range dataValKeysUnused { + for rawKey := range dataValKeysUnused { key := rawKey.(string) if name != "" { key = fmt.Sprintf("%s.%s", name, key) diff --git a/vendor/vendor.json b/vendor/vendor.json index fe36fe283..6172b8c33 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -981,9 +981,10 @@ "revision": "87b45ffd0e9581375c491fef3d32130bb15c5bd7" }, { - "checksumSHA1": "4Js6Jlu93Wa0o6Kjt393L9Z7diE=", + "checksumSHA1": "1JtAhgmRN0x794LRNhs0DJ5t8io=", "path": "github.com/mitchellh/mapstructure", - "revision": "281073eb9eb092240d33ef253c404f1cca550309" + "revision": "b4575eea38cca1123ec2dc90c26529b5c5acfcff", + "revisionTime": "2018-01-11T00:07:20Z" }, { "checksumSHA1": "m2L8ohfZiFRsMW3iynaH/TWgnSY=", From 4622bb4585994ae4a3bfbefc69ebfb0a8b929c06 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Thu, 25 Jan 2018 16:29:24 -0800 Subject: [PATCH 38/61] return no artifact if no snapshot was created --- builder/oracle/classic/builder.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 7c682b875..9264e6466 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -92,6 +92,11 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe return nil, rawErr.(error) } + // If there is no snapshot, then just return + if _, ok := state.GetOk("snapshot"); !ok { + return nil, nil + } + // Build the artifact and return it artifact := &Artifact{ Snapshot: state.Get("snapshot").(*compute.Snapshot), From de2e5edf2ee57bd0dda934d23d9fc9d2aa96e822 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 26 Jan 2018 08:43:51 -0800 Subject: [PATCH 39/61] remove errant change in amazon builder --- builder/amazon/common/step_key_pair.go | 3 +-- builder/oracle/classic/builder.go | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/builder/amazon/common/step_key_pair.go b/builder/amazon/common/step_key_pair.go index 55f5aace7..927101fcf 100644 --- a/builder/amazon/common/step_key_pair.go +++ b/builder/amazon/common/step_key_pair.go @@ -6,7 +6,6 @@ import ( "io/ioutil" "os" "runtime" - "strings" "github.com/aws/aws-sdk-go/service/ec2" "github.com/hashicorp/packer/helper/multistep" @@ -37,7 +36,7 @@ func (s *StepKeyPair) Run(_ context.Context, state multistep.StateBag) multistep } state.Put("keyPair", s.KeyPairName) - state.Put("privateKey", strings.TrimSpace(string(privateKeyBytes))) + state.Put("privateKey", string(privateKeyBytes)) return multistep.ActionContinue } diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 9264e6466..8537289cf 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -35,8 +35,7 @@ func (b *Builder) Prepare(rawConfig ...interface{}) ([]string, error) { } func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packer.Artifact, error) { - - loggingEnabled := os.Getenv("PACKER_OCI_CLASSIC_LOGGING")) != "" + loggingEnabled := os.Getenv("PACKER_OCI_CLASSIC_LOGGING") != "" httpClient := cleanhttp.DefaultClient() config := &opc.Config{ Username: opc.String(b.config.Username), From 25bc1da8fee6f2314777bd47346e7151e7c6d65f Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 26 Jan 2018 08:48:23 -0800 Subject: [PATCH 40/61] remove unsused access config --- builder/oracle/classic/config.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index eb3fd6876..409118dd5 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -15,8 +15,6 @@ type Config struct { common.PackerConfig `mapstructure:",squash"` Comm communicator.Config `mapstructure:",squash"` - Access *AccessConfig - // Access config overrides Username string `mapstructure:"username"` Password string `mapstructure:"password"` From b6d21ecd63fc17650d32efadeaa2ffc85b1793f6 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 26 Jan 2018 08:53:24 -0800 Subject: [PATCH 41/61] validate that required fields are present --- builder/oracle/classic/config.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 409118dd5..178bb2d4b 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -56,7 +56,26 @@ func NewConfig(raws ...interface{}) (*Config, error) { c.SSHSourceList = "seciplist:/oracle/public/public-internet" } + // Validate that all required fields are present var errs *packer.MultiError + required := map[string]string{ + "username": c.Username, + "password": c.Password, + "api_endpoint": c.APIEndpoint, + "identity_domain": c.IdentityDomain, + "image_list": c.ImageList, + "shape": c.Shape, + } + for k, v := range required { + if v == "" { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("You must specify a %s.", k)) + } + } + + if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { + errs = packer.MultiErrorAppend(errs, es...) + } + if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { errs = packer.MultiErrorAppend(errs, es...) } From ff9fef5ed2db3b2be53c4074ed08393d24895491 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 26 Jan 2018 09:51:16 -0800 Subject: [PATCH 42/61] switch to using a UUID for packer-generated keys, and clean them up at end of build --- builder/oracle/classic/step_add_keys.go | 40 +++++++++++++++---------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/builder/oracle/classic/step_add_keys.go b/builder/oracle/classic/step_add_keys.go index e8013cf88..8a6025977 100644 --- a/builder/oracle/classic/step_add_keys.go +++ b/builder/oracle/classic/step_add_keys.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/hashicorp/go-oracle-terraform/compute" + uuid "github.com/hashicorp/go-uuid" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -23,7 +24,13 @@ func (s *stepAddKeysToAPI) Run(_ context.Context, state multistep.StateBag) mult sshPublicKey := strings.TrimSpace(state.Get("publicKey").(string)) // form API call to add key to compute cloud - sshKeyName := fmt.Sprintf("/Compute-%s/%s/packer_generated_key", config.IdentityDomain, config.Username) + uuid_string, err := uuid.GenerateUUID() + if err != nil { + ui.Error(fmt.Sprintf("Error creating unique SSH key name: %s", err.Error())) + return multistep.ActionHalt + } + sshKeyName := fmt.Sprintf("/Compute-%s/%s/packer_generated_key_%s", + config.IdentityDomain, config.Username, uuid_string) sshKeysClient := client.SSHKeys() sshKeysInput := compute.CreateSSHKeyInput{ @@ -35,25 +42,26 @@ func (s *stepAddKeysToAPI) Run(_ context.Context, state multistep.StateBag) mult // Load the packer-generated SSH key into the Oracle Compute cloud. keyInfo, err := sshKeysClient.CreateSSHKey(&sshKeysInput) if err != nil { - // Key already exists; update key instead of creating it - if strings.Contains(err.Error(), "packer_generated_key already exists") { - updateKeysInput := compute.UpdateSSHKeyInput{ - Name: sshKeyName, - Key: sshPublicKey, - Enabled: true, - } - keyInfo, err = sshKeysClient.UpdateSSHKey(&updateKeysInput) - } else { - err = fmt.Errorf("Problem adding Public SSH key through Oracle's API: %s", err) - ui.Error(err.Error()) - state.Put("error", err) - return multistep.ActionHalt - } + err = fmt.Errorf("Problem adding Public SSH key through Oracle's API: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt } state.Put("key_name", keyInfo.Name) return multistep.ActionContinue } func (s *stepAddKeysToAPI) Cleanup(state multistep.StateBag) { - // Nothing to do + // Delete the keys we created during this run + keyName := state.Get("key_name").(string) + ui := state.Get("ui").(packer.Ui) + ui.Say("Deleting SSH keys...") + deleteInput := compute.DeleteSSHKeyInput{Name: keyName} + client := state.Get("client").(*compute.ComputeClient) + deleteClient := client.SSHKeys() + err := deleteClient.DeleteSSHKey(&deleteInput) + if err != nil { + ui.Error(fmt.Sprintf("Error deleting SSH keys: %s", err.Error())) + } + return } From 0e5be59947ba353a09cd0c2a24e057b459937a51 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 26 Jan 2018 09:55:31 -0800 Subject: [PATCH 43/61] wrap error message for clarity --- builder/oracle/classic/step_create_ip_reservation.go | 2 -- builder/oracle/classic/step_security.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/builder/oracle/classic/step_create_ip_reservation.go b/builder/oracle/classic/step_create_ip_reservation.go index f24de7766..84154631b 100644 --- a/builder/oracle/classic/step_create_ip_reservation.go +++ b/builder/oracle/classic/step_create_ip_reservation.go @@ -3,7 +3,6 @@ package classic import ( "context" "fmt" - "log" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/helper/multistep" @@ -33,7 +32,6 @@ func (s *stepCreateIPReservation) Run(_ context.Context, state multistep.StateBa return multistep.ActionHalt } state.Put("instance_ip", ipRes.IP) - log.Printf("debug: ipRes is %#v", ipRes) return multistep.ActionContinue } diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index 8b1c48a4c..dae341ba6 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -54,7 +54,7 @@ func (s *stepSecurity) Run(_ context.Context, state multistep.StateBag) multiste config.IdentityDomain, config.Username, config.ImageName) _, err = secRulesClient.CreateSecRule(&secRulesInput) if err != nil { - log.Printf(err.Error()) + log.Printf("Error creating security rule to allow SSH: %s", err.Error()) if !strings.Contains(err.Error(), "already exists") { err = fmt.Errorf("Error creating security rule to"+ " allow Packer to connect to Oracle instance via SSH: %s", err) From fad4d5c27248ea350d4321e80462f4739057cf68 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 26 Jan 2018 12:40:34 -0800 Subject: [PATCH 44/61] update tests for mapstructure behavior changes --- builder/amazon/chroot/builder_test.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/builder/amazon/chroot/builder_test.go b/builder/amazon/chroot/builder_test.go index c13658286..c0e52a2f6 100644 --- a/builder/amazon/chroot/builder_test.go +++ b/builder/amazon/chroot/builder_test.go @@ -71,11 +71,16 @@ func TestBuilderPrepare_ChrootMounts(t *testing.T) { if err != nil { t.Errorf("err: %s", err) } +} + +func TestBuilderPrepare_ChrootMountsBadDefaults(t *testing.T) { + b := &Builder{} + config := testConfig() config["chroot_mounts"] = [][]string{ {"bad"}, } - warnings, err = b.Prepare(config) + warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } @@ -135,9 +140,14 @@ func TestBuilderPrepare_CopyFiles(t *testing.T) { if len(b.config.CopyFiles) != 1 && b.config.CopyFiles[0] != "/etc/resolv.conf" { t.Errorf("Was expecting default value for copy_files.") } +} + +func TestBuilderPrepare_CopyFilesNoDefault(t *testing.T) { + b := &Builder{} + config := testConfig() config["copy_files"] = []string{} - warnings, err = b.Prepare(config) + warnings, err := b.Prepare(config) if len(warnings) > 0 { t.Fatalf("bad: %#v", warnings) } From 18ffde4ecfa39402af0c9ddef12138222e51c8e4 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 26 Jan 2018 12:59:46 -0800 Subject: [PATCH 45/61] remove unused file --- builder/oracle/classic/access_config.go | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 builder/oracle/classic/access_config.go diff --git a/builder/oracle/classic/access_config.go b/builder/oracle/classic/access_config.go deleted file mode 100644 index 99483ceec..000000000 --- a/builder/oracle/classic/access_config.go +++ /dev/null @@ -1,4 +0,0 @@ -package classic - -type AccessConfig struct { -} From 71acccc1ed4afa077d0a1d65c8efba628f7077f2 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 26 Jan 2018 13:12:35 -0800 Subject: [PATCH 46/61] add UI output with resource names --- builder/oracle/classic/step_add_keys.go | 3 ++- builder/oracle/classic/step_create_instance.go | 4 ++-- builder/oracle/classic/step_create_ip_reservation.go | 9 +++++++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/builder/oracle/classic/step_add_keys.go b/builder/oracle/classic/step_add_keys.go index 8a6025977..b8331eb94 100644 --- a/builder/oracle/classic/step_add_keys.go +++ b/builder/oracle/classic/step_add_keys.go @@ -16,7 +16,6 @@ type stepAddKeysToAPI struct{} func (s *stepAddKeysToAPI) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { // get variables from state ui := state.Get("ui").(packer.Ui) - ui.Say("Adding SSH keys to API...") config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) @@ -32,6 +31,8 @@ func (s *stepAddKeysToAPI) Run(_ context.Context, state multistep.StateBag) mult sshKeyName := fmt.Sprintf("/Compute-%s/%s/packer_generated_key_%s", config.IdentityDomain, config.Username, uuid_string) + ui.Say(fmt.Sprintf("Creating temporary key: %s", sshKeyName)) + sshKeysClient := client.SSHKeys() sshKeysInput := compute.CreateSSHKeyInput{ Name: sshKeyName, diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 2f8493fd1..fd70c6afa 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -16,11 +16,11 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu // get variables from state ui := state.Get("ui").(packer.Ui) ui.Say("Creating Instance...") + config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) keyName := state.Get("key_name").(string) - - ipAddName := fmt.Sprintf("ipres_%s", config.ImageName) + ipAddName := state.Get("ipres_name").(string) secListName := state.Get("security_list").(string) netInfo := compute.NetworkingInfo{ diff --git a/builder/oracle/classic/step_create_ip_reservation.go b/builder/oracle/classic/step_create_ip_reservation.go index 84154631b..8e23be785 100644 --- a/builder/oracle/classic/step_create_ip_reservation.go +++ b/builder/oracle/classic/step_create_ip_reservation.go @@ -13,15 +13,19 @@ type stepCreateIPReservation struct{} func (s *stepCreateIPReservation) Run(_ context.Context, state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) - ui.Say("Creating IP reservation...") + config := state.Get("config").(*Config) client := state.Get("client").(*compute.ComputeClient) iprClient := client.IPReservations() // TODO: add optional Name and Tags + + ipresName := fmt.Sprintf("ipres_%s", config.ImageName) + ui.Say(fmt.Sprintf("Creating IP reservation: %s", ipresName)) + IPInput := &compute.CreateIPReservationInput{ ParentPool: compute.PublicReservationPool, Permanent: true, - Name: fmt.Sprintf("ipres_%s", config.ImageName), + Name: ipresName, } ipRes, err := iprClient.CreateIPReservation(IPInput) @@ -32,6 +36,7 @@ func (s *stepCreateIPReservation) Run(_ context.Context, state multistep.StateBa return multistep.ActionHalt } state.Put("instance_ip", ipRes.IP) + state.Put("ipres_name", ipresName) return multistep.ActionContinue } From c6b43ce6e9f645258f6898e0d4bf1646882865a0 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 26 Jan 2018 13:13:13 -0800 Subject: [PATCH 47/61] remove errouneous double prep --- builder/oracle/classic/config.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 178bb2d4b..540f24503 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -76,9 +76,5 @@ func NewConfig(raws ...interface{}) (*Config, error) { errs = packer.MultiErrorAppend(errs, es...) } - if es := c.Comm.Prepare(&c.ctx); len(es) > 0 { - errs = packer.MultiErrorAppend(errs, es...) - } - return c, nil } From 9edd98f7b0fda9d791e9944c6a06dd0a896f70e7 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Fri, 26 Jan 2018 13:43:19 -0800 Subject: [PATCH 48/61] Use more uuids and make messaging consistent. --- builder/oracle/classic/step_add_keys.go | 9 ++------- builder/oracle/classic/step_create_instance.go | 4 ++-- builder/oracle/classic/step_create_ip_reservation.go | 5 +++-- builder/oracle/classic/step_security.go | 2 +- builder/oracle/classic/step_snapshot.go | 2 +- 5 files changed, 9 insertions(+), 13 deletions(-) diff --git a/builder/oracle/classic/step_add_keys.go b/builder/oracle/classic/step_add_keys.go index b8331eb94..544c35bf2 100644 --- a/builder/oracle/classic/step_add_keys.go +++ b/builder/oracle/classic/step_add_keys.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/hashicorp/go-oracle-terraform/compute" - uuid "github.com/hashicorp/go-uuid" + "github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -23,13 +23,8 @@ func (s *stepAddKeysToAPI) Run(_ context.Context, state multistep.StateBag) mult sshPublicKey := strings.TrimSpace(state.Get("publicKey").(string)) // form API call to add key to compute cloud - uuid_string, err := uuid.GenerateUUID() - if err != nil { - ui.Error(fmt.Sprintf("Error creating unique SSH key name: %s", err.Error())) - return multistep.ActionHalt - } sshKeyName := fmt.Sprintf("/Compute-%s/%s/packer_generated_key_%s", - config.IdentityDomain, config.Username, uuid_string) + config.IdentityDomain, config.Username, uuid.TimeOrderedUUID()) ui.Say(fmt.Sprintf("Creating temporary key: %s", sshKeyName)) diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index fd70c6afa..ea840cb22 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -49,7 +49,7 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu } state.Put("instance_id", instanceInfo.ID) - ui.Say(fmt.Sprintf("Created instance (%s).", instanceInfo.ID)) + ui.Message(fmt.Sprintf("Created instance: %s.", instanceInfo.ID)) return multistep.ActionContinue } @@ -60,7 +60,7 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { config := state.Get("config").(*Config) imID := state.Get("instance_id").(string) - ui.Say(fmt.Sprintf("Terminating instance (%s)...", imID)) + ui.Say("Terminating source instance...") instanceClient := client.Instances() input := &compute.DeleteInstanceInput{ diff --git a/builder/oracle/classic/step_create_ip_reservation.go b/builder/oracle/classic/step_create_ip_reservation.go index 8e23be785..cee4a62e8 100644 --- a/builder/oracle/classic/step_create_ip_reservation.go +++ b/builder/oracle/classic/step_create_ip_reservation.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/helper/multistep" "github.com/hashicorp/packer/packer" ) @@ -19,8 +20,8 @@ func (s *stepCreateIPReservation) Run(_ context.Context, state multistep.StateBa iprClient := client.IPReservations() // TODO: add optional Name and Tags - ipresName := fmt.Sprintf("ipres_%s", config.ImageName) - ui.Say(fmt.Sprintf("Creating IP reservation: %s", ipresName)) + ipresName := fmt.Sprintf("ipres_%s_%s", config.ImageName, uuid.TimeOrderedUUID()) + ui.Say(fmt.Sprintf("Creating temporary IP reservation: %s", ipresName)) IPInput := &compute.CreateIPReservationInput{ ParentPool: compute.PublicReservationPool, diff --git a/builder/oracle/classic/step_security.go b/builder/oracle/classic/step_security.go index dae341ba6..22d74de08 100644 --- a/builder/oracle/classic/step_security.go +++ b/builder/oracle/classic/step_security.go @@ -71,7 +71,7 @@ func (s *stepSecurity) Run(_ context.Context, state multistep.StateBag) multiste func (s *stepSecurity) Cleanup(state multistep.StateBag) { client := state.Get("client").(*compute.ComputeClient) ui := state.Get("ui").(packer.Ui) - ui.Say("Deleting the packer-generated security rules and lists...") + ui.Say("Deleting temporary rules and lists...") // delete security rules that Packer generated secRuleName := state.Get("security_rule_name").(string) diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index 30a7d32c8..602ebd1ad 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -37,7 +37,7 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste } state.Put("snapshot", snap) - ui.Say(fmt.Sprintf("Created snapshot (%s).", snap.Name)) + ui.Message(fmt.Sprintf("Created snapshot: %s.", snap.Name)) return multistep.ActionContinue } From 98857c42cfe1c0ba7484804fdd4af543a3a6a7ec Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 26 Jan 2018 14:27:18 -0800 Subject: [PATCH 49/61] add tests; fix a couple issues caught by said tests --- builder/oracle/classic/config.go | 8 ++++ builder/oracle/classic/config_test.go | 59 +++++++++++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 builder/oracle/classic/config_test.go diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 540f24503..695307338 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -56,6 +56,10 @@ func NewConfig(raws ...interface{}) (*Config, error) { c.SSHSourceList = "seciplist:/oracle/public/public-internet" } + if c.Comm.SSHUsername == "" { + c.Comm.SSHUsername = "opc" + } + // Validate that all required fields are present var errs *packer.MultiError required := map[string]string{ @@ -76,5 +80,9 @@ func NewConfig(raws ...interface{}) (*Config, error) { errs = packer.MultiErrorAppend(errs, es...) } + if errs != nil && len(errs.Errors) > 0 { + return nil, errs + } + return c, nil } diff --git a/builder/oracle/classic/config_test.go b/builder/oracle/classic/config_test.go new file mode 100644 index 000000000..195e104a8 --- /dev/null +++ b/builder/oracle/classic/config_test.go @@ -0,0 +1,59 @@ +package classic + +import ( + "testing" +) + +func testConfig() map[string]interface{} { + return map[string]interface{}{ + "identity_domain": "abc12345", + "username": "test@hashicorp.com", + "password": "testpassword123", + "api_endpoint": "https://api-test.compute.test.oraclecloud.com/", + "image_list": "/oracle/public/myimage", + "shape": "oc3", + "image_name": "TestImageName", + "ssh_username": "opc", + } +} + +func TestConfigAutoFillsSourceList(t *testing.T) { + tc := testConfig() + conf, err := NewConfig(tc) + if err != nil { + t.Fatalf("Should not have error: %s", err.Error()) + } + if conf.SSHSourceList != "seciplist:/oracle/public/public-internet" { + t.Fatalf("conf.SSHSourceList should have been "+ + "\"seciplist:/oracle/public/public-internet\" but is \"%s\"", + conf.SSHSourceList) + } +} + +func TestConfigValidationCatchesMissing(t *testing.T) { + required := []string{ + "username", + "password", + "api_endpoint", + "identity_domain", + "image_list", + "shape", + } + for _, key := range required { + tc := testConfig() + delete(tc, key) + _, err := NewConfig(tc) + if err == nil { + t.Fatalf("Test should have failed when config lacked %s!", key) + } + } +} + +func TestValidationsIgnoresOptional(t *testing.T) { + tc := testConfig() + delete(tc, "ssh_username") + _, err := NewConfig(tc) + if err != nil { + t.Fatalf("Test shouldn't care if ssh_username is missing: err: %#v", err.Error()) + } +} From 565b660b19d03c61b2dcbad244cbf323f27b8855 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 26 Jan 2018 14:28:27 -0800 Subject: [PATCH 50/61] comments --- builder/oracle/classic/config.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 695307338..7b2f7dfb2 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -51,11 +51,11 @@ func NewConfig(raws ...interface{}) (*Config, error) { if err != nil { return nil, fmt.Errorf("Error parsing API Endpoint: %s", err) } - // set default source list + // Set default source list if c.SSHSourceList == "" { c.SSHSourceList = "seciplist:/oracle/public/public-internet" } - + // Use default oracle username with sudo privileges if c.Comm.SSHUsername == "" { c.Comm.SSHUsername = "opc" } From 3ee1aa3ed62ce8082a5e2b92aa7258ab5d21ef35 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 26 Jan 2018 15:18:33 -0800 Subject: [PATCH 51/61] clean up ip reservations --- builder/oracle/classic/config_test.go | 2 +- .../oracle/classic/step_create_ip_reservation.go | 13 ++++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/builder/oracle/classic/config_test.go b/builder/oracle/classic/config_test.go index 195e104a8..81b8f0344 100644 --- a/builder/oracle/classic/config_test.go +++ b/builder/oracle/classic/config_test.go @@ -54,6 +54,6 @@ func TestValidationsIgnoresOptional(t *testing.T) { delete(tc, "ssh_username") _, err := NewConfig(tc) if err != nil { - t.Fatalf("Test shouldn't care if ssh_username is missing: err: %#v", err.Error()) + t.Fatalf("Shouldn't care if ssh_username is missing: err: %#v", err.Error()) } } diff --git a/builder/oracle/classic/step_create_ip_reservation.go b/builder/oracle/classic/step_create_ip_reservation.go index cee4a62e8..ca324b7e0 100644 --- a/builder/oracle/classic/step_create_ip_reservation.go +++ b/builder/oracle/classic/step_create_ip_reservation.go @@ -42,5 +42,16 @@ func (s *stepCreateIPReservation) Run(_ context.Context, state multistep.StateBa } func (s *stepCreateIPReservation) Cleanup(state multistep.StateBag) { - // TODO: delete ip reservation + ui := state.Get("ui").(packer.Ui) + ui.Message("Cleaning up IP reservations...") + client := state.Get("client").(*compute.ComputeClient) + + ipResName := state.Get("ipres_name").(string) + input := compute.DeleteIPReservationInput{Name: ipResName} + ipClient := client.IPReservations() + err := ipClient.DeleteIPReservation(&input) + if err != nil { + fmt.Printf("error deleting IP reservation: %s", err.Error()) + } + } From 56c6fed42a3b1ae5683a1d0c2e113ba9d1c4f037 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Fri, 26 Jan 2018 15:20:12 -0800 Subject: [PATCH 52/61] ui.say vs ui.message --- builder/oracle/classic/step_create_ip_reservation.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/oracle/classic/step_create_ip_reservation.go b/builder/oracle/classic/step_create_ip_reservation.go index ca324b7e0..74466a39f 100644 --- a/builder/oracle/classic/step_create_ip_reservation.go +++ b/builder/oracle/classic/step_create_ip_reservation.go @@ -21,7 +21,7 @@ func (s *stepCreateIPReservation) Run(_ context.Context, state multistep.StateBa // TODO: add optional Name and Tags ipresName := fmt.Sprintf("ipres_%s_%s", config.ImageName, uuid.TimeOrderedUUID()) - ui.Say(fmt.Sprintf("Creating temporary IP reservation: %s", ipresName)) + ui.Message(fmt.Sprintf("Creating temporary IP reservation: %s", ipresName)) IPInput := &compute.CreateIPReservationInput{ ParentPool: compute.PublicReservationPool, @@ -43,7 +43,7 @@ func (s *stepCreateIPReservation) Run(_ context.Context, state multistep.StateBa func (s *stepCreateIPReservation) Cleanup(state multistep.StateBag) { ui := state.Get("ui").(packer.Ui) - ui.Message("Cleaning up IP reservations...") + ui.Say("Cleaning up IP reservations...") client := state.Get("client").(*compute.ComputeClient) ipResName := state.Get("ipres_name").(string) From 705459c26060e98691db345a83ec9dc5e7f03e4f Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Mon, 29 Jan 2018 16:50:53 -0800 Subject: [PATCH 53/61] add snapshotted machine image to image lists, then delete the snapshot. --- builder/oracle/classic/builder.go | 1 + builder/oracle/classic/config.go | 22 +++--- .../oracle/classic/step_create_instance.go | 2 +- builder/oracle/classic/step_list_images.go | 70 +++++++++++++++++++ builder/oracle/classic/step_snapshot.go | 19 ++++- 5 files changed, 103 insertions(+), 11 deletions(-) create mode 100644 builder/oracle/classic/step_list_images.go diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 8537289cf..088ff4dbc 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -80,6 +80,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &common.StepProvision{}, &stepSnapshot{}, + &stepListImages{}, } // Run the steps diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index 7b2f7dfb2..e0c82beb2 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -23,9 +23,13 @@ type Config struct { apiEndpointURL *url.URL // Image - ImageName string `mapstructure:"image_name"` - Shape string `mapstructure:"shape"` - ImageList string `mapstructure:"image_list"` + ImageName string `mapstructure:"image_name"` + Shape string `mapstructure:"shape"` + SourceImageList string `mapstructure:"source_image_list"` + DestImageList string `mapstructure:"dest_image_list"` + // Optional; if you don't enter anything, the image list description + // will read "Packer-built image list" + DestImageListDescription string `mapstructure:"dest_image_list_description` // Optional. Describes what computers are allowed to reach your instance // via SSH. This whitelist must contain the computer you're running Packer // from. It defaults to public-internet, meaning that you can SSH into your @@ -63,12 +67,12 @@ func NewConfig(raws ...interface{}) (*Config, error) { // Validate that all required fields are present var errs *packer.MultiError required := map[string]string{ - "username": c.Username, - "password": c.Password, - "api_endpoint": c.APIEndpoint, - "identity_domain": c.IdentityDomain, - "image_list": c.ImageList, - "shape": c.Shape, + "username": c.Username, + "password": c.Password, + "api_endpoint": c.APIEndpoint, + "identity_domain": c.IdentityDomain, + "source_image_list": c.SourceImageList, + "shape": c.Shape, } for k, v := range required { if v == "" { diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index ea840cb22..891fda33e 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -35,7 +35,7 @@ func (s *stepCreateInstance) Run(_ context.Context, state multistep.StateBag) mu input := &compute.CreateInstanceInput{ Name: config.ImageName, Shape: config.Shape, - ImageList: config.ImageList, + ImageList: config.SourceImageList, SSHKeys: []string{keyName}, Networking: map[string]compute.NetworkingInfo{"eth0": netInfo}, } diff --git a/builder/oracle/classic/step_list_images.go b/builder/oracle/classic/step_list_images.go new file mode 100644 index 000000000..8d0c5ee12 --- /dev/null +++ b/builder/oracle/classic/step_list_images.go @@ -0,0 +1,70 @@ +package classic + +import ( + "context" + "fmt" + + "github.com/hashicorp/go-oracle-terraform/compute" + "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/packer" +) + +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) + client := state.Get("client").(*compute.ComputeClient) + ui.Say("Adding image to image list...") + + // TODO: Try to get image list + imageListClient := client.ImageList() + getInput := compute.GetImageListInput{ + Name: config.DestImageList, + } + imList, err := imageListClient.GetImageList(&getInput) + if err != nil { + ui.Say(fmt.Sprintf(err.Error())) + // If the list didn't exist, create it. + ui.Say(fmt.Sprintf("Creating image list: %s", config.DestImageList)) + + ilInput := compute.CreateImageListInput{ + Name: config.DestImageList, + Description: "Packer-built image list", + } + + // Load the packer-generated SSH key into the Oracle Compute cloud. + imList, err = imageListClient.CreateImageList(&ilInput) + if err != nil { + err = fmt.Errorf("Problem creating an image list through Oracle's API: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + ui.Message(fmt.Sprintf("Image list %s created!", imList.URI)) + } + + // Now create and image list entry for the image into that list. + snap := state.Get("snapshot").(*compute.Snapshot) + entriesClient := client.ImageListEntries() + entriesInput := compute.CreateImageListEntryInput{ + Name: config.DestImageList, + MachineImages: []string{fmt.Sprintf("Compute-%s/%s/%s", config.IdentityDomain, config.Username, snap.MachineImage)}, + Version: 1, + } + entryInfo, err := entriesClient.CreateImageListEntry(&entriesInput) + if err != nil { + err = fmt.Errorf("Problem creating an image list entry: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + ui.Message(fmt.Sprintf("created image list entry %s", entryInfo.Name)) + return multistep.ActionContinue +} + +func (s *stepListImages) Cleanup(state multistep.StateBag) { + // Nothing to do + return +} diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index 602ebd1ad..b98530691 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -42,5 +42,22 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste } func (s *stepSnapshot) Cleanup(state multistep.StateBag) { - // Nothing to do + // Delete the snapshot + ui := state.Get("ui").(packer.Ui) + ui.Say("Creating Snapshot...") + client := state.Get("client").(*compute.ComputeClient) + snap := state.Get("snapshot").(*compute.Snapshot) + snapClient := client.Snapshots() + snapInput := compute.DeleteSnapshotInput{ + Snapshot: snap.Name, + MachineImage: snap.MachineImage, + } + machineClient := client.MachineImages() + err := snapClient.DeleteSnapshot(machineClient, &snapInput) + if err != nil { + err = fmt.Errorf("Problem deleting snapshot: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + } + return } From 871ead371a67446aa0b62d7e8f213cdedd1c927b Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 31 Jan 2018 10:47:19 -0800 Subject: [PATCH 54/61] Clean up based on Oracle comments --- builder/oracle/classic/artifact.go | 20 +++++++------ builder/oracle/classic/builder.go | 6 ++-- builder/oracle/classic/config.go | 3 +- builder/oracle/classic/config_test.go | 20 +++++++------ .../oracle/classic/step_create_instance.go | 2 -- builder/oracle/classic/step_list_images.go | 30 ++++++++++++++++--- builder/oracle/classic/step_snapshot.go | 4 +-- .../go-oracle-terraform/compute/snapshots.go | 25 ++++++++++++++++ vendor/vendor.json | 2 +- .../docs/builders/oracle-classic.html.md | 10 +++++-- 10 files changed, 90 insertions(+), 32 deletions(-) diff --git a/builder/oracle/classic/artifact.go b/builder/oracle/classic/artifact.go index fb7861b04..82987be1a 100644 --- a/builder/oracle/classic/artifact.go +++ b/builder/oracle/classic/artifact.go @@ -6,10 +6,13 @@ import ( "github.com/hashicorp/go-oracle-terraform/compute" ) -// Artifact is an artifact implementation that contains a Snapshot. +// Artifact is an artifact implementation that contains Image List +// and Machine Image info. type Artifact struct { - Snapshot *compute.Snapshot - driver *compute.ComputeClient + MachineImageName string + MachineImageFile string + ImageListVersion int + driver *compute.ComputeClient } // BuilderId uniquely identifies the builder. @@ -24,16 +27,15 @@ func (a *Artifact) Files() []string { } func (a *Artifact) Id() string { - return a.Snapshot.Name + return a.MachineImageName } func (a *Artifact) String() string { - return fmt.Sprintf("A Snapshot was created: \n"+ + return fmt.Sprintf("An image list entry was created: \n"+ "Name: %s\n"+ - "Instance: %s\n"+ - "MachineImage: %s\n"+ - "URI: %s", - a.Snapshot.Name, a.Snapshot.Instance, a.Snapshot.MachineImage, a.Snapshot.URI) + "File: %s\n"+ + "Version: %d", + a.MachineImageName, a.MachineImageFile, a.ImageListVersion) } func (a *Artifact) State(name string) interface{} { diff --git a/builder/oracle/classic/builder.go b/builder/oracle/classic/builder.go index 088ff4dbc..1e90439be 100644 --- a/builder/oracle/classic/builder.go +++ b/builder/oracle/classic/builder.go @@ -99,8 +99,10 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe // Build the artifact and return it artifact := &Artifact{ - Snapshot: state.Get("snapshot").(*compute.Snapshot), - driver: client, + ImageListVersion: state.Get("image_list_version").(int), + MachineImageName: state.Get("machine_image_name").(string), + MachineImageFile: state.Get("machine_image_file").(string), + driver: client, } return artifact, nil diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index e0c82beb2..c04235da9 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -29,7 +29,7 @@ type Config struct { DestImageList string `mapstructure:"dest_image_list"` // Optional; if you don't enter anything, the image list description // will read "Packer-built image list" - DestImageListDescription string `mapstructure:"dest_image_list_description` + DestImageListDescription string `mapstructure:"dest_image_list_description"` // Optional. Describes what computers are allowed to reach your instance // via SSH. This whitelist must contain the computer you're running Packer // from. It defaults to public-internet, meaning that you can SSH into your @@ -72,6 +72,7 @@ func NewConfig(raws ...interface{}) (*Config, error) { "api_endpoint": c.APIEndpoint, "identity_domain": c.IdentityDomain, "source_image_list": c.SourceImageList, + "dest_image_list": c.DestImageList, "shape": c.Shape, } for k, v := range required { diff --git a/builder/oracle/classic/config_test.go b/builder/oracle/classic/config_test.go index 81b8f0344..214365731 100644 --- a/builder/oracle/classic/config_test.go +++ b/builder/oracle/classic/config_test.go @@ -6,14 +6,15 @@ import ( func testConfig() map[string]interface{} { return map[string]interface{}{ - "identity_domain": "abc12345", - "username": "test@hashicorp.com", - "password": "testpassword123", - "api_endpoint": "https://api-test.compute.test.oraclecloud.com/", - "image_list": "/oracle/public/myimage", - "shape": "oc3", - "image_name": "TestImageName", - "ssh_username": "opc", + "identity_domain": "abc12345", + "username": "test@hashicorp.com", + "password": "testpassword123", + "api_endpoint": "https://api-test.compute.test.oraclecloud.com/", + "dest_image_list": "/Config-thing/myuser/myimage", + "source_image_list": "/oracle/public/whatever", + "shape": "oc3", + "image_name": "TestImageName", + "ssh_username": "opc", } } @@ -36,7 +37,8 @@ func TestConfigValidationCatchesMissing(t *testing.T) { "password", "api_endpoint", "identity_domain", - "image_list", + "dest_image_list", + "source_image_list", "shape", } for _, key := range required { diff --git a/builder/oracle/classic/step_create_instance.go b/builder/oracle/classic/step_create_instance.go index 891fda33e..041889ab1 100644 --- a/builder/oracle/classic/step_create_instance.go +++ b/builder/oracle/classic/step_create_instance.go @@ -3,7 +3,6 @@ package classic import ( "context" "fmt" - "log" "github.com/hashicorp/go-oracle-terraform/compute" "github.com/hashicorp/packer/helper/multistep" @@ -67,7 +66,6 @@ func (s *stepCreateInstance) Cleanup(state multistep.StateBag) { Name: config.ImageName, ID: imID, } - log.Printf("instance destroy input is %#v", input) err := instanceClient.DeleteInstance(input) if err != nil { diff --git a/builder/oracle/classic/step_list_images.go b/builder/oracle/classic/step_list_images.go index 8d0c5ee12..a32355e79 100644 --- a/builder/oracle/classic/step_list_images.go +++ b/builder/oracle/classic/step_list_images.go @@ -18,7 +18,6 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis client := state.Get("client").(*compute.ComputeClient) ui.Say("Adding image to image list...") - // TODO: Try to get image list imageListClient := client.ImageList() getInput := compute.GetImageListInput{ Name: config.DestImageList, @@ -27,7 +26,8 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis if err != nil { ui.Say(fmt.Sprintf(err.Error())) // If the list didn't exist, create it. - ui.Say(fmt.Sprintf("Creating image list: %s", config.DestImageList)) + ui.Say(fmt.Sprintf("Destination image list %s does not exist; Creating it...", + config.DestImageList)) ilInput := compute.CreateImageListInput{ Name: config.DestImageList, @@ -37,7 +37,7 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis // Load the packer-generated SSH key into the Oracle Compute cloud. imList, err = imageListClient.CreateImageList(&ilInput) if err != nil { - err = fmt.Errorf("Problem creating an image list through Oracle's API: %s", err) + err = fmt.Errorf("Problem creating image list: %s", err) ui.Error(err.Error()) state.Put("error", err) return multistep.ActionHalt @@ -47,11 +47,12 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis // Now create and image list entry for the image into that list. snap := state.Get("snapshot").(*compute.Snapshot) + version := len(imList.Entries) + 1 entriesClient := client.ImageListEntries() entriesInput := compute.CreateImageListEntryInput{ Name: config.DestImageList, MachineImages: []string{fmt.Sprintf("Compute-%s/%s/%s", config.IdentityDomain, config.Username, snap.MachineImage)}, - Version: 1, + Version: version, } entryInfo, err := entriesClient.CreateImageListEntry(&entriesInput) if err != nil { @@ -60,7 +61,28 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis state.Put("error", err) return multistep.ActionHalt } + state.Put("image_list_entry", entryInfo) ui.Message(fmt.Sprintf("created image list entry %s", entryInfo.Name)) + + imList, err = imageListClient.GetImageList(&getInput) + + machineImagesClient := client.MachineImages() + getImagesInput := compute.GetMachineImageInput{ + Name: config.ImageName, + } + + // Grab info about the machine image to return with the artifact + imInfo, err := machineImagesClient.GetMachineImage(&getImagesInput) + if err != nil { + err = fmt.Errorf("Problem getting machine image info: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + state.Put("machine_image_file", imInfo.File) + state.Put("machine_image_name", imInfo.Name) + state.Put("image_list_version", version) + return multistep.ActionContinue } diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index b98530691..452fb56eb 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -52,8 +52,8 @@ func (s *stepSnapshot) Cleanup(state multistep.StateBag) { Snapshot: snap.Name, MachineImage: snap.MachineImage, } - machineClient := client.MachineImages() - err := snapClient.DeleteSnapshot(machineClient, &snapInput) + + err := snapClient.DeleteSnapshotResourceOnly(&snapInput) if err != nil { err = fmt.Errorf("Problem deleting snapshot: %s", err) ui.Error(err.Error()) diff --git a/vendor/github.com/hashicorp/go-oracle-terraform/compute/snapshots.go b/vendor/github.com/hashicorp/go-oracle-terraform/compute/snapshots.go index d2a9616e9..370fd0c97 100644 --- a/vendor/github.com/hashicorp/go-oracle-terraform/compute/snapshots.go +++ b/vendor/github.com/hashicorp/go-oracle-terraform/compute/snapshots.go @@ -176,6 +176,31 @@ func (c *SnapshotsClient) DeleteSnapshot(machineImagesClient *MachineImagesClien return nil } +// DeleteSnapshot deletes the Snapshot with the given name. +// A machine image gets created with the associated snapshot is not deleted +// by this method. +func (c *SnapshotsClient) DeleteSnapshotResourceOnly(input *DeleteSnapshotInput) error { + // Wait for snapshot complete in case delay is active and the corresponding + // instance needs to be deleted first + getInput := &GetSnapshotInput{ + Name: input.Snapshot, + } + + if input.Timeout == 0 { + input.Timeout = WaitForSnapshotCompleteTimeout + } + + if _, err := c.WaitForSnapshotComplete(getInput, input.Timeout); err != nil { + return fmt.Errorf("Could not delete snapshot: %s", err) + } + + if err := c.deleteResource(input.Snapshot); err != nil { + return fmt.Errorf("Could not delete snapshot: %s", err) + } + + return nil +} + // WaitForSnapshotComplete waits for an snapshot to be completely initialized and available. func (c *SnapshotsClient) WaitForSnapshotComplete(input *GetSnapshotInput, timeout time.Duration) (*Snapshot, error) { var info *Snapshot diff --git a/vendor/vendor.json b/vendor/vendor.json index 6172b8c33..421c8710a 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -803,7 +803,7 @@ "revisionTime": "2018-01-11T20:31:13Z" }, { - "checksumSHA1": "RhoE7zmHsn5zoXbx5AsAUUQI72E=", + "checksumSHA1": "wce86V0j11J6xRSvJEanprjK7so=", "path": "github.com/hashicorp/go-oracle-terraform/compute", "revision": "5a9a298c54339d2296d2f1135eae55a3a8f5e8c2", "revisionTime": "2018-01-11T20:31:13Z" diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md index 08d3d10b1..83169fcee 100644 --- a/website/source/docs/builders/oracle-classic.html.md +++ b/website/source/docs/builders/oracle-classic.html.md @@ -40,14 +40,17 @@ This builder currently only works with the SSH communicator. requests. Instructions for determining your API endpoint can be found [here](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsa/SendRequests.html) + - `dest_image_list` (string) - Where to save the machine image to once you've + provisioned it. If the provided image list does not exist, Packer will create it. + - `identity_domain` (string) - This is your customer-specific identity domain as generated by Oracle. If you don't know what your identity domain is, ask your account administrator. For a little more information, see the Oracle [documentation](https://docs.oracle.com/en/cloud/get-started/subscriptions-cloud/ocuid/identity-domain-overview.html#GUID-7969F881-5F4D-443E-B86C-9044C8085B8A). - - `image_list` (string) - This is what image you want to use as your base image. + - `source_image_list` (string) - This is what image you want to use as your base image. See the [documentation](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsg/listing-machine-images.html) - for more details. To see what public image lists are available, you can use + for more details. You may use either a public image list, or a private image list. To see what public image lists are available, you can use the CLI, as described [here](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stopc/image-lists-stclr-and-nmcli.html#GUID-DB7E75FE-F752-4FF7-AB70-3C8DCDFCA0FA) - `password` (string) - Your account password. @@ -60,6 +63,9 @@ This builder currently only works with the SSH communicator. ### Optional + - `dest_image_list_description` (string) - a description for your destination + image list. If you don't provide one, Packer will provide a generic description. + - `ssh_username` (string) - The username that Packer will use to SSH into the instance; defaults to `opc`, the default oracle user, which has sudo priveliges. If you have already configured users on your machine, you may From af26b312cdbcf5b3b0b0ed2fa0d3f34f5a5bd4eb Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 31 Jan 2018 11:35:34 -0800 Subject: [PATCH 55/61] fix logline --- builder/oracle/classic/step_snapshot.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builder/oracle/classic/step_snapshot.go b/builder/oracle/classic/step_snapshot.go index 452fb56eb..b7cbbf008 100644 --- a/builder/oracle/classic/step_snapshot.go +++ b/builder/oracle/classic/step_snapshot.go @@ -44,7 +44,7 @@ func (s *stepSnapshot) Run(_ context.Context, state multistep.StateBag) multiste func (s *stepSnapshot) Cleanup(state multistep.StateBag) { // Delete the snapshot ui := state.Get("ui").(packer.Ui) - ui.Say("Creating Snapshot...") + ui.Say("Deleting Snapshot...") client := state.Get("client").(*compute.ComputeClient) snap := state.Get("snapshot").(*compute.Snapshot) snapClient := client.Snapshots() From 64e26d6fa253cb838ce69cbdd55cd6a290893206 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 31 Jan 2018 12:23:25 -0800 Subject: [PATCH 56/61] update example file in the oracle-classic docs --- website/source/docs/builders/oracle-classic.html.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md index 83169fcee..ac539b255 100644 --- a/website/source/docs/builders/oracle-classic.html.md +++ b/website/source/docs/builders/oracle-classic.html.md @@ -89,9 +89,10 @@ obfuscated; you will need to add a working `username`, `password`, "password": "supersecretpasswordhere", "identity_domain": "#######", "api_endpoint": "https://api-###.compute.###.oraclecloud.com/", - "image_list": "/oracle/public/OL_7.2_UEKR4_x86_64", + "source_image_list": "/oracle/public/OL_7.2_UEKR4_x86_64", "shape": "oc3", "image_name": "Packer_Builder_Test_{{timestamp}}" + "dest_image_list": "Packer_Builder_Test_List" } ], "provisioners": [ From 66cd85828e9e56925f95fb35266e17c089cc99f7 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 31 Jan 2018 12:48:40 -0800 Subject: [PATCH 57/61] rename dest_image_list_description to image_description --- builder/oracle/classic/config.go | 2 +- website/source/docs/builders/oracle-classic.html.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/builder/oracle/classic/config.go b/builder/oracle/classic/config.go index c04235da9..4cd7c42a7 100644 --- a/builder/oracle/classic/config.go +++ b/builder/oracle/classic/config.go @@ -29,7 +29,7 @@ type Config struct { DestImageList string `mapstructure:"dest_image_list"` // Optional; if you don't enter anything, the image list description // will read "Packer-built image list" - DestImageListDescription string `mapstructure:"dest_image_list_description"` + DestImageListDescription string `mapstructure:"image_description"` // Optional. Describes what computers are allowed to reach your instance // via SSH. This whitelist must contain the computer you're running Packer // from. It defaults to public-internet, meaning that you can SSH into your diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md index ac539b255..e71cc9cc3 100644 --- a/website/source/docs/builders/oracle-classic.html.md +++ b/website/source/docs/builders/oracle-classic.html.md @@ -63,7 +63,7 @@ This builder currently only works with the SSH communicator. ### Optional - - `dest_image_list_description` (string) - a description for your destination + - `image_description` (string) - a description for your destination image list. If you don't provide one, Packer will provide a generic description. - `ssh_username` (string) - The username that Packer will use to SSH into the From 3180dc327c09f180ad9d6119ff504f372e028888 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 31 Jan 2018 15:02:19 -0800 Subject: [PATCH 58/61] remove copypasta comment --- builder/oracle/classic/step_list_images.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/builder/oracle/classic/step_list_images.go b/builder/oracle/classic/step_list_images.go index a32355e79..64255b428 100644 --- a/builder/oracle/classic/step_list_images.go +++ b/builder/oracle/classic/step_list_images.go @@ -24,8 +24,8 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis } imList, err := imageListClient.GetImageList(&getInput) if err != nil { - ui.Say(fmt.Sprintf(err.Error())) // If the list didn't exist, create it. + ui.Say(fmt.Sprintf(err.Error())) ui.Say(fmt.Sprintf("Destination image list %s does not exist; Creating it...", config.DestImageList)) @@ -34,7 +34,6 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis Description: "Packer-built image list", } - // Load the packer-generated SSH key into the Oracle Compute cloud. imList, err = imageListClient.CreateImageList(&ilInput) if err != nil { err = fmt.Errorf("Problem creating image list: %s", err) From 8f7937f492de127ed72237de6b7a8df8d49c0301 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 31 Jan 2018 15:22:09 -0800 Subject: [PATCH 59/61] fix machine image name to include prepended / --- builder/oracle/classic/step_list_images.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/builder/oracle/classic/step_list_images.go b/builder/oracle/classic/step_list_images.go index 64255b428..4125733b0 100644 --- a/builder/oracle/classic/step_list_images.go +++ b/builder/oracle/classic/step_list_images.go @@ -49,9 +49,10 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis version := len(imList.Entries) + 1 entriesClient := client.ImageListEntries() entriesInput := compute.CreateImageListEntryInput{ - Name: config.DestImageList, - MachineImages: []string{fmt.Sprintf("Compute-%s/%s/%s", config.IdentityDomain, config.Username, snap.MachineImage)}, - Version: version, + Name: config.DestImageList, + MachineImages: []string{fmt.Sprintf("/Compute-%s/%s/%s", + config.IdentityDomain, config.Username, snap.MachineImage)}, + Version: version, } entryInfo, err := entriesClient.CreateImageListEntry(&entriesInput) if err != nil { From 383ac13e2a5459d9d2d4f6d7d0101057f4c85669 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Wed, 31 Jan 2018 16:37:55 -0800 Subject: [PATCH 60/61] update default of image list after adding new entry. --- builder/oracle/classic/step_list_images.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/builder/oracle/classic/step_list_images.go b/builder/oracle/classic/step_list_images.go index 4125733b0..a99612c14 100644 --- a/builder/oracle/classic/step_list_images.go +++ b/builder/oracle/classic/step_list_images.go @@ -64,13 +64,25 @@ func (s *stepListImages) Run(_ context.Context, state multistep.StateBag) multis state.Put("image_list_entry", entryInfo) ui.Message(fmt.Sprintf("created image list entry %s", entryInfo.Name)) - imList, err = imageListClient.GetImageList(&getInput) - machineImagesClient := client.MachineImages() getImagesInput := compute.GetMachineImageInput{ Name: config.ImageName, } + // Update image list default to use latest version + updateInput := compute.UpdateImageListInput{ + Default: version, + Description: config.DestImageListDescription, + Name: config.DestImageList, + } + _, err = imageListClient.UpdateImageList(&updateInput) + if err != nil { + err = fmt.Errorf("Problem updating default image list version: %s", err) + ui.Error(err.Error()) + state.Put("error", err) + return multistep.ActionHalt + } + // Grab info about the machine image to return with the artifact imInfo, err := machineImagesClient.GetMachineImage(&getImagesInput) if err != nil { From 30a4998a810fb4d2be485dde0b782e2e2129c2a6 Mon Sep 17 00:00:00 2001 From: Megan Marsh Date: Thu, 1 Feb 2018 16:35:10 -0800 Subject: [PATCH 61/61] branding --- website/source/docs/builders/oracle-classic.html.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/website/source/docs/builders/oracle-classic.html.md b/website/source/docs/builders/oracle-classic.html.md index e71cc9cc3..0d9dc05b7 100644 --- a/website/source/docs/builders/oracle-classic.html.md +++ b/website/source/docs/builders/oracle-classic.html.md @@ -3,16 +3,16 @@ description: The oracle-classic builder is able to create new custom images for use with Oracle Compute Cloud. layout: docs -page_title: 'Oracle Classic - Builders' +page_title: 'Oracle Cloud Infrastructure Classic - Builders' sidebar_current: 'docs-builders-oracle-classic' --- -# Oracle Compute Cloud (Classic) Builder +# Oracle Cloud Infrastructure Classic Compute Builder Type: `oracle-classic` The `oracle-classic` Packer builder is able to create custom images for use -with [Oracle Compute Cloud](https://cloud.oracle.com/compute-classic). The builder +with [Oracle Cloud Infrastructure Classic Compute](https://cloud.oracle.com/compute-classic). The builder takes a base image, runs any provisioning necessary on the base image after launching it, and finally snapshots it creating a reusable custom image. @@ -25,7 +25,7 @@ to use it or delete it. ## Authorization -This builder authenticates API calls to Compute Classic using basic +This builder authenticates API calls to Oracle Cloud Infrastructure Classic Compute using basic authentication (user name and password). To read more, see the [authentication documentation](https://docs.oracle.com/en/cloud/iaas/compute-iaas-cloud/stcsa/Authentication.html)