parent
2dbfef5750
commit
6fa213235f
|
@ -28,6 +28,7 @@ import (
|
|||
googlecomputeexportpostprocessor "github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-export"
|
||||
googlecomputeimportpostprocessor "github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-import"
|
||||
ncloudbuilder "github.com/hashicorp/packer-plugin-ncloud/builder/ncloud"
|
||||
openstackbuilder "github.com/hashicorp/packer-plugin-openstack/builder/openstack"
|
||||
oscbsubuilder "github.com/hashicorp/packer-plugin-outscale/builder/osc/bsu"
|
||||
oscbsusurrogatebuilder "github.com/hashicorp/packer-plugin-outscale/builder/osc/bsusurrogate"
|
||||
oscbsuvolumebuilder "github.com/hashicorp/packer-plugin-outscale/builder/osc/bsuvolume"
|
||||
|
@ -68,6 +69,7 @@ var VendoredBuilders = map[string]packersdk.Builder{
|
|||
"docker": new(dockerbuilder.Builder),
|
||||
"googlecompute": new(googlecomputebuilder.Builder),
|
||||
"ncloud": new(ncloudbuilder.Builder),
|
||||
"openstack": new(openstackbuilder.Builder),
|
||||
"proxmox": new(proxmoxiso.Builder),
|
||||
"proxmox-iso": new(proxmoxiso.Builder),
|
||||
"proxmox-clone": new(proxmoxclone.Builder),
|
||||
|
|
1
go.mod
1
go.mod
|
@ -45,6 +45,7 @@ require (
|
|||
github.com/hashicorp/packer-plugin-docker v0.0.7
|
||||
github.com/hashicorp/packer-plugin-googlecompute v0.0.1
|
||||
github.com/hashicorp/packer-plugin-ncloud v0.0.2
|
||||
github.com/hashicorp/packer-plugin-openstack v0.0.1
|
||||
github.com/hashicorp/packer-plugin-outscale v0.0.1
|
||||
github.com/hashicorp/packer-plugin-parallels v0.0.1
|
||||
github.com/hashicorp/packer-plugin-proxmox v0.0.2
|
||||
|
|
5
go.sum
5
go.sum
|
@ -474,6 +474,8 @@ github.com/hashicorp/packer-plugin-googlecompute v0.0.1 h1:Shjio88MraB+ocj0VI5+M
|
|||
github.com/hashicorp/packer-plugin-googlecompute v0.0.1/go.mod h1:MfV898IrEMpKH6wVnvOI5Tkhxm2snf3QxwVqV4k3bNI=
|
||||
github.com/hashicorp/packer-plugin-ncloud v0.0.2 h1:MGvGkOVfzeosqOSs5dteghLwv9VRcRxTuLoLX1ssUag=
|
||||
github.com/hashicorp/packer-plugin-ncloud v0.0.2/go.mod h1:Hud2R1pkky96TQy3TPTTrr9Kej4b/4dqC/v+uEE0VDY=
|
||||
github.com/hashicorp/packer-plugin-openstack v0.0.1 h1:FUaNjKguAipPZZXQ4UiJK6c5+2nS89CRxJHjAsfVyIQ=
|
||||
github.com/hashicorp/packer-plugin-openstack v0.0.1/go.mod h1:L1OTbN24H+izce3v5IyQLdjDdrUigxPWgAQOK920h9A=
|
||||
github.com/hashicorp/packer-plugin-outscale v0.0.1 h1:BrL8hKypNYrvP3NR+d+xX03SZKB08yTgXPRnH9piUI8=
|
||||
github.com/hashicorp/packer-plugin-outscale v0.0.1/go.mod h1:6jEWfJO7TgAbaL3e+St1bN5PoIC/MmDIsYqNUzAHF1w=
|
||||
github.com/hashicorp/packer-plugin-parallels v0.0.1 h1:fcaaiGWdU1+X4IGadXdUhJ2si1ZA3apXS9tMNJXln2A=
|
||||
|
@ -1192,8 +1194,9 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -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.
|
334
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/access_config.go
generated
vendored
Normal file
334
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/access_config.go
generated
vendored
Normal file
|
@ -0,0 +1,334 @@
|
|||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack"
|
||||
"github.com/gophercloud/utils/openstack/clientconfig"
|
||||
"github.com/hashicorp/go-cleanhttp"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
)
|
||||
|
||||
// AccessConfig is for common configuration related to openstack access
|
||||
type AccessConfig struct {
|
||||
// The username or id used to connect to the OpenStack service. If not
|
||||
// specified, Packer will use the environment variable OS_USERNAME or
|
||||
// OS_USERID, if set. This is not required if using access token or
|
||||
// application credential instead of password, or if using cloud.yaml.
|
||||
Username string `mapstructure:"username" required:"true"`
|
||||
// Sets username
|
||||
UserID string `mapstructure:"user_id"`
|
||||
// The password used to connect to the OpenStack service. If not specified,
|
||||
// Packer will use the environment variables OS_PASSWORD, if set. This is
|
||||
// not required if using access token or application credential instead of
|
||||
// password, or if using cloud.yaml.
|
||||
Password string `mapstructure:"password" required:"true"`
|
||||
// The URL to the OpenStack Identity service. If not specified, Packer will
|
||||
// use the environment variables OS_AUTH_URL, if set. This is not required
|
||||
// if using cloud.yaml.
|
||||
IdentityEndpoint string `mapstructure:"identity_endpoint" required:"true"`
|
||||
// The tenant ID or name to boot the instance into. Some OpenStack
|
||||
// installations require this. If not specified, Packer will use the
|
||||
// environment variable OS_TENANT_NAME or OS_TENANT_ID, if set. Tenant is
|
||||
// also called Project in later versions of OpenStack.
|
||||
TenantID string `mapstructure:"tenant_id" required:"false"`
|
||||
TenantName string `mapstructure:"tenant_name"`
|
||||
DomainID string `mapstructure:"domain_id"`
|
||||
// The Domain name or ID you are authenticating with. OpenStack
|
||||
// installations require this if identity v3 is used. Packer will use the
|
||||
// environment variable OS_DOMAIN_NAME or OS_DOMAIN_ID, if set.
|
||||
DomainName string `mapstructure:"domain_name" required:"false"`
|
||||
// Whether or not the connection to OpenStack can be done over an insecure
|
||||
// connection. By default this is false.
|
||||
Insecure bool `mapstructure:"insecure" required:"false"`
|
||||
// The name of the region, such as "DFW", in which to launch the server to
|
||||
// create the image. If not specified, Packer will use the environment
|
||||
// variable OS_REGION_NAME, if set.
|
||||
Region string `mapstructure:"region" required:"false"`
|
||||
// The endpoint type to use. Can be any of "internal", "internalURL",
|
||||
// "admin", "adminURL", "public", and "publicURL". By default this is
|
||||
// "public".
|
||||
EndpointType string `mapstructure:"endpoint_type" required:"false"`
|
||||
// Custom CA certificate file path. If omitted the OS_CACERT environment
|
||||
// variable can be used.
|
||||
CACertFile string `mapstructure:"cacert" required:"false"`
|
||||
// Client certificate file path for SSL client authentication. If omitted
|
||||
// the OS_CERT environment variable can be used.
|
||||
ClientCertFile string `mapstructure:"cert" required:"false"`
|
||||
// Client private key file path for SSL client authentication. If omitted
|
||||
// the OS_KEY environment variable can be used.
|
||||
ClientKeyFile string `mapstructure:"key" required:"false"`
|
||||
// the token (id) to use with token based authorization. Packer will use
|
||||
// the environment variable OS_TOKEN, if set.
|
||||
Token string `mapstructure:"token" required:"false"`
|
||||
// The application credential name to use with application credential based
|
||||
// authorization. Packer will use the environment variable
|
||||
// OS_APPLICATION_CREDENTIAL_NAME, if set.
|
||||
ApplicationCredentialName string `mapstructure:"application_credential_name" required:"false"`
|
||||
// The application credential id to use with application credential based
|
||||
// authorization. Packer will use the environment variable
|
||||
// OS_APPLICATION_CREDENTIAL_ID, if set.
|
||||
ApplicationCredentialID string `mapstructure:"application_credential_id" required:"false"`
|
||||
// The application credential secret to use with application credential
|
||||
// based authorization. Packer will use the environment variable
|
||||
// OS_APPLICATION_CREDENTIAL_SECRET, if set.
|
||||
ApplicationCredentialSecret string `mapstructure:"application_credential_secret" required:"false"`
|
||||
// An entry in a `clouds.yaml` file. See the OpenStack os-client-config
|
||||
// [documentation](https://docs.openstack.org/os-client-config/latest/user/configuration.html)
|
||||
// for more information about `clouds.yaml` files. If omitted, the
|
||||
// `OS_CLOUD` environment variable is used.
|
||||
Cloud string `mapstructure:"cloud" required:"false"`
|
||||
|
||||
osClient *gophercloud.ProviderClient
|
||||
}
|
||||
|
||||
func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
if c.EndpointType != "internal" && c.EndpointType != "internalURL" &&
|
||||
c.EndpointType != "admin" && c.EndpointType != "adminURL" &&
|
||||
c.EndpointType != "public" && c.EndpointType != "publicURL" &&
|
||||
c.EndpointType != "" {
|
||||
return []error{fmt.Errorf("Invalid endpoint type provided")}
|
||||
}
|
||||
|
||||
// Legacy RackSpace stuff. We're keeping this around to keep things BC.
|
||||
if c.Password == "" {
|
||||
c.Password = os.Getenv("SDK_PASSWORD")
|
||||
}
|
||||
if c.Region == "" {
|
||||
c.Region = os.Getenv("SDK_REGION")
|
||||
}
|
||||
if c.TenantName == "" {
|
||||
c.TenantName = os.Getenv("SDK_PROJECT")
|
||||
}
|
||||
if c.Username == "" {
|
||||
c.Username = os.Getenv("SDK_USERNAME")
|
||||
}
|
||||
// End RackSpace
|
||||
|
||||
if c.Cloud == "" {
|
||||
c.Cloud = os.Getenv("OS_CLOUD")
|
||||
}
|
||||
if c.Region == "" {
|
||||
c.Region = os.Getenv("OS_REGION_NAME")
|
||||
}
|
||||
|
||||
if c.CACertFile == "" {
|
||||
c.CACertFile = os.Getenv("OS_CACERT")
|
||||
}
|
||||
if c.ClientCertFile == "" {
|
||||
c.ClientCertFile = os.Getenv("OS_CERT")
|
||||
}
|
||||
if c.ClientKeyFile == "" {
|
||||
c.ClientKeyFile = os.Getenv("OS_KEY")
|
||||
}
|
||||
|
||||
clientOpts := new(clientconfig.ClientOpts)
|
||||
|
||||
// If a cloud entry was given, base AuthOptions on a clouds.yaml file.
|
||||
if c.Cloud != "" {
|
||||
clientOpts.Cloud = c.Cloud
|
||||
|
||||
cloud, err := clientconfig.GetCloudFromYAML(clientOpts)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
if c.Region == "" && cloud.RegionName != "" {
|
||||
c.Region = cloud.RegionName
|
||||
}
|
||||
} else {
|
||||
authInfo := &clientconfig.AuthInfo{
|
||||
AuthURL: c.IdentityEndpoint,
|
||||
DomainID: c.DomainID,
|
||||
DomainName: c.DomainName,
|
||||
Password: c.Password,
|
||||
ProjectID: c.TenantID,
|
||||
ProjectName: c.TenantName,
|
||||
Token: c.Token,
|
||||
Username: c.Username,
|
||||
UserID: c.UserID,
|
||||
}
|
||||
clientOpts.AuthInfo = authInfo
|
||||
}
|
||||
|
||||
ao, err := clientconfig.AuthOptions(clientOpts)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
// Make sure we reauth as needed
|
||||
ao.AllowReauth = true
|
||||
|
||||
// Override values if we have them in our config
|
||||
overrides := []struct {
|
||||
From, To *string
|
||||
}{
|
||||
{&c.Username, &ao.Username},
|
||||
{&c.UserID, &ao.UserID},
|
||||
{&c.Password, &ao.Password},
|
||||
{&c.IdentityEndpoint, &ao.IdentityEndpoint},
|
||||
{&c.TenantID, &ao.TenantID},
|
||||
{&c.TenantName, &ao.TenantName},
|
||||
{&c.DomainID, &ao.DomainID},
|
||||
{&c.DomainName, &ao.DomainName},
|
||||
{&c.Token, &ao.TokenID},
|
||||
{&c.ApplicationCredentialName, &ao.ApplicationCredentialName},
|
||||
{&c.ApplicationCredentialID, &ao.ApplicationCredentialID},
|
||||
{&c.ApplicationCredentialSecret, &ao.ApplicationCredentialSecret},
|
||||
}
|
||||
for _, s := range overrides {
|
||||
if *s.From != "" {
|
||||
*s.To = *s.From
|
||||
}
|
||||
}
|
||||
|
||||
// Build the client itself
|
||||
client, err := openstack.NewClient(ao.IdentityEndpoint)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
tls_config := &tls.Config{}
|
||||
|
||||
if c.CACertFile != "" {
|
||||
caCert, err := ioutil.ReadFile(c.CACertFile)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
caCertPool := x509.NewCertPool()
|
||||
caCertPool.AppendCertsFromPEM(caCert)
|
||||
tls_config.RootCAs = caCertPool
|
||||
}
|
||||
|
||||
// If we have insecure set, then create a custom HTTP client that ignores
|
||||
// SSL errors.
|
||||
if c.Insecure {
|
||||
tls_config.InsecureSkipVerify = true
|
||||
}
|
||||
|
||||
if c.ClientCertFile != "" && c.ClientKeyFile != "" {
|
||||
cert, err := tls.LoadX509KeyPair(c.ClientCertFile, c.ClientKeyFile)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
tls_config.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
|
||||
transport := cleanhttp.DefaultTransport()
|
||||
transport.TLSClientConfig = tls_config
|
||||
client.HTTPClient.Transport = transport
|
||||
|
||||
// Auth
|
||||
err = openstack.Authenticate(client, *ao)
|
||||
if err != nil {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
c.osClient = client
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *AccessConfig) enableDebug(ui packersdk.Ui) {
|
||||
c.osClient.HTTPClient = http.Client{
|
||||
Transport: &DebugRoundTripper{
|
||||
ui: ui,
|
||||
rt: c.osClient.HTTPClient.Transport,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *AccessConfig) computeV2Client() (*gophercloud.ServiceClient, error) {
|
||||
return openstack.NewComputeV2(c.osClient, gophercloud.EndpointOpts{
|
||||
Region: c.Region,
|
||||
Availability: c.getEndpointType(),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *AccessConfig) imageV2Client() (*gophercloud.ServiceClient, error) {
|
||||
return openstack.NewImageServiceV2(c.osClient, gophercloud.EndpointOpts{
|
||||
Region: c.Region,
|
||||
Availability: c.getEndpointType(),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *AccessConfig) blockStorageV3Client() (*gophercloud.ServiceClient, error) {
|
||||
return openstack.NewBlockStorageV3(c.osClient, gophercloud.EndpointOpts{
|
||||
Region: c.Region,
|
||||
Availability: c.getEndpointType(),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *AccessConfig) networkV2Client() (*gophercloud.ServiceClient, error) {
|
||||
return openstack.NewNetworkV2(c.osClient, gophercloud.EndpointOpts{
|
||||
Region: c.Region,
|
||||
Availability: c.getEndpointType(),
|
||||
})
|
||||
}
|
||||
|
||||
func (c *AccessConfig) getEndpointType() gophercloud.Availability {
|
||||
if c.EndpointType == "internal" || c.EndpointType == "internalURL" {
|
||||
return gophercloud.AvailabilityInternal
|
||||
}
|
||||
if c.EndpointType == "admin" || c.EndpointType == "adminURL" {
|
||||
return gophercloud.AvailabilityAdmin
|
||||
}
|
||||
return gophercloud.AvailabilityPublic
|
||||
}
|
||||
|
||||
type DebugRoundTripper struct {
|
||||
ui packersdk.Ui
|
||||
rt http.RoundTripper
|
||||
numReauthAttempts int
|
||||
}
|
||||
|
||||
// RoundTrip performs a round-trip HTTP request and logs relevant information about it.
|
||||
func (drt *DebugRoundTripper) RoundTrip(request *http.Request) (*http.Response, error) {
|
||||
defer func() {
|
||||
if request.Body != nil {
|
||||
request.Body.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
var response *http.Response
|
||||
var err error
|
||||
|
||||
response, err = drt.rt.RoundTrip(request)
|
||||
if response == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode == http.StatusUnauthorized {
|
||||
if drt.numReauthAttempts == 3 {
|
||||
return response, fmt.Errorf("Tried to re-authenticate 3 times with no success.")
|
||||
}
|
||||
drt.numReauthAttempts++
|
||||
}
|
||||
|
||||
drt.DebugMessage(fmt.Sprintf("Request %s %s %d", request.Method, request.URL, response.StatusCode))
|
||||
|
||||
if response.StatusCode >= 400 {
|
||||
buf := bytes.NewBuffer([]byte{})
|
||||
body, _ := ioutil.ReadAll(io.TeeReader(response.Body, buf))
|
||||
drt.DebugMessage(fmt.Sprintf("Response Error: %+v\n", string(body)))
|
||||
bufWithClose := ioutil.NopCloser(buf)
|
||||
response.Body = bufWithClose
|
||||
}
|
||||
|
||||
return response, err
|
||||
}
|
||||
|
||||
func (drt *DebugRoundTripper) DebugMessage(message string) {
|
||||
drt.ui.Message(fmt.Sprintf("[DEBUG] %s", message))
|
||||
}
|
51
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/artifact.go
generated
vendored
Normal file
51
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/artifact.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
)
|
||||
|
||||
// Artifact is an artifact implementation that contains built images.
|
||||
type Artifact struct {
|
||||
// ImageId of built image
|
||||
ImageId string
|
||||
|
||||
// BuilderId is the unique ID for the builder that created this image
|
||||
BuilderIdValue string
|
||||
|
||||
// OpenStack connection for performing API stuff.
|
||||
Client *gophercloud.ServiceClient
|
||||
|
||||
// StateData should store data such as GeneratedData
|
||||
// to be shared with post-processors
|
||||
StateData map[string]interface{}
|
||||
}
|
||||
|
||||
func (a *Artifact) BuilderId() string {
|
||||
return a.BuilderIdValue
|
||||
}
|
||||
|
||||
func (*Artifact) Files() []string {
|
||||
// We have no files
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Artifact) Id() string {
|
||||
return a.ImageId
|
||||
}
|
||||
|
||||
func (a *Artifact) String() string {
|
||||
return fmt.Sprintf("An image was created: %v", a.ImageId)
|
||||
}
|
||||
|
||||
func (a *Artifact) State(name string) interface{} {
|
||||
return a.StateData[name]
|
||||
}
|
||||
|
||||
func (a *Artifact) Destroy() error {
|
||||
log.Printf("Destroying image: %s", a.ImageId)
|
||||
return images.Delete(a.Client, a.ImageId).ExtractErr()
|
||||
}
|
200
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/builder.go
generated
vendored
Normal file
200
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/builder.go
generated
vendored
Normal file
|
@ -0,0 +1,200 @@
|
|||
//go:generate packer-sdc mapstructure-to-hcl2 -type Config,ImageFilter,ImageFilterOptions
|
||||
|
||||
// The openstack package contains a packersdk.Builder implementation that
|
||||
// builds Images for openstack.
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/hashicorp/packer-plugin-sdk/common"
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep/commonsteps"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/config"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
)
|
||||
|
||||
// The unique ID for this builder
|
||||
const BuilderId = "mitchellh.openstack"
|
||||
|
||||
type Config struct {
|
||||
common.PackerConfig `mapstructure:",squash"`
|
||||
|
||||
AccessConfig `mapstructure:",squash"`
|
||||
ImageConfig `mapstructure:",squash"`
|
||||
RunConfig `mapstructure:",squash"`
|
||||
|
||||
ctx interpolate.Context
|
||||
}
|
||||
|
||||
type Builder struct {
|
||||
config Config
|
||||
runner multistep.Runner
|
||||
}
|
||||
|
||||
func (b *Builder) ConfigSpec() hcldec.ObjectSpec { return b.config.FlatMapstructure().HCL2Spec() }
|
||||
|
||||
func (b *Builder) Prepare(raws ...interface{}) ([]string, []string, error) {
|
||||
err := config.Decode(&b.config, &config.DecodeOpts{
|
||||
PluginType: BuilderId,
|
||||
Interpolate: true,
|
||||
InterpolateContext: &b.config.ctx,
|
||||
}, raws...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Accumulate any errors
|
||||
var errs *packersdk.MultiError
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.AccessConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.ImageConfig.Prepare(&b.config.ctx)...)
|
||||
errs = packersdk.MultiErrorAppend(errs, b.config.RunConfig.Prepare(&b.config.ctx)...)
|
||||
|
||||
if errs != nil && len(errs.Errors) > 0 {
|
||||
return nil, nil, errs
|
||||
}
|
||||
|
||||
if b.config.ImageConfig.ImageDiskFormat != "" && !b.config.RunConfig.UseBlockStorageVolume {
|
||||
return nil, nil, fmt.Errorf("use_blockstorage_volume must be true if image_disk_format is specified.")
|
||||
}
|
||||
|
||||
// By default, instance name is same as image name
|
||||
if b.config.InstanceName == "" {
|
||||
b.config.InstanceName = b.config.ImageName
|
||||
}
|
||||
|
||||
packersdk.LogSecretFilter.Set(b.config.Password)
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
func (b *Builder) Run(ctx context.Context, ui packersdk.Ui, hook packersdk.Hook) (packersdk.Artifact, error) {
|
||||
if b.config.PackerDebug {
|
||||
b.config.enableDebug(ui)
|
||||
}
|
||||
|
||||
computeClient, err := b.config.computeV2Client()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error initializing compute client: %s", err)
|
||||
}
|
||||
|
||||
imageClient, err := b.config.imageV2Client()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error initializing image client: %s", err)
|
||||
}
|
||||
|
||||
// Setup the state bag and initial state for the steps
|
||||
state := new(multistep.BasicStateBag)
|
||||
state.Put("config", &b.config)
|
||||
state.Put("hook", hook)
|
||||
state.Put("ui", ui)
|
||||
|
||||
// Build the steps
|
||||
steps := []multistep.Step{
|
||||
&StepLoadFlavor{
|
||||
Flavor: b.config.Flavor,
|
||||
},
|
||||
&StepKeyPair{
|
||||
Debug: b.config.PackerDebug,
|
||||
Comm: &b.config.Comm,
|
||||
DebugKeyPath: fmt.Sprintf("os_%s.pem", b.config.PackerBuildName),
|
||||
},
|
||||
&StepSourceImageInfo{
|
||||
SourceImage: b.config.RunConfig.SourceImage,
|
||||
SourceImageName: b.config.RunConfig.SourceImageName,
|
||||
ExternalSourceImageURL: b.config.RunConfig.ExternalSourceImageURL,
|
||||
ExternalSourceImageFormat: b.config.RunConfig.ExternalSourceImageFormat,
|
||||
ExternalSourceImageProperties: b.config.RunConfig.ExternalSourceImageProperties,
|
||||
SourceImageOpts: b.config.RunConfig.sourceImageOpts,
|
||||
SourceMostRecent: b.config.SourceImageFilters.MostRecent,
|
||||
SourceProperties: b.config.SourceImageFilters.Filters.Properties,
|
||||
},
|
||||
&StepDiscoverNetwork{
|
||||
Networks: b.config.Networks,
|
||||
NetworkDiscoveryCIDRs: b.config.NetworkDiscoveryCIDRs,
|
||||
Ports: b.config.Ports,
|
||||
},
|
||||
&StepCreateVolume{
|
||||
UseBlockStorageVolume: b.config.UseBlockStorageVolume,
|
||||
VolumeName: b.config.VolumeName,
|
||||
VolumeType: b.config.VolumeType,
|
||||
VolumeAvailabilityZone: b.config.VolumeAvailabilityZone,
|
||||
},
|
||||
&StepRunSourceServer{
|
||||
Name: b.config.InstanceName,
|
||||
SecurityGroups: b.config.SecurityGroups,
|
||||
AvailabilityZone: b.config.AvailabilityZone,
|
||||
UserData: b.config.UserData,
|
||||
UserDataFile: b.config.UserDataFile,
|
||||
ConfigDrive: b.config.ConfigDrive,
|
||||
InstanceMetadata: b.config.InstanceMetadata,
|
||||
UseBlockStorageVolume: b.config.UseBlockStorageVolume,
|
||||
ForceDelete: b.config.ForceDelete,
|
||||
},
|
||||
&StepGetPassword{
|
||||
Debug: b.config.PackerDebug,
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
},
|
||||
&StepWaitForRackConnect{
|
||||
Wait: b.config.RackconnectWait,
|
||||
},
|
||||
&StepAllocateIp{
|
||||
FloatingIPNetwork: b.config.FloatingIPNetwork,
|
||||
FloatingIP: b.config.FloatingIP,
|
||||
ReuseIPs: b.config.ReuseIPs,
|
||||
InstanceFloatingIPNet: b.config.InstanceFloatingIPNet,
|
||||
},
|
||||
&communicator.StepConnect{
|
||||
Config: &b.config.RunConfig.Comm,
|
||||
Host: CommHost(
|
||||
b.config.RunConfig.Comm.Host(),
|
||||
computeClient,
|
||||
b.config.SSHInterface,
|
||||
b.config.SSHIPVersion),
|
||||
SSHConfig: b.config.RunConfig.Comm.SSHConfigFunc(),
|
||||
},
|
||||
&commonsteps.StepProvision{},
|
||||
&commonsteps.StepCleanupTempKeys{
|
||||
Comm: &b.config.RunConfig.Comm,
|
||||
},
|
||||
&StepStopServer{},
|
||||
&StepDetachVolume{
|
||||
UseBlockStorageVolume: b.config.UseBlockStorageVolume,
|
||||
},
|
||||
&stepCreateImage{
|
||||
UseBlockStorageVolume: b.config.UseBlockStorageVolume,
|
||||
},
|
||||
&stepUpdateImageTags{},
|
||||
&stepUpdateImageVisibility{},
|
||||
&stepAddImageMembers{},
|
||||
&stepUpdateImageMinDisk{},
|
||||
}
|
||||
|
||||
// Run!
|
||||
b.runner = commonsteps.NewRunner(steps, b.config.PackerConfig, ui)
|
||||
b.runner.Run(ctx, state)
|
||||
|
||||
// If there was an error, return that
|
||||
if rawErr, ok := state.GetOk("error"); ok {
|
||||
return nil, rawErr.(error)
|
||||
}
|
||||
|
||||
// If there are no images, then just return
|
||||
if _, ok := state.GetOk("image"); !ok {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Build the artifact and return it
|
||||
artifact := &Artifact{
|
||||
ImageId: state.Get("image").(string),
|
||||
BuilderIdValue: BuilderId,
|
||||
Client: imageClient,
|
||||
StateData: map[string]interface{}{"generated_data": state.Get("generated_data")},
|
||||
}
|
||||
|
||||
return artifact, nil
|
||||
}
|
322
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/builder.hcl2spec.go
generated
vendored
Normal file
322
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/builder.hcl2spec.go
generated
vendored
Normal file
|
@ -0,0 +1,322 @@
|
|||
// Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT.
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
"github.com/hashicorp/hcl/v2/hcldec"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
// FlatConfig is an auto-generated flat version of Config.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatConfig struct {
|
||||
PackerBuildName *string `mapstructure:"packer_build_name" cty:"packer_build_name" hcl:"packer_build_name"`
|
||||
PackerBuilderType *string `mapstructure:"packer_builder_type" cty:"packer_builder_type" hcl:"packer_builder_type"`
|
||||
PackerCoreVersion *string `mapstructure:"packer_core_version" cty:"packer_core_version" hcl:"packer_core_version"`
|
||||
PackerDebug *bool `mapstructure:"packer_debug" cty:"packer_debug" hcl:"packer_debug"`
|
||||
PackerForce *bool `mapstructure:"packer_force" cty:"packer_force" hcl:"packer_force"`
|
||||
PackerOnError *string `mapstructure:"packer_on_error" cty:"packer_on_error" hcl:"packer_on_error"`
|
||||
PackerUserVars map[string]string `mapstructure:"packer_user_variables" cty:"packer_user_variables" hcl:"packer_user_variables"`
|
||||
PackerSensitiveVars []string `mapstructure:"packer_sensitive_variables" cty:"packer_sensitive_variables" hcl:"packer_sensitive_variables"`
|
||||
Username *string `mapstructure:"username" required:"true" cty:"username" hcl:"username"`
|
||||
UserID *string `mapstructure:"user_id" cty:"user_id" hcl:"user_id"`
|
||||
Password *string `mapstructure:"password" required:"true" cty:"password" hcl:"password"`
|
||||
IdentityEndpoint *string `mapstructure:"identity_endpoint" required:"true" cty:"identity_endpoint" hcl:"identity_endpoint"`
|
||||
TenantID *string `mapstructure:"tenant_id" required:"false" cty:"tenant_id" hcl:"tenant_id"`
|
||||
TenantName *string `mapstructure:"tenant_name" cty:"tenant_name" hcl:"tenant_name"`
|
||||
DomainID *string `mapstructure:"domain_id" cty:"domain_id" hcl:"domain_id"`
|
||||
DomainName *string `mapstructure:"domain_name" required:"false" cty:"domain_name" hcl:"domain_name"`
|
||||
Insecure *bool `mapstructure:"insecure" required:"false" cty:"insecure" hcl:"insecure"`
|
||||
Region *string `mapstructure:"region" required:"false" cty:"region" hcl:"region"`
|
||||
EndpointType *string `mapstructure:"endpoint_type" required:"false" cty:"endpoint_type" hcl:"endpoint_type"`
|
||||
CACertFile *string `mapstructure:"cacert" required:"false" cty:"cacert" hcl:"cacert"`
|
||||
ClientCertFile *string `mapstructure:"cert" required:"false" cty:"cert" hcl:"cert"`
|
||||
ClientKeyFile *string `mapstructure:"key" required:"false" cty:"key" hcl:"key"`
|
||||
Token *string `mapstructure:"token" required:"false" cty:"token" hcl:"token"`
|
||||
ApplicationCredentialName *string `mapstructure:"application_credential_name" required:"false" cty:"application_credential_name" hcl:"application_credential_name"`
|
||||
ApplicationCredentialID *string `mapstructure:"application_credential_id" required:"false" cty:"application_credential_id" hcl:"application_credential_id"`
|
||||
ApplicationCredentialSecret *string `mapstructure:"application_credential_secret" required:"false" cty:"application_credential_secret" hcl:"application_credential_secret"`
|
||||
Cloud *string `mapstructure:"cloud" required:"false" cty:"cloud" hcl:"cloud"`
|
||||
ImageName *string `mapstructure:"image_name" required:"true" cty:"image_name" hcl:"image_name"`
|
||||
ImageMetadata map[string]string `mapstructure:"metadata" required:"false" cty:"metadata" hcl:"metadata"`
|
||||
ImageVisibility *images.ImageVisibility `mapstructure:"image_visibility" required:"false" cty:"image_visibility" hcl:"image_visibility"`
|
||||
ImageMembers []string `mapstructure:"image_members" required:"false" cty:"image_members" hcl:"image_members"`
|
||||
ImageAutoAcceptMembers *bool `mapstructure:"image_auto_accept_members" required:"false" cty:"image_auto_accept_members" hcl:"image_auto_accept_members"`
|
||||
ImageDiskFormat *string `mapstructure:"image_disk_format" required:"false" cty:"image_disk_format" hcl:"image_disk_format"`
|
||||
ImageTags []string `mapstructure:"image_tags" required:"false" cty:"image_tags" hcl:"image_tags"`
|
||||
ImageMinDisk *int `mapstructure:"image_min_disk" required:"false" cty:"image_min_disk" hcl:"image_min_disk"`
|
||||
SkipCreateImage *bool `mapstructure:"skip_create_image" required:"false" cty:"skip_create_image" hcl:"skip_create_image"`
|
||||
Type *string `mapstructure:"communicator" cty:"communicator" hcl:"communicator"`
|
||||
PauseBeforeConnect *string `mapstructure:"pause_before_connecting" cty:"pause_before_connecting" hcl:"pause_before_connecting"`
|
||||
SSHHost *string `mapstructure:"ssh_host" cty:"ssh_host" hcl:"ssh_host"`
|
||||
SSHPort *int `mapstructure:"ssh_port" cty:"ssh_port" hcl:"ssh_port"`
|
||||
SSHUsername *string `mapstructure:"ssh_username" cty:"ssh_username" hcl:"ssh_username"`
|
||||
SSHPassword *string `mapstructure:"ssh_password" cty:"ssh_password" hcl:"ssh_password"`
|
||||
SSHKeyPairName *string `mapstructure:"ssh_keypair_name" undocumented:"true" cty:"ssh_keypair_name" hcl:"ssh_keypair_name"`
|
||||
SSHTemporaryKeyPairName *string `mapstructure:"temporary_key_pair_name" undocumented:"true" cty:"temporary_key_pair_name" hcl:"temporary_key_pair_name"`
|
||||
SSHTemporaryKeyPairType *string `mapstructure:"temporary_key_pair_type" cty:"temporary_key_pair_type" hcl:"temporary_key_pair_type"`
|
||||
SSHTemporaryKeyPairBits *int `mapstructure:"temporary_key_pair_bits" cty:"temporary_key_pair_bits" hcl:"temporary_key_pair_bits"`
|
||||
SSHCiphers []string `mapstructure:"ssh_ciphers" cty:"ssh_ciphers" hcl:"ssh_ciphers"`
|
||||
SSHClearAuthorizedKeys *bool `mapstructure:"ssh_clear_authorized_keys" cty:"ssh_clear_authorized_keys" hcl:"ssh_clear_authorized_keys"`
|
||||
SSHKEXAlgos []string `mapstructure:"ssh_key_exchange_algorithms" cty:"ssh_key_exchange_algorithms" hcl:"ssh_key_exchange_algorithms"`
|
||||
SSHPrivateKeyFile *string `mapstructure:"ssh_private_key_file" undocumented:"true" cty:"ssh_private_key_file" hcl:"ssh_private_key_file"`
|
||||
SSHCertificateFile *string `mapstructure:"ssh_certificate_file" cty:"ssh_certificate_file" hcl:"ssh_certificate_file"`
|
||||
SSHPty *bool `mapstructure:"ssh_pty" cty:"ssh_pty" hcl:"ssh_pty"`
|
||||
SSHTimeout *string `mapstructure:"ssh_timeout" cty:"ssh_timeout" hcl:"ssh_timeout"`
|
||||
SSHWaitTimeout *string `mapstructure:"ssh_wait_timeout" undocumented:"true" cty:"ssh_wait_timeout" hcl:"ssh_wait_timeout"`
|
||||
SSHAgentAuth *bool `mapstructure:"ssh_agent_auth" undocumented:"true" cty:"ssh_agent_auth" hcl:"ssh_agent_auth"`
|
||||
SSHDisableAgentForwarding *bool `mapstructure:"ssh_disable_agent_forwarding" cty:"ssh_disable_agent_forwarding" hcl:"ssh_disable_agent_forwarding"`
|
||||
SSHHandshakeAttempts *int `mapstructure:"ssh_handshake_attempts" cty:"ssh_handshake_attempts" hcl:"ssh_handshake_attempts"`
|
||||
SSHBastionHost *string `mapstructure:"ssh_bastion_host" cty:"ssh_bastion_host" hcl:"ssh_bastion_host"`
|
||||
SSHBastionPort *int `mapstructure:"ssh_bastion_port" cty:"ssh_bastion_port" hcl:"ssh_bastion_port"`
|
||||
SSHBastionAgentAuth *bool `mapstructure:"ssh_bastion_agent_auth" cty:"ssh_bastion_agent_auth" hcl:"ssh_bastion_agent_auth"`
|
||||
SSHBastionUsername *string `mapstructure:"ssh_bastion_username" cty:"ssh_bastion_username" hcl:"ssh_bastion_username"`
|
||||
SSHBastionPassword *string `mapstructure:"ssh_bastion_password" cty:"ssh_bastion_password" hcl:"ssh_bastion_password"`
|
||||
SSHBastionInteractive *bool `mapstructure:"ssh_bastion_interactive" cty:"ssh_bastion_interactive" hcl:"ssh_bastion_interactive"`
|
||||
SSHBastionPrivateKeyFile *string `mapstructure:"ssh_bastion_private_key_file" cty:"ssh_bastion_private_key_file" hcl:"ssh_bastion_private_key_file"`
|
||||
SSHBastionCertificateFile *string `mapstructure:"ssh_bastion_certificate_file" cty:"ssh_bastion_certificate_file" hcl:"ssh_bastion_certificate_file"`
|
||||
SSHFileTransferMethod *string `mapstructure:"ssh_file_transfer_method" cty:"ssh_file_transfer_method" hcl:"ssh_file_transfer_method"`
|
||||
SSHProxyHost *string `mapstructure:"ssh_proxy_host" cty:"ssh_proxy_host" hcl:"ssh_proxy_host"`
|
||||
SSHProxyPort *int `mapstructure:"ssh_proxy_port" cty:"ssh_proxy_port" hcl:"ssh_proxy_port"`
|
||||
SSHProxyUsername *string `mapstructure:"ssh_proxy_username" cty:"ssh_proxy_username" hcl:"ssh_proxy_username"`
|
||||
SSHProxyPassword *string `mapstructure:"ssh_proxy_password" cty:"ssh_proxy_password" hcl:"ssh_proxy_password"`
|
||||
SSHKeepAliveInterval *string `mapstructure:"ssh_keep_alive_interval" cty:"ssh_keep_alive_interval" hcl:"ssh_keep_alive_interval"`
|
||||
SSHReadWriteTimeout *string `mapstructure:"ssh_read_write_timeout" cty:"ssh_read_write_timeout" hcl:"ssh_read_write_timeout"`
|
||||
SSHRemoteTunnels []string `mapstructure:"ssh_remote_tunnels" cty:"ssh_remote_tunnels" hcl:"ssh_remote_tunnels"`
|
||||
SSHLocalTunnels []string `mapstructure:"ssh_local_tunnels" cty:"ssh_local_tunnels" hcl:"ssh_local_tunnels"`
|
||||
SSHPublicKey []byte `mapstructure:"ssh_public_key" undocumented:"true" cty:"ssh_public_key" hcl:"ssh_public_key"`
|
||||
SSHPrivateKey []byte `mapstructure:"ssh_private_key" undocumented:"true" cty:"ssh_private_key" hcl:"ssh_private_key"`
|
||||
WinRMUser *string `mapstructure:"winrm_username" cty:"winrm_username" hcl:"winrm_username"`
|
||||
WinRMPassword *string `mapstructure:"winrm_password" cty:"winrm_password" hcl:"winrm_password"`
|
||||
WinRMHost *string `mapstructure:"winrm_host" cty:"winrm_host" hcl:"winrm_host"`
|
||||
WinRMNoProxy *bool `mapstructure:"winrm_no_proxy" cty:"winrm_no_proxy" hcl:"winrm_no_proxy"`
|
||||
WinRMPort *int `mapstructure:"winrm_port" cty:"winrm_port" hcl:"winrm_port"`
|
||||
WinRMTimeout *string `mapstructure:"winrm_timeout" cty:"winrm_timeout" hcl:"winrm_timeout"`
|
||||
WinRMUseSSL *bool `mapstructure:"winrm_use_ssl" cty:"winrm_use_ssl" hcl:"winrm_use_ssl"`
|
||||
WinRMInsecure *bool `mapstructure:"winrm_insecure" cty:"winrm_insecure" hcl:"winrm_insecure"`
|
||||
WinRMUseNTLM *bool `mapstructure:"winrm_use_ntlm" cty:"winrm_use_ntlm" hcl:"winrm_use_ntlm"`
|
||||
SSHInterface *string `mapstructure:"ssh_interface" required:"false" cty:"ssh_interface" hcl:"ssh_interface"`
|
||||
SSHIPVersion *string `mapstructure:"ssh_ip_version" required:"false" cty:"ssh_ip_version" hcl:"ssh_ip_version"`
|
||||
SourceImage *string `mapstructure:"source_image" required:"true" cty:"source_image" hcl:"source_image"`
|
||||
SourceImageName *string `mapstructure:"source_image_name" required:"true" cty:"source_image_name" hcl:"source_image_name"`
|
||||
ExternalSourceImageURL *string `mapstructure:"external_source_image_url" required:"true" cty:"external_source_image_url" hcl:"external_source_image_url"`
|
||||
ExternalSourceImageFormat *string `mapstructure:"external_source_image_format" required:"false" cty:"external_source_image_format" hcl:"external_source_image_format"`
|
||||
ExternalSourceImageProperties map[string]string `mapstructure:"external_source_image_properties" required:"false" cty:"external_source_image_properties" hcl:"external_source_image_properties"`
|
||||
SourceImageFilters *FlatImageFilter `mapstructure:"source_image_filter" required:"true" cty:"source_image_filter" hcl:"source_image_filter"`
|
||||
Flavor *string `mapstructure:"flavor" required:"true" cty:"flavor" hcl:"flavor"`
|
||||
AvailabilityZone *string `mapstructure:"availability_zone" required:"false" cty:"availability_zone" hcl:"availability_zone"`
|
||||
RackconnectWait *bool `mapstructure:"rackconnect_wait" required:"false" cty:"rackconnect_wait" hcl:"rackconnect_wait"`
|
||||
FloatingIPNetwork *string `mapstructure:"floating_ip_network" required:"false" cty:"floating_ip_network" hcl:"floating_ip_network"`
|
||||
InstanceFloatingIPNet *string `mapstructure:"instance_floating_ip_net" required:"false" cty:"instance_floating_ip_net" hcl:"instance_floating_ip_net"`
|
||||
FloatingIP *string `mapstructure:"floating_ip" required:"false" cty:"floating_ip" hcl:"floating_ip"`
|
||||
ReuseIPs *bool `mapstructure:"reuse_ips" required:"false" cty:"reuse_ips" hcl:"reuse_ips"`
|
||||
SecurityGroups []string `mapstructure:"security_groups" required:"false" cty:"security_groups" hcl:"security_groups"`
|
||||
Networks []string `mapstructure:"networks" required:"false" cty:"networks" hcl:"networks"`
|
||||
Ports []string `mapstructure:"ports" required:"false" cty:"ports" hcl:"ports"`
|
||||
NetworkDiscoveryCIDRs []string `mapstructure:"network_discovery_cidrs" required:"false" cty:"network_discovery_cidrs" hcl:"network_discovery_cidrs"`
|
||||
UserData *string `mapstructure:"user_data" required:"false" cty:"user_data" hcl:"user_data"`
|
||||
UserDataFile *string `mapstructure:"user_data_file" required:"false" cty:"user_data_file" hcl:"user_data_file"`
|
||||
InstanceName *string `mapstructure:"instance_name" required:"false" cty:"instance_name" hcl:"instance_name"`
|
||||
InstanceMetadata map[string]string `mapstructure:"instance_metadata" required:"false" cty:"instance_metadata" hcl:"instance_metadata"`
|
||||
ForceDelete *bool `mapstructure:"force_delete" required:"false" cty:"force_delete" hcl:"force_delete"`
|
||||
ConfigDrive *bool `mapstructure:"config_drive" required:"false" cty:"config_drive" hcl:"config_drive"`
|
||||
FloatingIPPool *string `mapstructure:"floating_ip_pool" required:"false" cty:"floating_ip_pool" hcl:"floating_ip_pool"`
|
||||
UseBlockStorageVolume *bool `mapstructure:"use_blockstorage_volume" required:"false" cty:"use_blockstorage_volume" hcl:"use_blockstorage_volume"`
|
||||
VolumeName *string `mapstructure:"volume_name" required:"false" cty:"volume_name" hcl:"volume_name"`
|
||||
VolumeType *string `mapstructure:"volume_type" required:"false" cty:"volume_type" hcl:"volume_type"`
|
||||
VolumeSize *int `mapstructure:"volume_size" required:"false" cty:"volume_size" hcl:"volume_size"`
|
||||
VolumeAvailabilityZone *string `mapstructure:"volume_availability_zone" required:"false" cty:"volume_availability_zone" hcl:"volume_availability_zone"`
|
||||
OpenstackProvider *string `mapstructure:"openstack_provider" cty:"openstack_provider" hcl:"openstack_provider"`
|
||||
UseFloatingIp *bool `mapstructure:"use_floating_ip" required:"false" cty:"use_floating_ip" hcl:"use_floating_ip"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatConfig.
|
||||
// FlatConfig is an auto-generated flat version of Config.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatConfig)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a Config.
|
||||
// This spec is used by HCL to read the fields of Config.
|
||||
// The decoded values from this spec will then be applied to a FlatConfig.
|
||||
func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"packer_build_name": &hcldec.AttrSpec{Name: "packer_build_name", Type: cty.String, Required: false},
|
||||
"packer_builder_type": &hcldec.AttrSpec{Name: "packer_builder_type", Type: cty.String, Required: false},
|
||||
"packer_core_version": &hcldec.AttrSpec{Name: "packer_core_version", Type: cty.String, Required: false},
|
||||
"packer_debug": &hcldec.AttrSpec{Name: "packer_debug", Type: cty.Bool, Required: false},
|
||||
"packer_force": &hcldec.AttrSpec{Name: "packer_force", Type: cty.Bool, Required: false},
|
||||
"packer_on_error": &hcldec.AttrSpec{Name: "packer_on_error", Type: cty.String, Required: false},
|
||||
"packer_user_variables": &hcldec.AttrSpec{Name: "packer_user_variables", Type: cty.Map(cty.String), Required: false},
|
||||
"packer_sensitive_variables": &hcldec.AttrSpec{Name: "packer_sensitive_variables", Type: cty.List(cty.String), Required: false},
|
||||
"username": &hcldec.AttrSpec{Name: "username", Type: cty.String, Required: false},
|
||||
"user_id": &hcldec.AttrSpec{Name: "user_id", Type: cty.String, Required: false},
|
||||
"password": &hcldec.AttrSpec{Name: "password", Type: cty.String, Required: false},
|
||||
"identity_endpoint": &hcldec.AttrSpec{Name: "identity_endpoint", Type: cty.String, Required: false},
|
||||
"tenant_id": &hcldec.AttrSpec{Name: "tenant_id", Type: cty.String, Required: false},
|
||||
"tenant_name": &hcldec.AttrSpec{Name: "tenant_name", Type: cty.String, Required: false},
|
||||
"domain_id": &hcldec.AttrSpec{Name: "domain_id", Type: cty.String, Required: false},
|
||||
"domain_name": &hcldec.AttrSpec{Name: "domain_name", Type: cty.String, Required: false},
|
||||
"insecure": &hcldec.AttrSpec{Name: "insecure", Type: cty.Bool, Required: false},
|
||||
"region": &hcldec.AttrSpec{Name: "region", Type: cty.String, Required: false},
|
||||
"endpoint_type": &hcldec.AttrSpec{Name: "endpoint_type", Type: cty.String, Required: false},
|
||||
"cacert": &hcldec.AttrSpec{Name: "cacert", Type: cty.String, Required: false},
|
||||
"cert": &hcldec.AttrSpec{Name: "cert", Type: cty.String, Required: false},
|
||||
"key": &hcldec.AttrSpec{Name: "key", Type: cty.String, Required: false},
|
||||
"token": &hcldec.AttrSpec{Name: "token", Type: cty.String, Required: false},
|
||||
"application_credential_name": &hcldec.AttrSpec{Name: "application_credential_name", Type: cty.String, Required: false},
|
||||
"application_credential_id": &hcldec.AttrSpec{Name: "application_credential_id", Type: cty.String, Required: false},
|
||||
"application_credential_secret": &hcldec.AttrSpec{Name: "application_credential_secret", Type: cty.String, Required: false},
|
||||
"cloud": &hcldec.AttrSpec{Name: "cloud", Type: cty.String, Required: false},
|
||||
"image_name": &hcldec.AttrSpec{Name: "image_name", Type: cty.String, Required: false},
|
||||
"metadata": &hcldec.AttrSpec{Name: "metadata", Type: cty.Map(cty.String), Required: false},
|
||||
"image_visibility": &hcldec.AttrSpec{Name: "image_visibility", Type: cty.String, Required: false},
|
||||
"image_members": &hcldec.AttrSpec{Name: "image_members", Type: cty.List(cty.String), Required: false},
|
||||
"image_auto_accept_members": &hcldec.AttrSpec{Name: "image_auto_accept_members", Type: cty.Bool, Required: false},
|
||||
"image_disk_format": &hcldec.AttrSpec{Name: "image_disk_format", Type: cty.String, Required: false},
|
||||
"image_tags": &hcldec.AttrSpec{Name: "image_tags", Type: cty.List(cty.String), Required: false},
|
||||
"image_min_disk": &hcldec.AttrSpec{Name: "image_min_disk", Type: cty.Number, Required: false},
|
||||
"skip_create_image": &hcldec.AttrSpec{Name: "skip_create_image", Type: cty.Bool, Required: false},
|
||||
"communicator": &hcldec.AttrSpec{Name: "communicator", Type: cty.String, Required: false},
|
||||
"pause_before_connecting": &hcldec.AttrSpec{Name: "pause_before_connecting", Type: cty.String, Required: false},
|
||||
"ssh_host": &hcldec.AttrSpec{Name: "ssh_host", Type: cty.String, Required: false},
|
||||
"ssh_port": &hcldec.AttrSpec{Name: "ssh_port", Type: cty.Number, Required: false},
|
||||
"ssh_username": &hcldec.AttrSpec{Name: "ssh_username", Type: cty.String, Required: false},
|
||||
"ssh_password": &hcldec.AttrSpec{Name: "ssh_password", Type: cty.String, Required: false},
|
||||
"ssh_keypair_name": &hcldec.AttrSpec{Name: "ssh_keypair_name", Type: cty.String, Required: false},
|
||||
"temporary_key_pair_name": &hcldec.AttrSpec{Name: "temporary_key_pair_name", Type: cty.String, Required: false},
|
||||
"temporary_key_pair_type": &hcldec.AttrSpec{Name: "temporary_key_pair_type", Type: cty.String, Required: false},
|
||||
"temporary_key_pair_bits": &hcldec.AttrSpec{Name: "temporary_key_pair_bits", Type: cty.Number, Required: false},
|
||||
"ssh_ciphers": &hcldec.AttrSpec{Name: "ssh_ciphers", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_clear_authorized_keys": &hcldec.AttrSpec{Name: "ssh_clear_authorized_keys", Type: cty.Bool, Required: false},
|
||||
"ssh_key_exchange_algorithms": &hcldec.AttrSpec{Name: "ssh_key_exchange_algorithms", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_private_key_file": &hcldec.AttrSpec{Name: "ssh_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_certificate_file": &hcldec.AttrSpec{Name: "ssh_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_pty": &hcldec.AttrSpec{Name: "ssh_pty", Type: cty.Bool, Required: false},
|
||||
"ssh_timeout": &hcldec.AttrSpec{Name: "ssh_timeout", Type: cty.String, Required: false},
|
||||
"ssh_wait_timeout": &hcldec.AttrSpec{Name: "ssh_wait_timeout", Type: cty.String, Required: false},
|
||||
"ssh_agent_auth": &hcldec.AttrSpec{Name: "ssh_agent_auth", Type: cty.Bool, Required: false},
|
||||
"ssh_disable_agent_forwarding": &hcldec.AttrSpec{Name: "ssh_disable_agent_forwarding", Type: cty.Bool, Required: false},
|
||||
"ssh_handshake_attempts": &hcldec.AttrSpec{Name: "ssh_handshake_attempts", Type: cty.Number, Required: false},
|
||||
"ssh_bastion_host": &hcldec.AttrSpec{Name: "ssh_bastion_host", Type: cty.String, Required: false},
|
||||
"ssh_bastion_port": &hcldec.AttrSpec{Name: "ssh_bastion_port", Type: cty.Number, Required: false},
|
||||
"ssh_bastion_agent_auth": &hcldec.AttrSpec{Name: "ssh_bastion_agent_auth", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_username": &hcldec.AttrSpec{Name: "ssh_bastion_username", Type: cty.String, Required: false},
|
||||
"ssh_bastion_password": &hcldec.AttrSpec{Name: "ssh_bastion_password", Type: cty.String, Required: false},
|
||||
"ssh_bastion_interactive": &hcldec.AttrSpec{Name: "ssh_bastion_interactive", Type: cty.Bool, Required: false},
|
||||
"ssh_bastion_private_key_file": &hcldec.AttrSpec{Name: "ssh_bastion_private_key_file", Type: cty.String, Required: false},
|
||||
"ssh_bastion_certificate_file": &hcldec.AttrSpec{Name: "ssh_bastion_certificate_file", Type: cty.String, Required: false},
|
||||
"ssh_file_transfer_method": &hcldec.AttrSpec{Name: "ssh_file_transfer_method", Type: cty.String, Required: false},
|
||||
"ssh_proxy_host": &hcldec.AttrSpec{Name: "ssh_proxy_host", Type: cty.String, Required: false},
|
||||
"ssh_proxy_port": &hcldec.AttrSpec{Name: "ssh_proxy_port", Type: cty.Number, Required: false},
|
||||
"ssh_proxy_username": &hcldec.AttrSpec{Name: "ssh_proxy_username", Type: cty.String, Required: false},
|
||||
"ssh_proxy_password": &hcldec.AttrSpec{Name: "ssh_proxy_password", Type: cty.String, Required: false},
|
||||
"ssh_keep_alive_interval": &hcldec.AttrSpec{Name: "ssh_keep_alive_interval", Type: cty.String, Required: false},
|
||||
"ssh_read_write_timeout": &hcldec.AttrSpec{Name: "ssh_read_write_timeout", Type: cty.String, Required: false},
|
||||
"ssh_remote_tunnels": &hcldec.AttrSpec{Name: "ssh_remote_tunnels", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_local_tunnels": &hcldec.AttrSpec{Name: "ssh_local_tunnels", Type: cty.List(cty.String), Required: false},
|
||||
"ssh_public_key": &hcldec.AttrSpec{Name: "ssh_public_key", Type: cty.List(cty.Number), Required: false},
|
||||
"ssh_private_key": &hcldec.AttrSpec{Name: "ssh_private_key", Type: cty.List(cty.Number), Required: false},
|
||||
"winrm_username": &hcldec.AttrSpec{Name: "winrm_username", Type: cty.String, Required: false},
|
||||
"winrm_password": &hcldec.AttrSpec{Name: "winrm_password", Type: cty.String, Required: false},
|
||||
"winrm_host": &hcldec.AttrSpec{Name: "winrm_host", Type: cty.String, Required: false},
|
||||
"winrm_no_proxy": &hcldec.AttrSpec{Name: "winrm_no_proxy", Type: cty.Bool, Required: false},
|
||||
"winrm_port": &hcldec.AttrSpec{Name: "winrm_port", Type: cty.Number, Required: false},
|
||||
"winrm_timeout": &hcldec.AttrSpec{Name: "winrm_timeout", Type: cty.String, Required: false},
|
||||
"winrm_use_ssl": &hcldec.AttrSpec{Name: "winrm_use_ssl", Type: cty.Bool, Required: false},
|
||||
"winrm_insecure": &hcldec.AttrSpec{Name: "winrm_insecure", Type: cty.Bool, Required: false},
|
||||
"winrm_use_ntlm": &hcldec.AttrSpec{Name: "winrm_use_ntlm", Type: cty.Bool, Required: false},
|
||||
"ssh_interface": &hcldec.AttrSpec{Name: "ssh_interface", Type: cty.String, Required: false},
|
||||
"ssh_ip_version": &hcldec.AttrSpec{Name: "ssh_ip_version", Type: cty.String, Required: false},
|
||||
"source_image": &hcldec.AttrSpec{Name: "source_image", Type: cty.String, Required: false},
|
||||
"source_image_name": &hcldec.AttrSpec{Name: "source_image_name", Type: cty.String, Required: false},
|
||||
"external_source_image_url": &hcldec.AttrSpec{Name: "external_source_image_url", Type: cty.String, Required: false},
|
||||
"external_source_image_format": &hcldec.AttrSpec{Name: "external_source_image_format", Type: cty.String, Required: false},
|
||||
"external_source_image_properties": &hcldec.AttrSpec{Name: "external_source_image_properties", Type: cty.Map(cty.String), Required: false},
|
||||
"source_image_filter": &hcldec.BlockSpec{TypeName: "source_image_filter", Nested: hcldec.ObjectSpec((*FlatImageFilter)(nil).HCL2Spec())},
|
||||
"flavor": &hcldec.AttrSpec{Name: "flavor", Type: cty.String, Required: false},
|
||||
"availability_zone": &hcldec.AttrSpec{Name: "availability_zone", Type: cty.String, Required: false},
|
||||
"rackconnect_wait": &hcldec.AttrSpec{Name: "rackconnect_wait", Type: cty.Bool, Required: false},
|
||||
"floating_ip_network": &hcldec.AttrSpec{Name: "floating_ip_network", Type: cty.String, Required: false},
|
||||
"instance_floating_ip_net": &hcldec.AttrSpec{Name: "instance_floating_ip_net", Type: cty.String, Required: false},
|
||||
"floating_ip": &hcldec.AttrSpec{Name: "floating_ip", Type: cty.String, Required: false},
|
||||
"reuse_ips": &hcldec.AttrSpec{Name: "reuse_ips", Type: cty.Bool, Required: false},
|
||||
"security_groups": &hcldec.AttrSpec{Name: "security_groups", Type: cty.List(cty.String), Required: false},
|
||||
"networks": &hcldec.AttrSpec{Name: "networks", Type: cty.List(cty.String), Required: false},
|
||||
"ports": &hcldec.AttrSpec{Name: "ports", Type: cty.List(cty.String), Required: false},
|
||||
"network_discovery_cidrs": &hcldec.AttrSpec{Name: "network_discovery_cidrs", Type: cty.List(cty.String), Required: false},
|
||||
"user_data": &hcldec.AttrSpec{Name: "user_data", Type: cty.String, Required: false},
|
||||
"user_data_file": &hcldec.AttrSpec{Name: "user_data_file", Type: cty.String, Required: false},
|
||||
"instance_name": &hcldec.AttrSpec{Name: "instance_name", Type: cty.String, Required: false},
|
||||
"instance_metadata": &hcldec.AttrSpec{Name: "instance_metadata", Type: cty.Map(cty.String), Required: false},
|
||||
"force_delete": &hcldec.AttrSpec{Name: "force_delete", Type: cty.Bool, Required: false},
|
||||
"config_drive": &hcldec.AttrSpec{Name: "config_drive", Type: cty.Bool, Required: false},
|
||||
"floating_ip_pool": &hcldec.AttrSpec{Name: "floating_ip_pool", Type: cty.String, Required: false},
|
||||
"use_blockstorage_volume": &hcldec.AttrSpec{Name: "use_blockstorage_volume", Type: cty.Bool, Required: false},
|
||||
"volume_name": &hcldec.AttrSpec{Name: "volume_name", Type: cty.String, Required: false},
|
||||
"volume_type": &hcldec.AttrSpec{Name: "volume_type", Type: cty.String, Required: false},
|
||||
"volume_size": &hcldec.AttrSpec{Name: "volume_size", Type: cty.Number, Required: false},
|
||||
"volume_availability_zone": &hcldec.AttrSpec{Name: "volume_availability_zone", Type: cty.String, Required: false},
|
||||
"openstack_provider": &hcldec.AttrSpec{Name: "openstack_provider", Type: cty.String, Required: false},
|
||||
"use_floating_ip": &hcldec.AttrSpec{Name: "use_floating_ip", Type: cty.Bool, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatImageFilter is an auto-generated flat version of ImageFilter.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatImageFilter struct {
|
||||
Filters *FlatImageFilterOptions `mapstructure:"filters" required:"false" cty:"filters" hcl:"filters"`
|
||||
MostRecent *bool `mapstructure:"most_recent" required:"false" cty:"most_recent" hcl:"most_recent"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatImageFilter.
|
||||
// FlatImageFilter is an auto-generated flat version of ImageFilter.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*ImageFilter) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatImageFilter)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a ImageFilter.
|
||||
// This spec is used by HCL to read the fields of ImageFilter.
|
||||
// The decoded values from this spec will then be applied to a FlatImageFilter.
|
||||
func (*FlatImageFilter) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"filters": &hcldec.BlockSpec{TypeName: "filters", Nested: hcldec.ObjectSpec((*FlatImageFilterOptions)(nil).HCL2Spec())},
|
||||
"most_recent": &hcldec.AttrSpec{Name: "most_recent", Type: cty.Bool, Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// FlatImageFilterOptions is an auto-generated flat version of ImageFilterOptions.
|
||||
// Where the contents of a field with a `mapstructure:,squash` tag are bubbled up.
|
||||
type FlatImageFilterOptions struct {
|
||||
Name *string `mapstructure:"name" cty:"name" hcl:"name"`
|
||||
Owner *string `mapstructure:"owner" cty:"owner" hcl:"owner"`
|
||||
Tags []string `mapstructure:"tags" cty:"tags" hcl:"tags"`
|
||||
Visibility *string `mapstructure:"visibility" cty:"visibility" hcl:"visibility"`
|
||||
Properties map[string]string `mapstructure:"properties" cty:"properties" hcl:"properties"`
|
||||
}
|
||||
|
||||
// FlatMapstructure returns a new FlatImageFilterOptions.
|
||||
// FlatImageFilterOptions is an auto-generated flat version of ImageFilterOptions.
|
||||
// Where the contents a fields with a `mapstructure:,squash` tag are bubbled up.
|
||||
func (*ImageFilterOptions) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } {
|
||||
return new(FlatImageFilterOptions)
|
||||
}
|
||||
|
||||
// HCL2Spec returns the hcl spec of a ImageFilterOptions.
|
||||
// This spec is used by HCL to read the fields of ImageFilterOptions.
|
||||
// The decoded values from this spec will then be applied to a FlatImageFilterOptions.
|
||||
func (*FlatImageFilterOptions) HCL2Spec() map[string]hcldec.Spec {
|
||||
s := map[string]hcldec.Spec{
|
||||
"name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false},
|
||||
"owner": &hcldec.AttrSpec{Name: "owner", Type: cty.String, Required: false},
|
||||
"tags": &hcldec.AttrSpec{Name: "tags", Type: cty.List(cty.String), Required: false},
|
||||
"visibility": &hcldec.AttrSpec{Name: "visibility", Type: cty.String, Required: false},
|
||||
"properties": &hcldec.AttrSpec{Name: "properties", Type: cty.Map(cty.String), Required: false},
|
||||
}
|
||||
return s
|
||||
}
|
83
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/image_config.go
generated
vendored
Normal file
83
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/image_config.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
imageservice "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
)
|
||||
|
||||
// ImageConfig is for common configuration related to creating Images.
|
||||
type ImageConfig struct {
|
||||
// The name of the resulting image.
|
||||
ImageName string `mapstructure:"image_name" required:"true"`
|
||||
// Glance metadata that will be applied to the image.
|
||||
ImageMetadata map[string]string `mapstructure:"metadata" required:"false"`
|
||||
// One of "public", "private", "shared", or "community".
|
||||
ImageVisibility imageservice.ImageVisibility `mapstructure:"image_visibility" required:"false"`
|
||||
// List of members to add to the image after creation. An image member is
|
||||
// usually a project (also called the "tenant") with whom the image is
|
||||
// shared.
|
||||
ImageMembers []string `mapstructure:"image_members" required:"false"`
|
||||
// When true, perform the image accept so the members can see the image in their
|
||||
// project. This requires a user with priveleges both in the build project and
|
||||
// in the members provided. Defaults to false.
|
||||
ImageAutoAcceptMembers bool `mapstructure:"image_auto_accept_members" required:"false"`
|
||||
// Disk format of the resulting image. This option works if
|
||||
// use_blockstorage_volume is true.
|
||||
ImageDiskFormat string `mapstructure:"image_disk_format" required:"false"`
|
||||
// List of tags to add to the image after creation.
|
||||
ImageTags []string `mapstructure:"image_tags" required:"false"`
|
||||
// Minimum disk size needed to boot image, in gigabytes.
|
||||
ImageMinDisk int `mapstructure:"image_min_disk" required:"false"`
|
||||
// Skip creating the image. Useful for setting to `true` during a build test stage. Defaults to `false`.
|
||||
SkipCreateImage bool `mapstructure:"skip_create_image" required:"false"`
|
||||
}
|
||||
|
||||
func (c *ImageConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
errs := make([]error, 0)
|
||||
if c.ImageName == "" {
|
||||
errs = append(errs, fmt.Errorf("An image_name must be specified"))
|
||||
}
|
||||
|
||||
// By default, OpenStack seems to create the image with an image_type of
|
||||
// "snapshot", since it came from snapshotting a VM. A "snapshot" looks
|
||||
// slightly different in the OpenStack UI and OpenStack won't show
|
||||
// "snapshot" images as a choice in the list of images to boot from for a
|
||||
// new instance. See https://github.com/hashicorp/packer/issues/3038
|
||||
if c.ImageMetadata == nil {
|
||||
c.ImageMetadata = map[string]string{"image_type": "image"}
|
||||
} else if c.ImageMetadata["image_type"] == "" {
|
||||
c.ImageMetadata["image_type"] = "image"
|
||||
}
|
||||
|
||||
// ImageVisibility values
|
||||
// https://wiki.openstack.org/wiki/Glance-v2-community-image-visibility-design
|
||||
if c.ImageVisibility != "" {
|
||||
validVals := []imageservice.ImageVisibility{"public", "private", "shared", "community"}
|
||||
valid := false
|
||||
for _, val := range validVals {
|
||||
if strings.EqualFold(string(c.ImageVisibility), string(val)) {
|
||||
valid = true
|
||||
c.ImageVisibility = val
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
errs = append(errs, fmt.Errorf("Unknown visibility value %s", c.ImageVisibility))
|
||||
}
|
||||
}
|
||||
|
||||
if c.ImageMinDisk < 0 {
|
||||
errs = append(errs, fmt.Errorf("An image min disk size must be greater than or equal to 0"))
|
||||
}
|
||||
|
||||
if len(errs) > 0 {
|
||||
return errs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
180
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/networks.go
generated
vendored
Normal file
180
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/networks.go
generated
vendored
Normal file
|
@ -0,0 +1,180 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/attachinterfaces"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/external"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/subnets"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
)
|
||||
|
||||
// CheckFloatingIP gets a floating IP by its ID and checks if it is already
|
||||
// associated with any internal interface.
|
||||
// It returns floating IP if it can be used.
|
||||
func CheckFloatingIP(client *gophercloud.ServiceClient, id string) (*floatingips.FloatingIP, error) {
|
||||
floatingIP, err := floatingips.Get(client, id).Extract()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if floatingIP.PortID != "" {
|
||||
return nil, fmt.Errorf("provided floating IP '%s' is already associated with port '%s'",
|
||||
id, floatingIP.PortID)
|
||||
}
|
||||
|
||||
return floatingIP, nil
|
||||
}
|
||||
|
||||
// FindFreeFloatingIP returns free unassociated floating IP.
|
||||
// It will return first floating IP if there are many.
|
||||
func FindFreeFloatingIP(client *gophercloud.ServiceClient) (*floatingips.FloatingIP, error) {
|
||||
var freeFloatingIP *floatingips.FloatingIP
|
||||
|
||||
pager := floatingips.List(client, floatingips.ListOpts{
|
||||
Status: "DOWN",
|
||||
})
|
||||
err := pager.EachPage(func(page pagination.Page) (bool, error) {
|
||||
candidates, err := floatingips.ExtractFloatingIPs(page)
|
||||
if err != nil {
|
||||
return false, err // stop and throw error out
|
||||
}
|
||||
|
||||
for _, candidate := range candidates {
|
||||
if candidate.PortID != "" {
|
||||
continue // this floating IP is associated with port, move to next in list
|
||||
}
|
||||
|
||||
// Floating IP is able to be allocated.
|
||||
freeFloatingIP = &candidate
|
||||
return false, nil // stop iterating over pages
|
||||
}
|
||||
return true, nil // try the next page
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if freeFloatingIP == nil {
|
||||
return nil, fmt.Errorf("no free floating IPs found")
|
||||
}
|
||||
|
||||
return freeFloatingIP, nil
|
||||
}
|
||||
|
||||
// GetInstancePortID returns internal port of the instance that can be used for
|
||||
// the association of a floating IP.
|
||||
// It will return an ID of a first port if there are many.
|
||||
func GetInstancePortID(client *gophercloud.ServiceClient, id string, instance_float_net string) (string, error) {
|
||||
|
||||
selected_interface := 0
|
||||
|
||||
interfacesPage, err := attachinterfaces.List(client, id).AllPages()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
interfaces, err := attachinterfaces.ExtractInterfaces(interfacesPage)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(interfaces) == 0 {
|
||||
return "", fmt.Errorf("instance '%s' has no interfaces", id)
|
||||
}
|
||||
|
||||
for i := 0; i < len(interfaces); i++ {
|
||||
log.Printf("Instance interface: %v: %+v\n", i, interfaces[i])
|
||||
if interfaces[i].NetID == instance_float_net {
|
||||
log.Printf("Found preferred interface: %v\n", i)
|
||||
selected_interface = i
|
||||
log.Printf("Using interface value: %v", selected_interface)
|
||||
}
|
||||
}
|
||||
|
||||
return interfaces[selected_interface].PortID, nil
|
||||
}
|
||||
|
||||
// CheckFloatingIPNetwork checks provided network reference and returns a valid
|
||||
// Networking service ID.
|
||||
func CheckFloatingIPNetwork(client *gophercloud.ServiceClient, networkRef string) (string, error) {
|
||||
if _, err := uuid.Parse(networkRef); err != nil {
|
||||
return GetFloatingIPNetworkIDByName(client, networkRef)
|
||||
}
|
||||
|
||||
return networkRef, nil
|
||||
}
|
||||
|
||||
// ExternalNetwork is a network with external router.
|
||||
type ExternalNetwork struct {
|
||||
networks.Network
|
||||
external.NetworkExternalExt
|
||||
}
|
||||
|
||||
// GetFloatingIPNetworkIDByName searches for the external network ID by the provided name.
|
||||
func GetFloatingIPNetworkIDByName(client *gophercloud.ServiceClient, networkName string) (string, error) {
|
||||
var externalNetworks []ExternalNetwork
|
||||
|
||||
allPages, err := networks.List(client, networks.ListOpts{
|
||||
Name: networkName,
|
||||
}).AllPages()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := networks.ExtractNetworksInto(allPages, &externalNetworks); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(externalNetworks) == 0 {
|
||||
return "", fmt.Errorf("can't find external network %s", networkName)
|
||||
}
|
||||
// Check and return the first external network.
|
||||
if !externalNetworks[0].External {
|
||||
return "", fmt.Errorf("network %s is not external", networkName)
|
||||
}
|
||||
|
||||
return externalNetworks[0].ID, nil
|
||||
}
|
||||
|
||||
// DiscoverProvisioningNetwork finds the first network whose subnet matches the given network ranges.
|
||||
func DiscoverProvisioningNetwork(client *gophercloud.ServiceClient, cidrs []string) (string, error) {
|
||||
allPages, err := subnets.List(client, subnets.ListOpts{}).AllPages()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
allSubnets, err := subnets.ExtractSubnets(allPages)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, subnet := range allSubnets {
|
||||
_, tenantIPNet, err := net.ParseCIDR(subnet.CIDR)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for _, cidr := range cidrs {
|
||||
_, candidateIPNet, err := net.ParseCIDR(cidr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if containsNet(candidateIPNet, tenantIPNet) {
|
||||
return subnet.NetworkID, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("failed to discover a provisioning network")
|
||||
}
|
||||
|
||||
// containsNet returns true whenever IPNet `a` contains IPNet `b`
|
||||
func containsNet(a *net.IPNet, b *net.IPNet) bool {
|
||||
aMask, _ := a.Mask.Size()
|
||||
bMask, _ := b.Mask.Size()
|
||||
return a.Contains(b.IP) && aMask <= bMask
|
||||
}
|
345
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/run_config.go
generated
vendored
Normal file
345
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/run_config.go
generated
vendored
Normal file
|
@ -0,0 +1,345 @@
|
|||
//go:generate packer-sdc struct-markdown
|
||||
|
||||
package openstack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/template/interpolate"
|
||||
"github.com/hashicorp/packer-plugin-sdk/uuid"
|
||||
)
|
||||
|
||||
// RunConfig contains configuration for running an instance from a source image
|
||||
// and details on how to access that launched image.
|
||||
type RunConfig struct {
|
||||
Comm communicator.Config `mapstructure:",squash"`
|
||||
// The type of interface to connect via SSH. Values useful for Rackspace
|
||||
// are "public" or "private", and the default behavior is to connect via
|
||||
// whichever is returned first from the OpenStack API.
|
||||
SSHInterface string `mapstructure:"ssh_interface" required:"false"`
|
||||
// The IP version to use for SSH connections, valid values are `4` and `6`.
|
||||
// Useful on dual stacked instances where the default behavior is to
|
||||
// connect via whichever IP address is returned first from the OpenStack
|
||||
// API.
|
||||
SSHIPVersion string `mapstructure:"ssh_ip_version" required:"false"`
|
||||
// The ID or full URL to the base image to use. This is the image that will
|
||||
// be used to launch a new server and provision it. Unless you specify
|
||||
// completely custom SSH settings, the source image must have cloud-init
|
||||
// installed so that the keypair gets assigned properly.
|
||||
SourceImage string `mapstructure:"source_image" required:"true"`
|
||||
// The name of the base image to use. This is an alternative way of
|
||||
// providing source_image and only either of them can be specified.
|
||||
SourceImageName string `mapstructure:"source_image_name" required:"true"`
|
||||
// The URL of an external base image to use. This is an alternative way of
|
||||
// providing source_image and only either of them can be specified.
|
||||
ExternalSourceImageURL string `mapstructure:"external_source_image_url" required:"true"`
|
||||
// The format of the external source image to use, e.g. qcow2, raw.
|
||||
ExternalSourceImageFormat string `mapstructure:"external_source_image_format" required:"false"`
|
||||
// Properties to set for the external source image
|
||||
ExternalSourceImageProperties map[string]string `mapstructure:"external_source_image_properties" required:"false"`
|
||||
// Filters used to populate filter options. Example:
|
||||
//
|
||||
// ```json
|
||||
//{
|
||||
// "source_image_filter": {
|
||||
// "filters": {
|
||||
// "name": "ubuntu-16.04",
|
||||
// "visibility": "protected",
|
||||
// "owner": "d1a588cf4b0743344508dc145649372d1",
|
||||
// "tags": ["prod", "ready"],
|
||||
// "properties": {
|
||||
// "os_distro": "ubuntu"
|
||||
// }
|
||||
// },
|
||||
// "most_recent": true
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// This selects the most recent production Ubuntu 16.04 shared to you by
|
||||
// the given owner. NOTE: This will fail unless *exactly* one image is
|
||||
// returned, or `most_recent` is set to true. In the example of multiple
|
||||
// returned images, `most_recent` will cause this to succeed by selecting
|
||||
// the newest image of the returned images.
|
||||
//
|
||||
// - `filters` (map of strings) - filters used to select a
|
||||
// `source_image`.
|
||||
// NOTE: This will fail unless *exactly* one image is returned, or
|
||||
// `most_recent` is set to true. Of the filters described in
|
||||
// [ImageService](https://developer.openstack.org/api-ref/image/v2/), the
|
||||
// following are valid:
|
||||
//
|
||||
// - name (string)
|
||||
// - owner (string)
|
||||
// - tags (array of strings)
|
||||
// - visibility (string)
|
||||
// - properties (map of strings to strings) (fields that can be set
|
||||
// with `openstack image set --property key=value`)
|
||||
//
|
||||
// - `most_recent` (boolean) - Selects the newest created image when
|
||||
// true.
|
||||
// This is most useful for selecting a daily distro build.
|
||||
//
|
||||
// You may set use this in place of `source_image` If `source_image_filter`
|
||||
// is provided alongside `source_image`, the `source_image` will override
|
||||
// the filter. The filter will not be used in this case.
|
||||
SourceImageFilters ImageFilter `mapstructure:"source_image_filter" required:"true"`
|
||||
// The ID, name, or full URL for the desired flavor for the server to be
|
||||
// created.
|
||||
Flavor string `mapstructure:"flavor" required:"true"`
|
||||
// The availability zone to launch the server in. If this isn't specified,
|
||||
// the default enforced by your OpenStack cluster will be used. This may be
|
||||
// required for some OpenStack clusters.
|
||||
AvailabilityZone string `mapstructure:"availability_zone" required:"false"`
|
||||
// For rackspace, whether or not to wait for Rackconnect to assign the
|
||||
// machine an IP address before connecting via SSH. Defaults to false.
|
||||
RackconnectWait bool `mapstructure:"rackconnect_wait" required:"false"`
|
||||
// The ID or name of an external network that can be used for creation of a
|
||||
// new floating IP.
|
||||
FloatingIPNetwork string `mapstructure:"floating_ip_network" required:"false"`
|
||||
// The ID of the network to which the instance is attached and which should
|
||||
// be used to associate with the floating IP. This provides control over
|
||||
// the floating ip association on multi-homed instances. The association
|
||||
// otherwise depends on a first-returned-interface policy which could fail
|
||||
// if the network to which it is connected is unreachable from the floating
|
||||
// IP network.
|
||||
InstanceFloatingIPNet string `mapstructure:"instance_floating_ip_net" required:"false"`
|
||||
// A specific floating IP to assign to this instance.
|
||||
FloatingIP string `mapstructure:"floating_ip" required:"false"`
|
||||
// Whether or not to attempt to reuse existing unassigned floating ips in
|
||||
// the project before allocating a new one. Note that it is not possible to
|
||||
// safely do this concurrently, so if you are running multiple openstack
|
||||
// builds concurrently, or if other processes are assigning and using
|
||||
// floating IPs in the same openstack project while packer is running, you
|
||||
// should not set this to true. Defaults to false.
|
||||
ReuseIPs bool `mapstructure:"reuse_ips" required:"false"`
|
||||
// A list of security groups by name to add to this instance.
|
||||
SecurityGroups []string `mapstructure:"security_groups" required:"false"`
|
||||
// A list of networks by UUID to attach to this instance.
|
||||
Networks []string `mapstructure:"networks" required:"false"`
|
||||
// A list of ports by UUID to attach to this instance.
|
||||
Ports []string `mapstructure:"ports" required:"false"`
|
||||
// A list of network CIDRs to discover the network to attach to this instance.
|
||||
// The first network whose subnet is contained within any of the given CIDRs
|
||||
// is used. Ignored if either of the above two options are provided.
|
||||
NetworkDiscoveryCIDRs []string `mapstructure:"network_discovery_cidrs" required:"false"`
|
||||
// User data to apply when launching the instance. Note that you need to be
|
||||
// careful about escaping characters due to the templates being JSON. It is
|
||||
// often more convenient to use user_data_file, instead. Packer will not
|
||||
// automatically wait for a user script to finish before shutting down the
|
||||
// instance this must be handled in a provisioner.
|
||||
UserData string `mapstructure:"user_data" required:"false"`
|
||||
// Path to a file that will be used for the user data when launching the
|
||||
// instance.
|
||||
UserDataFile string `mapstructure:"user_data_file" required:"false"`
|
||||
// Name that is applied to the server instance created by Packer. If this
|
||||
// isn't specified, the default is same as image_name.
|
||||
InstanceName string `mapstructure:"instance_name" required:"false"`
|
||||
// Metadata that is applied to the server instance created by Packer. Also
|
||||
// called server properties in some documentation. The strings have a max
|
||||
// size of 255 bytes each.
|
||||
InstanceMetadata map[string]string `mapstructure:"instance_metadata" required:"false"`
|
||||
// Whether to force the OpenStack instance to be forcefully deleted. This
|
||||
// is useful for environments that have reclaim / soft deletion enabled. By
|
||||
// default this is false.
|
||||
ForceDelete bool `mapstructure:"force_delete" required:"false"`
|
||||
// Whether or not nova should use ConfigDrive for cloud-init metadata.
|
||||
ConfigDrive bool `mapstructure:"config_drive" required:"false"`
|
||||
// Deprecated use floating_ip_network instead.
|
||||
FloatingIPPool string `mapstructure:"floating_ip_pool" required:"false"`
|
||||
// Use Block Storage service volume for the instance root volume instead of
|
||||
// Compute service local volume (default).
|
||||
UseBlockStorageVolume bool `mapstructure:"use_blockstorage_volume" required:"false"`
|
||||
// Name of the Block Storage service volume. If this isn't specified,
|
||||
// random string will be used.
|
||||
VolumeName string `mapstructure:"volume_name" required:"false"`
|
||||
// Type of the Block Storage service volume. If this isn't specified, the
|
||||
// default enforced by your OpenStack cluster will be used.
|
||||
VolumeType string `mapstructure:"volume_type" required:"false"`
|
||||
// Size of the Block Storage service volume in GB. If this isn't specified,
|
||||
// it is set to source image min disk value (if set) or calculated from the
|
||||
// source image bytes size. Note that in some cases this needs to be
|
||||
// specified, if use_blockstorage_volume is true.
|
||||
VolumeSize int `mapstructure:"volume_size" required:"false"`
|
||||
// Availability zone of the Block Storage service volume. If omitted,
|
||||
// Compute instance availability zone will be used. If both of Compute
|
||||
// instance and Block Storage volume availability zones aren't specified,
|
||||
// the default enforced by your OpenStack cluster will be used.
|
||||
VolumeAvailabilityZone string `mapstructure:"volume_availability_zone" required:"false"`
|
||||
|
||||
// Not really used, but here for BC
|
||||
OpenstackProvider string `mapstructure:"openstack_provider"`
|
||||
// *Deprecated* use `floating_ip` or `floating_ip_pool` instead.
|
||||
UseFloatingIp bool `mapstructure:"use_floating_ip" required:"false"`
|
||||
|
||||
sourceImageOpts images.ListOpts
|
||||
}
|
||||
|
||||
type ImageFilter struct {
|
||||
// filters used to select a source_image. NOTE: This will fail unless
|
||||
// exactly one image is returned, or most_recent is set to true. Of the
|
||||
// filters described in ImageService, the following are valid:
|
||||
Filters ImageFilterOptions `mapstructure:"filters" required:"false"`
|
||||
// Selects the newest created image when true. This is most useful for
|
||||
// selecting a daily distro build.
|
||||
MostRecent bool `mapstructure:"most_recent" required:"false"`
|
||||
}
|
||||
|
||||
type ImageFilterOptions struct {
|
||||
Name string `mapstructure:"name"`
|
||||
Owner string `mapstructure:"owner"`
|
||||
Tags []string `mapstructure:"tags"`
|
||||
Visibility string `mapstructure:"visibility"`
|
||||
Properties map[string]string `mapstructure:"properties"`
|
||||
}
|
||||
|
||||
func (f *ImageFilterOptions) Empty() bool {
|
||||
return f.Name == "" && f.Owner == "" && len(f.Tags) == 0 && f.Visibility == "" && len(f.Properties) == 0
|
||||
}
|
||||
|
||||
func (f *ImageFilterOptions) Build() (*images.ListOpts, error) {
|
||||
opts := images.ListOpts{}
|
||||
// Set defaults for status, member_status, and sort
|
||||
opts.Status = images.ImageStatusActive
|
||||
opts.MemberStatus = images.ImageMemberStatusAccepted
|
||||
opts.Sort = "created_at:desc"
|
||||
|
||||
var err error
|
||||
|
||||
if f.Name != "" {
|
||||
opts.Name = f.Name
|
||||
}
|
||||
if f.Owner != "" {
|
||||
opts.Owner = f.Owner
|
||||
}
|
||||
if len(f.Tags) > 0 {
|
||||
opts.Tags = f.Tags
|
||||
}
|
||||
if f.Visibility != "" {
|
||||
v, err := getImageVisibility(f.Visibility)
|
||||
if err == nil {
|
||||
opts.Visibility = *v
|
||||
}
|
||||
}
|
||||
|
||||
return &opts, err
|
||||
}
|
||||
|
||||
func (c *RunConfig) Prepare(ctx *interpolate.Context) []error {
|
||||
// If we are not given an explicit ssh_keypair_name or
|
||||
// ssh_private_key_file, then create a temporary one, but only if the
|
||||
// temporary_key_pair_name has not been provided and we are not using
|
||||
// ssh_password.
|
||||
if c.Comm.SSHKeyPairName == "" && c.Comm.SSHTemporaryKeyPairName == "" &&
|
||||
c.Comm.SSHPrivateKeyFile == "" && c.Comm.SSHPassword == "" {
|
||||
|
||||
c.Comm.SSHTemporaryKeyPairName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
|
||||
}
|
||||
|
||||
if c.FloatingIPPool != "" && c.FloatingIPNetwork == "" {
|
||||
c.FloatingIPNetwork = c.FloatingIPPool
|
||||
}
|
||||
|
||||
// Validation
|
||||
errs := c.Comm.Prepare(ctx)
|
||||
|
||||
if c.Comm.SSHKeyPairName != "" {
|
||||
if c.Comm.Type == "winrm" && c.Comm.WinRMPassword == "" && c.Comm.SSHPrivateKeyFile == "" {
|
||||
errs = append(errs, errors.New("A ssh_private_key_file must be provided to retrieve the winrm password when using ssh_keypair_name."))
|
||||
} else if c.Comm.SSHPrivateKeyFile == "" && !c.Comm.SSHAgentAuth {
|
||||
errs = append(errs, errors.New("A ssh_private_key_file must be provided or ssh_agent_auth enabled when ssh_keypair_name is specified."))
|
||||
}
|
||||
}
|
||||
|
||||
if c.SourceImage == "" && c.SourceImageName == "" && c.ExternalSourceImageURL == "" && c.SourceImageFilters.Filters.Empty() {
|
||||
errs = append(errs, errors.New("Either a source_image, a source_image_name, an external_source_image_url or source_image_filter must be specified"))
|
||||
} else {
|
||||
// Make sure we've only set one image source option
|
||||
thereCanBeOnlyOne := []bool{len(c.SourceImageName) > 0, len(c.SourceImage) > 0, len(c.ExternalSourceImageURL) > 0, !c.SourceImageFilters.Filters.Empty()}
|
||||
numSet := 0
|
||||
for _, val := range thereCanBeOnlyOne {
|
||||
if val {
|
||||
numSet += 1
|
||||
}
|
||||
}
|
||||
|
||||
if numSet > 1 {
|
||||
errs = append(errs, errors.New("Only one of the options source_image, source_image_name, external_source_image_url, or source_image_filter can be specified, not multiple."))
|
||||
}
|
||||
}
|
||||
|
||||
// if external_source_image_format is not set use qcow2 as default
|
||||
if c.ExternalSourceImageFormat == "" {
|
||||
c.ExternalSourceImageFormat = "qcow2"
|
||||
}
|
||||
|
||||
if c.Flavor == "" {
|
||||
errs = append(errs, errors.New("A flavor must be specified"))
|
||||
}
|
||||
|
||||
if c.SSHIPVersion != "" && c.SSHIPVersion != "4" && c.SSHIPVersion != "6" {
|
||||
errs = append(errs, errors.New("SSH IP version must be either 4 or 6"))
|
||||
}
|
||||
|
||||
for key, value := range c.InstanceMetadata {
|
||||
if len(key) > 255 {
|
||||
errs = append(errs, fmt.Errorf("Instance metadata key too long (max 255 bytes): %s", key))
|
||||
}
|
||||
if len(value) > 255 {
|
||||
errs = append(errs, fmt.Errorf("Instance metadata value too long (max 255 bytes): %s", value))
|
||||
}
|
||||
}
|
||||
|
||||
if c.UseBlockStorageVolume {
|
||||
// Use Compute instance availability zone for the Block Storage volume
|
||||
// if it's not provided.
|
||||
if c.VolumeAvailabilityZone == "" {
|
||||
c.VolumeAvailabilityZone = c.AvailabilityZone
|
||||
}
|
||||
|
||||
// Use random name for the Block Storage volume if it's not provided.
|
||||
if c.VolumeName == "" {
|
||||
c.VolumeName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
|
||||
}
|
||||
}
|
||||
|
||||
// if neither ID, image name or external image URL is provided outside the filter,
|
||||
// build the filter
|
||||
if len(c.SourceImage) == 0 && len(c.SourceImageName) == 0 && len(c.ExternalSourceImageURL) == 0 {
|
||||
|
||||
listOpts, filterErr := c.SourceImageFilters.Filters.Build()
|
||||
|
||||
if filterErr != nil {
|
||||
errs = append(errs, filterErr)
|
||||
}
|
||||
c.sourceImageOpts = *listOpts
|
||||
}
|
||||
|
||||
// if c.ExternalSourceImageURL is set use a generated source image name
|
||||
if c.ExternalSourceImageURL != "" {
|
||||
c.SourceImageName = fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Retrieve the specific ImageVisibility using the exported const from images
|
||||
func getImageVisibility(visibility string) (*images.ImageVisibility, error) {
|
||||
visibilities := [...]images.ImageVisibility{
|
||||
images.ImageVisibilityPublic,
|
||||
images.ImageVisibilityPrivate,
|
||||
images.ImageVisibilityCommunity,
|
||||
images.ImageVisibilityShared,
|
||||
}
|
||||
|
||||
for _, v := range visibilities {
|
||||
if string(v) == visibility {
|
||||
return &v, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("Not a valid visibility: %s", visibility)
|
||||
}
|
93
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/server.go
generated
vendored
Normal file
93
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/server.go
generated
vendored
Normal file
|
@ -0,0 +1,93 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
// StateRefreshFunc is a function type used for StateChangeConf that is
|
||||
// responsible for refreshing the item being watched for a state change.
|
||||
//
|
||||
// It returns three results. `result` is any object that will be returned
|
||||
// as the final object after waiting for state change. This allows you to
|
||||
// return the final updated object, for example an openstack instance after
|
||||
// refreshing it.
|
||||
//
|
||||
// `state` is the latest state of that object. And `err` is any error that
|
||||
// may have happened while refreshing the state.
|
||||
type StateRefreshFunc func() (result interface{}, state string, progress int, err error)
|
||||
|
||||
// StateChangeConf is the configuration struct used for `WaitForState`.
|
||||
type StateChangeConf struct {
|
||||
Pending []string
|
||||
Refresh StateRefreshFunc
|
||||
StepState multistep.StateBag
|
||||
Target []string
|
||||
}
|
||||
|
||||
// ServerStateRefreshFunc returns a StateRefreshFunc that is used to watch
|
||||
// an openstack server.
|
||||
func ServerStateRefreshFunc(
|
||||
client *gophercloud.ServiceClient, s *servers.Server) StateRefreshFunc {
|
||||
return func() (interface{}, string, int, error) {
|
||||
serverNew, err := servers.Get(client, s.ID).Extract()
|
||||
if err != nil {
|
||||
if _, ok := err.(gophercloud.ErrDefault404); ok {
|
||||
log.Printf("[INFO] 404 on ServerStateRefresh, returning DELETED")
|
||||
return nil, "DELETED", 0, nil
|
||||
}
|
||||
log.Printf("[ERROR] Error on ServerStateRefresh: %s", err)
|
||||
return nil, "", 0, err
|
||||
}
|
||||
|
||||
return serverNew, serverNew.Status, serverNew.Progress, nil
|
||||
}
|
||||
}
|
||||
|
||||
// WaitForState watches an object and waits for it to achieve a certain
|
||||
// state.
|
||||
func WaitForState(conf *StateChangeConf) (i interface{}, err error) {
|
||||
log.Printf("Waiting for state to become: %s", conf.Target)
|
||||
|
||||
for {
|
||||
var currentProgress int
|
||||
var currentState string
|
||||
i, currentState, currentProgress, err = conf.Refresh()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, t := range conf.Target {
|
||||
if currentState == t {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if conf.StepState != nil {
|
||||
if _, ok := conf.StepState.GetOk(multistep.StateCancelled); ok {
|
||||
return nil, errors.New("interrupted")
|
||||
}
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, allowed := range conf.Pending {
|
||||
if currentState == allowed {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return nil, fmt.Errorf("unexpected state '%s', wanted target '%s'", currentState, conf.Target)
|
||||
}
|
||||
|
||||
log.Printf("Waiting for state to become: %s currently %s (%d%%)", conf.Target, currentState, currentProgress)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
116
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/ssh.go
generated
vendored
Normal file
116
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/ssh.go
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
)
|
||||
|
||||
// CommHost looks up the host for the communicator.
|
||||
func CommHost(
|
||||
host string,
|
||||
client *gophercloud.ServiceClient,
|
||||
sshinterface string,
|
||||
sshipversion string) func(multistep.StateBag) (string, error) {
|
||||
return func(state multistep.StateBag) (string, error) {
|
||||
if host != "" {
|
||||
log.Printf("Using host value: %s", host)
|
||||
return host, nil
|
||||
}
|
||||
|
||||
s := state.Get("server").(*servers.Server)
|
||||
|
||||
// If we have a specific interface, try that
|
||||
if sshinterface != "" {
|
||||
if addr := sshAddrFromPool(s, sshinterface, sshipversion); addr != "" {
|
||||
log.Printf("[DEBUG] Using IP address %s from specified interface %s to connect", addr, sshinterface)
|
||||
return addr, nil
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a floating IP, use that
|
||||
ip := state.Get("access_ip").(*floatingips.FloatingIP)
|
||||
if ip != nil && ip.FloatingIP != "" {
|
||||
log.Printf("[DEBUG] Using floating IP %s to connect", ip.FloatingIP)
|
||||
return ip.FloatingIP, nil
|
||||
}
|
||||
|
||||
if s.AccessIPv4 != "" {
|
||||
log.Printf("[DEBUG] Using AccessIPv4 %s to connect", s.AccessIPv4)
|
||||
return s.AccessIPv4, nil
|
||||
}
|
||||
|
||||
// Try to get it from the requested interface
|
||||
if addr := sshAddrFromPool(s, sshinterface, sshipversion); addr != "" {
|
||||
log.Printf("[DEBUG] Using IP address %s to connect", addr)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
s, err := servers.Get(client, s.ID).Extract()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
state.Put("server", s)
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
return "", errors.New("couldn't determine IP address for server")
|
||||
}
|
||||
}
|
||||
|
||||
func sshAddrFromPool(s *servers.Server, desired string, sshIPVersion string) string {
|
||||
// Get all the addresses associated with this server. This
|
||||
// was taken directly from Terraform.
|
||||
for pool, networkAddresses := range s.Addresses {
|
||||
// If we have an SSH interface specified, skip it if no match
|
||||
if desired != "" && pool != desired {
|
||||
log.Printf(
|
||||
"[INFO] Skipping pool %s, doesn't match requested %s",
|
||||
pool, desired)
|
||||
continue
|
||||
}
|
||||
|
||||
elements, ok := networkAddresses.([]interface{})
|
||||
if !ok {
|
||||
log.Printf(
|
||||
"[ERROR] Unknown return type for address field: %#v",
|
||||
networkAddresses)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, element := range elements {
|
||||
var addr string
|
||||
address := element.(map[string]interface{})
|
||||
if address["OS-EXT-IPS:type"] == "floating" {
|
||||
addr = address["addr"].(string)
|
||||
} else if sshIPVersion == "4" {
|
||||
if address["version"].(float64) == 4 {
|
||||
addr = address["addr"].(string)
|
||||
}
|
||||
} else if sshIPVersion == "6" {
|
||||
if address["version"].(float64) == 6 {
|
||||
addr = fmt.Sprintf("[%s]", address["addr"].(string))
|
||||
}
|
||||
} else {
|
||||
if address["version"].(float64) == 6 {
|
||||
addr = fmt.Sprintf("[%s]", address["addr"].(string))
|
||||
} else {
|
||||
addr = address["addr"].(string)
|
||||
}
|
||||
}
|
||||
|
||||
if addr != "" {
|
||||
log.Printf("[DEBUG] Detected address: %s", addr)
|
||||
return addr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
63
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_add_image_members.go
generated
vendored
Normal file
63
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_add_image_members.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/members"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepAddImageMembers struct{}
|
||||
|
||||
func (s *stepAddImageMembers) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
if config.SkipCreateImage {
|
||||
ui.Say("Skipping image add members...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
imageId := state.Get("image").(string)
|
||||
|
||||
if len(config.ImageMembers) == 0 {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
imageClient, err := config.imageV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing image service client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
for _, member := range config.ImageMembers {
|
||||
ui.Say(fmt.Sprintf("Adding member '%s' to image %s", member, imageId))
|
||||
r := members.Create(imageClient, imageId, member)
|
||||
if _, err = r.Extract(); err != nil {
|
||||
err = fmt.Errorf("Error adding member to image: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
if config.ImageAutoAcceptMembers {
|
||||
for _, member := range config.ImageMembers {
|
||||
ui.Say(fmt.Sprintf("Accepting image %s for member '%s'", imageId, member))
|
||||
r := members.Update(imageClient, imageId, member, members.UpdateOpts{Status: "accepted"})
|
||||
if _, err = r.Extract(); err != nil {
|
||||
err = fmt.Errorf("Error accepting image for member: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepAddImageMembers) Cleanup(multistep.StateBag) {
|
||||
// No cleanup...
|
||||
}
|
178
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_allocate_ip.go
generated
vendored
Normal file
178
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_allocate_ip.go
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/floatingips"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepAllocateIp struct {
|
||||
FloatingIPNetwork string
|
||||
FloatingIP string
|
||||
ReuseIPs bool
|
||||
InstanceFloatingIPNet string
|
||||
}
|
||||
|
||||
func (s *StepAllocateIp) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
server := state.Get("server").(*servers.Server)
|
||||
|
||||
var instanceIP floatingips.FloatingIP
|
||||
|
||||
// This is here in case we error out before putting instanceIp into the
|
||||
// statebag below, because it is requested by Cleanup()
|
||||
state.Put("access_ip", &instanceIP)
|
||||
|
||||
if s.FloatingIP == "" && !s.ReuseIPs && s.FloatingIPNetwork == "" {
|
||||
ui.Message("Floating IP not required")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// We need the v2 compute client
|
||||
computeClient, err := config.computeV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing compute client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// We need the v2 network client
|
||||
networkClient, err := config.networkV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing network client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Try to Use the OpenStack floating IP by checking provided parameters in
|
||||
// the following order:
|
||||
// - try to use "FloatingIP" ID directly if it's provided
|
||||
// - try to find free floating IP in the project if "ReuseIPs" is set
|
||||
// - create a new floating IP if "FloatingIPNetwork" is provided (it can be
|
||||
// ID or name of the network).
|
||||
if s.FloatingIP != "" {
|
||||
// Try to use FloatingIP if it was provided by the user.
|
||||
freeFloatingIP, err := CheckFloatingIP(networkClient, s.FloatingIP)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error using provided floating IP '%s': %s", s.FloatingIP, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
instanceIP = *freeFloatingIP
|
||||
ui.Message(fmt.Sprintf("Selected floating IP: '%s' (%s)", instanceIP.ID, instanceIP.FloatingIP))
|
||||
state.Put("floatingip_istemp", false)
|
||||
} else if s.ReuseIPs {
|
||||
// If ReuseIPs is set to true and we have a free floating IP, use it rather
|
||||
// than creating one.
|
||||
ui.Say(fmt.Sprint("Searching for unassociated floating IP"))
|
||||
freeFloatingIP, err := FindFreeFloatingIP(networkClient)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error searching for floating IP: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
instanceIP = *freeFloatingIP
|
||||
ui.Message(fmt.Sprintf("Selected floating IP: '%s' (%s)", instanceIP.ID, instanceIP.FloatingIP))
|
||||
state.Put("floatingip_istemp", false)
|
||||
} else if s.FloatingIPNetwork != "" {
|
||||
// Lastly, if FloatingIPNetwork was provided by the user, we need to use it
|
||||
// to allocate a new floating IP and associate it to the instance.
|
||||
floatingNetwork, err := CheckFloatingIPNetwork(networkClient, s.FloatingIPNetwork)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error using the provided floating_ip_network: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Creating floating IP using network %s ...", floatingNetwork))
|
||||
newIP, err := floatingips.Create(networkClient, floatingips.CreateOpts{
|
||||
FloatingNetworkID: floatingNetwork,
|
||||
}).Extract()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating floating IP from floating network '%s': %s", floatingNetwork, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
instanceIP = *newIP
|
||||
ui.Message(fmt.Sprintf("Created floating IP: '%s' (%s)", instanceIP.ID, instanceIP.FloatingIP))
|
||||
state.Put("floatingip_istemp", true)
|
||||
}
|
||||
|
||||
// Assoctate a floating IP if it was obtained in the previous steps.
|
||||
if instanceIP.ID != "" {
|
||||
ui.Say(fmt.Sprintf("Associating floating IP '%s' (%s) with instance port...",
|
||||
instanceIP.ID, instanceIP.FloatingIP))
|
||||
|
||||
portID, err := GetInstancePortID(computeClient, server.ID, s.InstanceFloatingIPNet)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error getting interfaces of the instance '%s': %s", server.ID, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
_, err = floatingips.Update(networkClient, instanceIP.ID, floatingips.UpdateOpts{
|
||||
PortID: &portID,
|
||||
}).Extract()
|
||||
if err != nil {
|
||||
err := fmt.Errorf(
|
||||
"Error associating floating IP '%s' (%s) with instance port '%s': %s",
|
||||
instanceIP.ID, instanceIP.FloatingIP, portID, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf(
|
||||
"Added floating IP '%s' (%s) to instance!", instanceIP.ID, instanceIP.FloatingIP))
|
||||
}
|
||||
|
||||
state.Put("access_ip", &instanceIP)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepAllocateIp) Cleanup(state multistep.StateBag) {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
instanceIP := state.Get("access_ip").(*floatingips.FloatingIP)
|
||||
|
||||
// Don't clean up if unless required
|
||||
if instanceIP.ID == "" && instanceIP.FloatingIP == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Don't delete pool addresses we didn't allocate
|
||||
if state.Get("floatingip_istemp") == false {
|
||||
return
|
||||
}
|
||||
|
||||
// We need the v2 network client
|
||||
client, err := config.networkV2Client()
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(
|
||||
"Error deleting temporary floating IP '%s' (%s)", instanceIP.ID, instanceIP.FloatingIP))
|
||||
return
|
||||
}
|
||||
|
||||
if instanceIP.ID != "" {
|
||||
if err := floatingips.Delete(client, instanceIP.ID).ExtractErr(); err != nil {
|
||||
ui.Error(fmt.Sprintf(
|
||||
"Error deleting temporary floating IP '%s' (%s)", instanceIP.ID, instanceIP.FloatingIP))
|
||||
return
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Deleted temporary floating IP '%s' (%s)", instanceIP.ID, instanceIP.FloatingIP))
|
||||
}
|
||||
}
|
152
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_create_image.go
generated
vendored
Normal file
152
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_create_image.go
generated
vendored
Normal file
|
@ -0,0 +1,152 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepCreateImage struct {
|
||||
UseBlockStorageVolume bool
|
||||
}
|
||||
|
||||
func (s *stepCreateImage) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
server := state.Get("server").(*servers.Server)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
if config.SkipCreateImage {
|
||||
ui.Say("Skipping image creation...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// We need the v2 compute client
|
||||
computeClient, err := config.computeV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing compute client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// We need the v2 image client
|
||||
imageClient, err := config.imageV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing image service client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Create the image.
|
||||
// Image source depends on the type of the Compute instance. It can be
|
||||
// Block Storage service volume or regular Compute service local volume.
|
||||
ui.Say(fmt.Sprintf("Creating the image: %s", config.ImageName))
|
||||
var imageId string
|
||||
var blockStorageClient *gophercloud.ServiceClient
|
||||
if s.UseBlockStorageVolume {
|
||||
// We need the v3 block storage client.
|
||||
blockStorageClient, err = config.blockStorageV3Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing block storage client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
volume := state.Get("volume_id").(string)
|
||||
|
||||
// set ImageMetadata before uploading to glance so the new image captured the desired values
|
||||
if len(config.ImageMetadata) > 0 {
|
||||
err = volumeactions.SetImageMetadata(blockStorageClient, volume, volumeactions.ImageMetadataOpts{
|
||||
Metadata: config.ImageMetadata,
|
||||
}).ExtractErr()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error setting image metadata: %s", err)
|
||||
ui.Error(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
image, err := volumeactions.UploadImage(blockStorageClient, volume, volumeactions.UploadImageOpts{
|
||||
DiskFormat: config.ImageDiskFormat,
|
||||
ImageName: config.ImageName,
|
||||
}).Extract()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating image: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
imageId = image.ImageID
|
||||
} else {
|
||||
imageId, err = servers.CreateImage(computeClient, server.ID, servers.CreateImageOpts{
|
||||
Name: config.ImageName,
|
||||
Metadata: config.ImageMetadata,
|
||||
}).ExtractImageID()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating image: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
// Set the Image ID in the state
|
||||
ui.Message(fmt.Sprintf("Image: %s", imageId))
|
||||
state.Put("image", imageId)
|
||||
|
||||
// Wait for the image to become ready
|
||||
ui.Say(fmt.Sprintf("Waiting for image %s (image id: %s) to become ready...", config.ImageName, imageId))
|
||||
if err := WaitForImage(ctx, imageClient, imageId); err != nil {
|
||||
err := fmt.Errorf("Error waiting for image: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepCreateImage) Cleanup(multistep.StateBag) {
|
||||
// No cleanup...
|
||||
}
|
||||
|
||||
// WaitForImage waits for the given Image ID to become ready.
|
||||
func WaitForImage(ctx context.Context, client *gophercloud.ServiceClient, imageId string) error {
|
||||
maxNumErrors := 10
|
||||
numErrors := 0
|
||||
|
||||
for {
|
||||
if err := ctx.Err(); err != nil {
|
||||
return err
|
||||
}
|
||||
image, err := images.Get(client, imageId).Extract()
|
||||
if err != nil {
|
||||
errCode, ok := err.(*gophercloud.ErrUnexpectedResponseCode)
|
||||
if ok && (errCode.Actual == 500 || errCode.Actual == 404) {
|
||||
numErrors++
|
||||
if numErrors >= maxNumErrors {
|
||||
log.Printf("[ERROR] Maximum number of errors (%d) reached; failing with: %s", numErrors, err)
|
||||
return err
|
||||
}
|
||||
log.Printf("[ERROR] %d error received, will ignore and retry: %s", errCode.Actual, err)
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if image.Status == "active" {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("Waiting for image creation status: %s", image.Status)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
134
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_create_volume.go
generated
vendored
Normal file
134
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_create_volume.go
generated
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepCreateVolume struct {
|
||||
UseBlockStorageVolume bool
|
||||
VolumeName string
|
||||
VolumeType string
|
||||
VolumeAvailabilityZone string
|
||||
volumeID string
|
||||
doCleanup bool
|
||||
}
|
||||
|
||||
func (s *StepCreateVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// Proceed only if block storage volume is required.
|
||||
if !s.UseBlockStorageVolume {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
sourceImage := state.Get("source_image").(string)
|
||||
|
||||
// We will need Block Storage and Image services clients.
|
||||
blockStorageClient, err := config.blockStorageV3Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing block storage client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
volumeSize := config.VolumeSize
|
||||
|
||||
// Get needed volume size from the source image.
|
||||
if volumeSize == 0 {
|
||||
imageClient, err := config.imageV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing image client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
volumeSize, err = GetVolumeSize(imageClient, sourceImage)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating volume: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say("Creating volume...")
|
||||
volumeOpts := volumes.CreateOpts{
|
||||
Size: volumeSize,
|
||||
VolumeType: s.VolumeType,
|
||||
AvailabilityZone: s.VolumeAvailabilityZone,
|
||||
Name: s.VolumeName,
|
||||
ImageID: sourceImage,
|
||||
Metadata: config.ImageMetadata,
|
||||
}
|
||||
volume, err := volumes.Create(blockStorageClient, volumeOpts).Extract()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating volume: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Wait for volume to become available.
|
||||
ui.Say(fmt.Sprintf("Waiting for volume %s (volume id: %s) to become available...", config.VolumeName, volume.ID))
|
||||
if err := WaitForVolume(blockStorageClient, volume.ID); err != nil {
|
||||
err := fmt.Errorf("Error waiting for volume: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Volume was created, so remember to clean it up.
|
||||
s.doCleanup = true
|
||||
|
||||
// Set the Volume ID in the state.
|
||||
ui.Message(fmt.Sprintf("Volume ID: %s", volume.ID))
|
||||
state.Put("volume_id", volume.ID)
|
||||
s.volumeID = volume.ID
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepCreateVolume) Cleanup(state multistep.StateBag) {
|
||||
if !s.doCleanup {
|
||||
return
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
blockStorageClient, err := config.blockStorageV3Client()
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(
|
||||
"Error cleaning up volume. Please delete the volume manually: %s", s.volumeID))
|
||||
return
|
||||
}
|
||||
|
||||
// Wait for volume to become available.
|
||||
status, err := GetVolumeStatus(blockStorageClient, s.volumeID)
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(
|
||||
"Error getting the volume information. Please delete the volume manually: %s", s.volumeID))
|
||||
return
|
||||
}
|
||||
|
||||
if status != "available" {
|
||||
ui.Say(fmt.Sprintf(
|
||||
"Waiting for volume %s (volume id: %s) to become available...", s.VolumeName, s.volumeID))
|
||||
if err := WaitForVolume(blockStorageClient, s.volumeID); err != nil {
|
||||
ui.Error(fmt.Sprintf(
|
||||
"Error getting the volume information. Please delete the volume manually: %s", s.volumeID))
|
||||
return
|
||||
}
|
||||
}
|
||||
ui.Say(fmt.Sprintf("Deleting volume: %s ...", s.volumeID))
|
||||
err = volumes.Delete(blockStorageClient, s.volumeID, volumes.DeleteOpts{}).ExtractErr()
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(
|
||||
"Error cleaning up volume. Please delete the volume manually: %s", s.volumeID))
|
||||
}
|
||||
}
|
54
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_detach_volume.go
generated
vendored
Normal file
54
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_detach_volume.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepDetachVolume struct {
|
||||
UseBlockStorageVolume bool
|
||||
}
|
||||
|
||||
func (s *StepDetachVolume) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
// Proceed only if block storage volume is used.
|
||||
if !s.UseBlockStorageVolume {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
blockStorageClient, err := config.blockStorageV3Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing block storage client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
volume := state.Get("volume_id").(string)
|
||||
ui.Say(fmt.Sprintf("Detaching volume %s (volume id: %s)", config.VolumeName, volume))
|
||||
if err := volumeactions.Detach(blockStorageClient, volume, volumeactions.DetachOpts{}).ExtractErr(); err != nil {
|
||||
err = fmt.Errorf("Error detaching block storage volume: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Wait for volume to become available.
|
||||
ui.Say(fmt.Sprintf("Waiting for volume %s (volume id: %s) to become available...", config.VolumeName, volume))
|
||||
if err := WaitForVolume(blockStorageClient, volume); err != nil {
|
||||
err := fmt.Errorf("Error waiting for volume: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepDetachVolume) Cleanup(multistep.StateBag) {
|
||||
// No cleanup.
|
||||
}
|
55
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_discover_network.go
generated
vendored
Normal file
55
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_discover_network.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepDiscoverNetwork struct {
|
||||
Networks []string
|
||||
NetworkDiscoveryCIDRs []string
|
||||
Ports []string
|
||||
}
|
||||
|
||||
func (s *StepDiscoverNetwork) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
networkClient, err := config.networkV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing network client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
networks := []servers.Network{}
|
||||
for _, port := range s.Ports {
|
||||
networks = append(networks, servers.Network{Port: port})
|
||||
}
|
||||
for _, uuid := range s.Networks {
|
||||
networks = append(networks, servers.Network{UUID: uuid})
|
||||
}
|
||||
|
||||
cidrs := s.NetworkDiscoveryCIDRs
|
||||
if len(networks) == 0 && len(cidrs) > 0 {
|
||||
ui.Say(fmt.Sprintf("Discovering provisioning network..."))
|
||||
|
||||
networkID, err := DiscoverProvisioningNetwork(networkClient, cidrs)
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Found network ID: %s", networkID))
|
||||
networks = append(networks, servers.Network{UUID: networkID})
|
||||
}
|
||||
|
||||
state.Put("networks", networks)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepDiscoverNetwork) Cleanup(state multistep.StateBag) {}
|
85
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_get_password.go
generated
vendored
Normal file
85
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_get_password.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// StepGetPassword reads the password from a booted OpenStack server and sets
|
||||
// it on the WinRM config.
|
||||
type StepGetPassword struct {
|
||||
Debug bool
|
||||
Comm *communicator.Config
|
||||
BuildName string
|
||||
}
|
||||
|
||||
func (s *StepGetPassword) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
// Skip if we're not using winrm
|
||||
if s.Comm.Type != "winrm" {
|
||||
log.Printf("[INFO] Not using winrm communicator, skipping get password...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// If we already have a password, skip it
|
||||
if s.Comm.WinRMPassword != "" {
|
||||
ui.Say("Skipping waiting for password since WinRM password set...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// We need the v2 compute client
|
||||
computeClient, err := config.computeV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing compute client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Waiting for password since WinRM password is not set...")
|
||||
server := state.Get("server").(*servers.Server)
|
||||
var password string
|
||||
|
||||
privateKey, err := ssh.ParseRawPrivateKey(s.Comm.SSHPrivateKey)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error parsing private key: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
for ; password == "" && err == nil; password, err = servers.GetPassword(computeClient, server.ID).ExtractPassword(privateKey.(*rsa.PrivateKey)) {
|
||||
|
||||
// Check for an interrupt in between attempts.
|
||||
if _, ok := state.GetOk(multistep.StateCancelled); ok {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
log.Printf("Retrying to get a administrator password evry 5 seconds.")
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Password retrieved!"))
|
||||
s.Comm.WinRMPassword = password
|
||||
|
||||
// In debug-mode, we output the password
|
||||
if s.Debug {
|
||||
ui.Message(fmt.Sprintf(
|
||||
"Password (since debug is enabled) \"%s\"", s.Comm.WinRMPassword))
|
||||
}
|
||||
|
||||
packersdk.LogSecretFilter.Set(s.Comm.WinRMPassword)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepGetPassword) Cleanup(multistep.StateBag) {}
|
190
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_key_pair.go
generated
vendored
Normal file
190
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_key_pair.go
generated
vendored
Normal file
|
@ -0,0 +1,190 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||
"github.com/hashicorp/packer-plugin-sdk/communicator"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
"github.com/hashicorp/packer-plugin-sdk/tmp"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type StepKeyPair struct {
|
||||
Debug bool
|
||||
Comm *communicator.Config
|
||||
DebugKeyPath string
|
||||
|
||||
doCleanup bool
|
||||
}
|
||||
|
||||
func (s *StepKeyPair) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
if s.Comm.SSHPrivateKeyFile != "" {
|
||||
ui.Say("Using existing SSH private key")
|
||||
privateKeyBytes, err := s.Comm.ReadSSHPrivateKeyFile()
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.Comm.SSHPrivateKey = privateKeyBytes
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName == "" {
|
||||
ui.Say("Using SSH Agent with key pair in Source image")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
if s.Comm.SSHAgentAuth && s.Comm.SSHKeyPairName != "" {
|
||||
ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.Comm.SSHKeyPairName))
|
||||
s.Comm.SSHKeyPairName = ""
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
if s.Comm.SSHTemporaryKeyPairName == "" {
|
||||
ui.Say("Not using temporary keypair")
|
||||
s.Comm.SSHKeyPairName = ""
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
// We need the v2 compute client
|
||||
computeClient, err := config.computeV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing compute client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Creating temporary keypair: %s ...", s.Comm.SSHTemporaryKeyPairName))
|
||||
keypair, err := keypairs.Create(computeClient, keypairs.CreateOpts{
|
||||
Name: s.Comm.SSHTemporaryKeyPairName,
|
||||
}).Extract()
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error creating temporary keypair: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if len(keypair.PrivateKey) == 0 {
|
||||
state.Put("error", fmt.Errorf("The temporary keypair returned was blank"))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Created temporary keypair: %s", s.Comm.SSHTemporaryKeyPairName))
|
||||
|
||||
keypair.PrivateKey = string(berToDer([]byte(keypair.PrivateKey), ui))
|
||||
|
||||
// If we're in debug mode, output the private key to the working
|
||||
// directory.
|
||||
if s.Debug {
|
||||
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
|
||||
f, err := os.Create(s.DebugKeyPath)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Write the key out
|
||||
if _, err := f.Write([]byte(keypair.PrivateKey)); err != nil {
|
||||
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Chmod it so that it is SSH ready
|
||||
if runtime.GOOS != "windows" {
|
||||
if err := f.Chmod(0600); err != nil {
|
||||
state.Put("error", fmt.Errorf("Error setting permissions of debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we created a temporary key, so remember to clean it up
|
||||
s.doCleanup = true
|
||||
|
||||
// Set some state data for use in future steps
|
||||
s.Comm.SSHKeyPairName = s.Comm.SSHTemporaryKeyPairName
|
||||
s.Comm.SSHPrivateKey = []byte(keypair.PrivateKey)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Work around for https://github.com/hashicorp/packer/issues/2526
|
||||
func berToDer(ber []byte, ui packersdk.Ui) []byte {
|
||||
// Check if x/crypto/ssh can parse the key
|
||||
_, err := ssh.ParsePrivateKey(ber)
|
||||
if err == nil {
|
||||
return ber
|
||||
}
|
||||
// Can't parse the key, maybe it's BER encoded. Try to convert it with OpenSSL.
|
||||
log.Println("Couldn't parse SSH key, trying work around for [GH-2526].")
|
||||
|
||||
openSslPath, err := exec.LookPath("openssl")
|
||||
if err != nil {
|
||||
log.Println("Couldn't find OpenSSL, aborting work around.")
|
||||
return ber
|
||||
}
|
||||
|
||||
berKey, err := tmp.File("packer-ber-privatekey-")
|
||||
defer os.Remove(berKey.Name())
|
||||
if err != nil {
|
||||
return ber
|
||||
}
|
||||
ioutil.WriteFile(berKey.Name(), ber, os.ModeAppend)
|
||||
derKey, err := tmp.File("packer-der-privatekey-")
|
||||
defer os.Remove(derKey.Name())
|
||||
if err != nil {
|
||||
return ber
|
||||
}
|
||||
|
||||
args := []string{"rsa", "-in", berKey.Name(), "-out", derKey.Name()}
|
||||
log.Printf("Executing: %s %v", openSslPath, args)
|
||||
if err := exec.Command(openSslPath, args...).Run(); err != nil {
|
||||
log.Printf("OpenSSL failed with error: %s", err)
|
||||
return ber
|
||||
}
|
||||
|
||||
der, err := ioutil.ReadFile(derKey.Name())
|
||||
if err != nil {
|
||||
return ber
|
||||
}
|
||||
ui.Say("Successfully converted BER encoded SSH key to DER encoding.")
|
||||
return der
|
||||
}
|
||||
|
||||
func (s *StepKeyPair) Cleanup(state multistep.StateBag) {
|
||||
if !s.doCleanup {
|
||||
return
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
// We need the v2 compute client
|
||||
computeClient, err := config.computeV2Client()
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(
|
||||
"Error cleaning up keypair. Please delete the key manually: %s", s.Comm.SSHTemporaryKeyPairName))
|
||||
return
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Deleting temporary keypair: %s ...", s.Comm.SSHTemporaryKeyPairName))
|
||||
err = keypairs.Delete(computeClient, s.Comm.SSHTemporaryKeyPairName).ExtractErr()
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf(
|
||||
"Error cleaning up keypair. Please delete the key manually: %s", s.Comm.SSHTemporaryKeyPairName))
|
||||
}
|
||||
}
|
63
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_load_flavor.go
generated
vendored
Normal file
63
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_load_flavor.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/flavors"
|
||||
flavors_utils "github.com/gophercloud/utils/openstack/compute/v2/flavors"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
// StepLoadFlavor gets the FlavorRef from a Flavor. It first assumes
|
||||
// that the Flavor is a ref and verifies it. Otherwise, it tries to find
|
||||
// the flavor by name.
|
||||
type StepLoadFlavor struct {
|
||||
Flavor string
|
||||
}
|
||||
|
||||
func (s *StepLoadFlavor) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
// We need the v2 compute client
|
||||
client, err := config.computeV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing compute client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Loading flavor: %s", s.Flavor))
|
||||
log.Printf("[INFO] Loading flavor by ID: %s", s.Flavor)
|
||||
flavor, err := flavors.Get(client, s.Flavor).Extract()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to find flavor by ID: %s", err)
|
||||
geterr := err
|
||||
|
||||
log.Printf("[INFO] Loading flavor by name: %s", s.Flavor)
|
||||
id, err := flavors_utils.IDFromName(client, s.Flavor)
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to find flavor by name: %s", err)
|
||||
err = fmt.Errorf(
|
||||
"Unable to find specified flavor by ID or name!\n\n"+
|
||||
"Error from ID lookup: %s\n\n"+
|
||||
"Error from name lookup: %s",
|
||||
geterr,
|
||||
err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
flavor = &flavors.Flavor{ID: id}
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Verified flavor. ID: %s", flavor.ID))
|
||||
state.Put("flavor_id", flavor.ID)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepLoadFlavor) Cleanup(state multistep.StateBag) {
|
||||
}
|
173
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_run_source_server.go
generated
vendored
Normal file
173
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_run_source_server.go
generated
vendored
Normal file
|
@ -0,0 +1,173 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/bootfromvolume"
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/keypairs"
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepRunSourceServer struct {
|
||||
Name string
|
||||
SecurityGroups []string
|
||||
AvailabilityZone string
|
||||
UserData string
|
||||
UserDataFile string
|
||||
ConfigDrive bool
|
||||
InstanceMetadata map[string]string
|
||||
UseBlockStorageVolume bool
|
||||
ForceDelete bool
|
||||
server *servers.Server
|
||||
}
|
||||
|
||||
func (s *StepRunSourceServer) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
flavor := state.Get("flavor_id").(string)
|
||||
sourceImage := state.Get("source_image").(string)
|
||||
networks := state.Get("networks").([]servers.Network)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
// We need the v2 compute client
|
||||
computeClient, err := config.computeV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing compute client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
userData := []byte(s.UserData)
|
||||
if s.UserDataFile != "" {
|
||||
userData, err = ioutil.ReadFile(s.UserDataFile)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error reading user data file: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say("Launching server...")
|
||||
|
||||
serverOpts := servers.CreateOpts{
|
||||
Name: s.Name,
|
||||
ImageRef: sourceImage,
|
||||
FlavorRef: flavor,
|
||||
SecurityGroups: s.SecurityGroups,
|
||||
Networks: networks,
|
||||
AvailabilityZone: s.AvailabilityZone,
|
||||
UserData: userData,
|
||||
ConfigDrive: &s.ConfigDrive,
|
||||
ServiceClient: computeClient,
|
||||
Metadata: s.InstanceMetadata,
|
||||
}
|
||||
|
||||
var serverOptsExt servers.CreateOptsBuilder
|
||||
|
||||
// Create root volume in the Block Storage service if required.
|
||||
// Add block device mapping v2 to the server create options if required.
|
||||
if s.UseBlockStorageVolume {
|
||||
volume := state.Get("volume_id").(string)
|
||||
blockDeviceMappingV2 := []bootfromvolume.BlockDevice{
|
||||
{
|
||||
BootIndex: 0,
|
||||
DestinationType: bootfromvolume.DestinationVolume,
|
||||
SourceType: bootfromvolume.SourceVolume,
|
||||
UUID: volume,
|
||||
},
|
||||
}
|
||||
// ImageRef and block device mapping is an invalid options combination.
|
||||
serverOpts.ImageRef = ""
|
||||
serverOptsExt = bootfromvolume.CreateOptsExt{
|
||||
CreateOptsBuilder: serverOpts,
|
||||
BlockDevice: blockDeviceMappingV2,
|
||||
}
|
||||
} else {
|
||||
serverOptsExt = serverOpts
|
||||
}
|
||||
|
||||
// Add keypair to the server create options.
|
||||
keyName := config.Comm.SSHKeyPairName
|
||||
if keyName != "" {
|
||||
serverOptsExt = keypairs.CreateOptsExt{
|
||||
CreateOptsBuilder: serverOptsExt,
|
||||
KeyName: keyName,
|
||||
}
|
||||
}
|
||||
|
||||
ui.Say("Launching server...")
|
||||
s.server, err = servers.Create(computeClient, serverOptsExt).Extract()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error launching source server: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Server ID: %s", s.server.ID))
|
||||
log.Printf("server id: %s", s.server.ID)
|
||||
|
||||
ui.Say("Waiting for server to become ready...")
|
||||
stateChange := StateChangeConf{
|
||||
Pending: []string{"BUILD"},
|
||||
Target: []string{"ACTIVE"},
|
||||
Refresh: ServerStateRefreshFunc(computeClient, s.server),
|
||||
StepState: state,
|
||||
}
|
||||
latestServer, err := WaitForState(&stateChange)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error waiting for server (%s) to become ready: %s", s.server.ID, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
s.server = latestServer.(*servers.Server)
|
||||
state.Put("server", s.server)
|
||||
// instance_id is the generic term used so that users can have access to the
|
||||
// instance id inside of the provisioners, used in step_provision.
|
||||
state.Put("instance_id", s.server.ID)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepRunSourceServer) Cleanup(state multistep.StateBag) {
|
||||
if s.server == nil {
|
||||
return
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
// We need the v2 compute client
|
||||
computeClient, err := config.computeV2Client()
|
||||
if err != nil {
|
||||
ui.Error(fmt.Sprintf("Error terminating server, may still be around: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Terminating the source server: %s ...", s.server.ID))
|
||||
if config.ForceDelete {
|
||||
if err := servers.ForceDelete(computeClient, s.server.ID).ExtractErr(); err != nil {
|
||||
ui.Error(fmt.Sprintf("Error terminating server, may still be around: %s", err))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if err := servers.Delete(computeClient, s.server.ID).ExtractErr(); err != nil {
|
||||
ui.Error(fmt.Sprintf("Error terminating server, may still be around: %s", err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
stateChange := StateChangeConf{
|
||||
Pending: []string{"ACTIVE", "BUILD", "REBUILD", "SUSPENDED", "SHUTOFF", "STOPPED"},
|
||||
Refresh: ServerStateRefreshFunc(computeClient, s.server),
|
||||
Target: []string{"DELETED"},
|
||||
}
|
||||
|
||||
WaitForState(&stateChange)
|
||||
}
|
201
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_source_image_info.go
generated
vendored
Normal file
201
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_source_image_info.go
generated
vendored
Normal file
|
@ -0,0 +1,201 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/imageimport"
|
||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
"github.com/gophercloud/gophercloud/pagination"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepSourceImageInfo struct {
|
||||
SourceImage string
|
||||
SourceImageName string
|
||||
ExternalSourceImageURL string
|
||||
ExternalSourceImageFormat string
|
||||
ExternalSourceImageProperties map[string]string
|
||||
SourceImageOpts images.ListOpts
|
||||
SourceMostRecent bool
|
||||
SourceProperties map[string]string
|
||||
}
|
||||
|
||||
func PropertiesSatisfied(image *images.Image, props *map[string]string) bool {
|
||||
for key, value := range *props {
|
||||
if image.Properties[key] != value {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (s *StepSourceImageInfo) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
client, err := config.imageV2Client()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error creating image client: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if s.ExternalSourceImageURL != "" {
|
||||
createOpts := images.CreateOpts{
|
||||
Name: s.SourceImageName,
|
||||
ContainerFormat: "bare",
|
||||
DiskFormat: s.ExternalSourceImageFormat,
|
||||
Properties: s.ExternalSourceImageProperties,
|
||||
}
|
||||
|
||||
ui.Say("Creating image using external source image with name " + s.SourceImageName)
|
||||
ui.Say("Using disk format " + s.ExternalSourceImageFormat)
|
||||
image, err := images.Create(client, createOpts).Extract()
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating source image: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say("Created image with ID " + image.ID)
|
||||
|
||||
importOpts := imageimport.CreateOpts{
|
||||
Name: imageimport.WebDownloadMethod,
|
||||
URI: s.ExternalSourceImageURL,
|
||||
}
|
||||
|
||||
ui.Say("Importing External Source Image from URL " + s.ExternalSourceImageURL)
|
||||
err = imageimport.Create(client, image.ID, importOpts).ExtractErr()
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error importing source image: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
for image.Status != images.ImageStatusActive {
|
||||
ui.Message("Image not Active, retrying in 10 seconds")
|
||||
time.Sleep(10 * time.Second)
|
||||
|
||||
img, err := images.Get(client, image.ID).Extract()
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error querying image: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
image = img
|
||||
}
|
||||
|
||||
s.SourceImage = image.ID
|
||||
}
|
||||
|
||||
if s.SourceImage != "" {
|
||||
state.Put("source_image", s.SourceImage)
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
if s.SourceImageName != "" {
|
||||
s.SourceImageOpts = images.ListOpts{
|
||||
Name: s.SourceImageName,
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Using Image Filters %+v", s.SourceImageOpts)
|
||||
image := &images.Image{}
|
||||
count := 0
|
||||
err = images.List(client, s.SourceImageOpts).EachPage(func(page pagination.Page) (bool, error) {
|
||||
imgs, err := images.ExtractImages(page)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
for _, img := range imgs {
|
||||
// Check if all Properties are satisfied
|
||||
if PropertiesSatisfied(&img, &s.SourceProperties) {
|
||||
count++
|
||||
if count == 1 {
|
||||
// Tentatively return this result.
|
||||
*image = img
|
||||
}
|
||||
// Don't iterate over entries we will never use.
|
||||
if count > 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch count {
|
||||
case 0: // Continue looking at next page.
|
||||
return true, nil
|
||||
case 1: // Maybe we're done, maybe there is another result in a later page and it is an error.
|
||||
if s.SourceMostRecent {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
default: // By now we should know if getting 2+ results is an error or not.
|
||||
if s.SourceMostRecent {
|
||||
return false, nil
|
||||
}
|
||||
return false, fmt.Errorf(
|
||||
"Your query returned more than one result. Please try a more specific search, or set most_recent to true. Search filters: %+v properties %+v",
|
||||
s.SourceImageOpts, s.SourceProperties)
|
||||
}
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error querying image: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if image.ID == "" {
|
||||
err := fmt.Errorf("No image was found matching filters: %+v properties %+v",
|
||||
s.SourceImageOpts, s.SourceProperties)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Found Image ID: %s", image.ID))
|
||||
|
||||
state.Put("source_image", image.ID)
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepSourceImageInfo) Cleanup(state multistep.StateBag) {
|
||||
if s.ExternalSourceImageURL != "" {
|
||||
config := state.Get("config").(*Config)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
client, err := config.imageV2Client()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error creating image client: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Deleting temporary external source image: %s ...", s.SourceImageName))
|
||||
err = images.Delete(client, s.SourceImage).ExtractErr()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("error cleaning up external source image: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
59
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_stop_server.go
generated
vendored
Normal file
59
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_stop_server.go
generated
vendored
Normal file
|
@ -0,0 +1,59 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/startstop"
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepStopServer struct{}
|
||||
|
||||
func (s *StepStopServer) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
server := state.Get("server").(*servers.Server)
|
||||
|
||||
// We need the v2 compute client
|
||||
client, err := config.computeV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing compute client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Stopping server: %s ...", server.ID))
|
||||
if err := startstop.Stop(client, server.ID).ExtractErr(); err != nil {
|
||||
if _, ok := err.(gophercloud.ErrDefault409); ok {
|
||||
// The server might have already been shut down by Windows Sysprep
|
||||
log.Printf("[WARN] 409 on stopping an already stopped server, continuing")
|
||||
return multistep.ActionContinue
|
||||
} else {
|
||||
err = fmt.Errorf("Error stopping server: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
|
||||
ui.Message(fmt.Sprintf("Waiting for server to stop: %s ...", server.ID))
|
||||
stateChange := StateChangeConf{
|
||||
Pending: []string{"ACTIVE"},
|
||||
Target: []string{"SHUTOFF", "STOPPED"},
|
||||
Refresh: ServerStateRefreshFunc(client, server),
|
||||
StepState: state,
|
||||
}
|
||||
if _, err := WaitForState(&stateChange); err != nil {
|
||||
err := fmt.Errorf("Error waiting for server (%s) to stop: %s", server.ID, err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepStopServer) Cleanup(state multistep.StateBag) {}
|
58
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_update_image_mindisk.go
generated
vendored
Normal file
58
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_update_image_mindisk.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepUpdateImageMinDisk struct{}
|
||||
|
||||
func (s *stepUpdateImageMinDisk) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
if config.SkipCreateImage {
|
||||
ui.Say("Skipping image update mindisk...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
imageId := state.Get("image").(string)
|
||||
|
||||
if config.ImageMinDisk == 0 {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
imageClient, err := config.imageV2Client()
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error initializing image service client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Updating image min disk to %d", config.ImageMinDisk))
|
||||
|
||||
r := images.Update(
|
||||
imageClient,
|
||||
imageId,
|
||||
images.UpdateOpts{
|
||||
images.ReplaceImageMinDisk{
|
||||
NewMinDisk: config.ImageMinDisk,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if _, err := r.Extract(); err != nil {
|
||||
err = fmt.Errorf("Error updating image min disk: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepUpdateImageMinDisk) Cleanup(multistep.StateBag) {
|
||||
// No cleanup...
|
||||
}
|
58
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_update_image_tags.go
generated
vendored
Normal file
58
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_update_image_tags.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
imageservice "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepUpdateImageTags struct{}
|
||||
|
||||
func (s *stepUpdateImageTags) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
if config.SkipCreateImage {
|
||||
ui.Say("Skipping image update tags...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
imageId := state.Get("image").(string)
|
||||
|
||||
if len(config.ImageTags) == 0 {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
imageClient, err := config.imageV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing image service client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Updating image tags to %s", strings.Join(config.ImageTags, ", ")))
|
||||
r := imageservice.Update(
|
||||
imageClient,
|
||||
imageId,
|
||||
imageservice.UpdateOpts{
|
||||
imageservice.ReplaceImageTags{
|
||||
NewTags: config.ImageTags,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if _, err = r.Extract(); err != nil {
|
||||
err = fmt.Errorf("Error updating image tags: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepUpdateImageTags) Cleanup(multistep.StateBag) {
|
||||
// No cleanup...
|
||||
}
|
57
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_update_image_visibility.go
generated
vendored
Normal file
57
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_update_image_visibility.go
generated
vendored
Normal file
|
@ -0,0 +1,57 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
imageservice "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type stepUpdateImageVisibility struct{}
|
||||
|
||||
func (s *stepUpdateImageVisibility) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
if config.SkipCreateImage {
|
||||
ui.Say("Skipping image update visibility...")
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
imageId := state.Get("image").(string)
|
||||
|
||||
if config.ImageVisibility == "" {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
imageClient, err := config.imageV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing image service client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Updating image visibility to %s", config.ImageVisibility))
|
||||
r := imageservice.Update(
|
||||
imageClient,
|
||||
imageId,
|
||||
imageservice.UpdateOpts{
|
||||
imageservice.UpdateVisibility{
|
||||
Visibility: config.ImageVisibility,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
if _, err = r.Extract(); err != nil {
|
||||
err = fmt.Errorf("Error updating image visibility: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *stepUpdateImageVisibility) Cleanup(multistep.StateBag) {
|
||||
// No cleanup...
|
||||
}
|
54
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_wait_for_rackconnect.go
generated
vendored
Normal file
54
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/step_wait_for_rackconnect.go
generated
vendored
Normal file
|
@ -0,0 +1,54 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud/openstack/compute/v2/servers"
|
||||
"github.com/hashicorp/packer-plugin-sdk/multistep"
|
||||
packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
|
||||
)
|
||||
|
||||
type StepWaitForRackConnect struct {
|
||||
Wait bool
|
||||
}
|
||||
|
||||
func (s *StepWaitForRackConnect) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
if !s.Wait {
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
config := state.Get("config").(*Config)
|
||||
server := state.Get("server").(*servers.Server)
|
||||
ui := state.Get("ui").(packersdk.Ui)
|
||||
|
||||
// We need the v2 compute client
|
||||
computeClient, err := config.computeV2Client()
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Error initializing compute client: %s", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf(
|
||||
"Waiting for server (%s) to become RackConnect ready...", server.ID))
|
||||
for {
|
||||
server, err = servers.Get(computeClient, server.ID).Extract()
|
||||
if err != nil {
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
if server.Metadata["rackconnect_automation_status"] == "DEPLOYED" {
|
||||
state.Put("server", server)
|
||||
break
|
||||
}
|
||||
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepWaitForRackConnect) Cleanup(state multistep.StateBag) {
|
||||
}
|
76
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/volume.go
generated
vendored
Normal file
76
vendor/github.com/hashicorp/packer-plugin-openstack/builder/openstack/volume.go
generated
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
package openstack
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/gophercloud/gophercloud"
|
||||
"github.com/gophercloud/gophercloud/openstack/blockstorage/v3/volumes"
|
||||
"github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
|
||||
)
|
||||
|
||||
// WaitForVolume waits for the given volume to become available.
|
||||
func WaitForVolume(blockStorageClient *gophercloud.ServiceClient, volumeID string) error {
|
||||
maxNumErrors := 10
|
||||
numErrors := 0
|
||||
|
||||
for {
|
||||
status, err := GetVolumeStatus(blockStorageClient, volumeID)
|
||||
if err != nil {
|
||||
errCode, ok := err.(*gophercloud.ErrUnexpectedResponseCode)
|
||||
if ok && (errCode.Actual == 500 || errCode.Actual == 404) {
|
||||
numErrors++
|
||||
if numErrors >= maxNumErrors {
|
||||
log.Printf("[ERROR] Maximum number of errors (%d) reached; failing with: %s", numErrors, err)
|
||||
return err
|
||||
}
|
||||
log.Printf("[ERROR] %d error received, will ignore and retry: %s", errCode.Actual, err)
|
||||
time.Sleep(2 * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
if status == "available" {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("Waiting for volume creation status: %s", status)
|
||||
time.Sleep(2 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// GetVolumeSize returns volume size in gigabytes based on the image min disk
|
||||
// value if it's not empty.
|
||||
// Or it calculates needed gigabytes size from the image bytes size.
|
||||
func GetVolumeSize(imageClient *gophercloud.ServiceClient, imageID string) (int, error) {
|
||||
sourceImage, err := images.Get(imageClient, imageID).Extract()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if sourceImage.MinDiskGigabytes != 0 {
|
||||
return sourceImage.MinDiskGigabytes, nil
|
||||
}
|
||||
|
||||
volumeSizeMB := sourceImage.SizeBytes / 1024 / 1024
|
||||
volumeSizeGB := int(sourceImage.SizeBytes / 1024 / 1024 / 1024)
|
||||
|
||||
// Increment gigabytes size if the initial size can't be divided without
|
||||
// remainder.
|
||||
if volumeSizeMB%1024 > 0 {
|
||||
volumeSizeGB++
|
||||
}
|
||||
|
||||
return volumeSizeGB, nil
|
||||
}
|
||||
|
||||
func GetVolumeStatus(blockStorageClient *gophercloud.ServiceClient, volumeID string) (string, error) {
|
||||
volume, err := volumes.Get(blockStorageClient, volumeID).Extract()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return volume.Status, nil
|
||||
}
|
|
@ -11,6 +11,7 @@ go:
|
|||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
- "1.14.x"
|
||||
- "tip"
|
||||
|
||||
go_import_path: gopkg.in/yaml.v2
|
||||
|
|
|
@ -79,6 +79,8 @@ func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
|
|||
parser.encoding = encoding
|
||||
}
|
||||
|
||||
var disableLineWrapping = false
|
||||
|
||||
// Create a new emitter object.
|
||||
func yaml_emitter_initialize(emitter *yaml_emitter_t) {
|
||||
*emitter = yaml_emitter_t{
|
||||
|
@ -86,7 +88,9 @@ func yaml_emitter_initialize(emitter *yaml_emitter_t) {
|
|||
raw_buffer: make([]byte, 0, output_raw_buffer_size),
|
||||
states: make([]yaml_emitter_state_t, 0, initial_stack_size),
|
||||
events: make([]yaml_event_t, 0, initial_queue_size),
|
||||
best_width: -1,
|
||||
}
|
||||
if disableLineWrapping {
|
||||
emitter.best_width = -1
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
module "gopkg.in/yaml.v2"
|
||||
module gopkg.in/yaml.v2
|
||||
|
||||
require (
|
||||
"gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405
|
||||
)
|
||||
go 1.15
|
||||
|
||||
require gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405
|
||||
|
|
|
@ -175,7 +175,7 @@ func unmarshal(in []byte, out interface{}, strict bool) (err error) {
|
|||
// Zero valued structs will be omitted if all their public
|
||||
// fields are zero, unless they implement an IsZero
|
||||
// method (see the IsZeroer interface type), in which
|
||||
// case the field will be included if that method returns true.
|
||||
// case the field will be excluded if IsZero returns true.
|
||||
//
|
||||
// flow Marshal using a flow style (useful for structs,
|
||||
// sequences and maps).
|
||||
|
@ -464,3 +464,15 @@ func isZero(v reflect.Value) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// FutureLineWrap globally disables line wrapping when encoding long strings.
|
||||
// This is a temporary and thus deprecated method introduced to faciliate
|
||||
// migration towards v3, which offers more control of line lengths on
|
||||
// individual encodings, and has a default matching the behavior introduced
|
||||
// by this function.
|
||||
//
|
||||
// The default formatting of v2 was erroneously changed in v2.3.0 and reverted
|
||||
// in v2.4.0, at which point this function was introduced to help migration.
|
||||
func FutureLineWrap() {
|
||||
disableLineWrapping = true
|
||||
}
|
||||
|
|
|
@ -525,6 +525,9 @@ github.com/hashicorp/packer-plugin-googlecompute/post-processor/googlecompute-im
|
|||
# github.com/hashicorp/packer-plugin-ncloud v0.0.2
|
||||
## explicit
|
||||
github.com/hashicorp/packer-plugin-ncloud/builder/ncloud
|
||||
# github.com/hashicorp/packer-plugin-openstack v0.0.1
|
||||
## explicit
|
||||
github.com/hashicorp/packer-plugin-openstack/builder/openstack
|
||||
# github.com/hashicorp/packer-plugin-outscale v0.0.1
|
||||
## explicit
|
||||
github.com/hashicorp/packer-plugin-outscale/builder/osc/bsu
|
||||
|
@ -1210,7 +1213,7 @@ gopkg.in/square/go-jose.v2
|
|||
gopkg.in/square/go-jose.v2/cipher
|
||||
gopkg.in/square/go-jose.v2/json
|
||||
gopkg.in/square/go-jose.v2/jwt
|
||||
# gopkg.in/yaml.v2 v2.3.0
|
||||
# gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v2
|
||||
# gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
gopkg.in/yaml.v3
|
||||
|
|
|
@ -753,10 +753,6 @@
|
|||
"title": "1&1",
|
||||
"path": "builders/oneandone"
|
||||
},
|
||||
{
|
||||
"title": "OpenStack",
|
||||
"path": "builders/openstack"
|
||||
},
|
||||
{
|
||||
"title": "Oracle",
|
||||
"routes": [
|
||||
|
|
|
@ -37,6 +37,26 @@
|
|||
"pluginTier": "community",
|
||||
"version": "latest"
|
||||
},
|
||||
{
|
||||
"title": "Openstack",
|
||||
"path": "openstack",
|
||||
"repo": "hashicorp/packer-plugin-openstack",
|
||||
"pluginTier": "community",
|
||||
"version": "latest"
|
||||
},
|
||||
{
|
||||
"title": "Outscale",
|
||||
"path": "outscale",
|
||||
"repo": "hashicorp/packer-plugin-outscale",
|
||||
"version": "latest",
|
||||
"pluginTier": "community"
|
||||
},
|
||||
{
|
||||
"title": "Parallels",
|
||||
"path": "parallels",
|
||||
"repo": "hashicorp/packer-plugin-parallels",
|
||||
"version": "latest"
|
||||
},
|
||||
{
|
||||
"title": "Proxmox",
|
||||
"path": "proxmox",
|
||||
|
@ -74,18 +94,5 @@
|
|||
"path": "qemu",
|
||||
"repo": "hashicorp/packer-plugin-qemu",
|
||||
"version": "latest"
|
||||
},
|
||||
{
|
||||
"title": "Outscale",
|
||||
"path": "outscale",
|
||||
"repo": "hashicorp/packer-plugin-outscale",
|
||||
"version": "latest",
|
||||
"pluginTier": "community"
|
||||
},
|
||||
{
|
||||
"title": "Parallels",
|
||||
"path": "parallels",
|
||||
"repo": "hashicorp/packer-plugin-parallels",
|
||||
"version": "latest"
|
||||
}
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue