diff --git a/builder/openstack/access_config.go b/builder/openstack/access_config.go
deleted file mode 100644
index 499813eca..000000000
--- a/builder/openstack/access_config.go
+++ /dev/null
@@ -1,334 +0,0 @@
-//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))
-}
diff --git a/builder/openstack/artifact.go b/builder/openstack/artifact.go
deleted file mode 100644
index db2759ad5..000000000
--- a/builder/openstack/artifact.go
+++ /dev/null
@@ -1,51 +0,0 @@
-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()
-}
diff --git a/builder/openstack/artifact_test.go b/builder/openstack/artifact_test.go
deleted file mode 100644
index 180906ef3..000000000
--- a/builder/openstack/artifact_test.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package openstack
-
-import (
- "testing"
-
- packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
-)
-
-func TestArtifact_Impl(t *testing.T) {
- var _ packersdk.Artifact = new(Artifact)
-}
-
-func TestArtifactId(t *testing.T) {
- expected := `b8cdf55b-c916-40bd-b190-389ec144c4ed`
-
- a := &Artifact{
- ImageId: "b8cdf55b-c916-40bd-b190-389ec144c4ed",
- }
-
- result := a.Id()
- if result != expected {
- t.Fatalf("bad: %s", result)
- }
-}
-
-func TestArtifactString(t *testing.T) {
- expected := "An image was created: b8cdf55b-c916-40bd-b190-389ec144c4ed"
-
- a := &Artifact{
- ImageId: "b8cdf55b-c916-40bd-b190-389ec144c4ed",
- }
- result := a.String()
- if result != expected {
- t.Fatalf("bad: %s", result)
- }
-}
-
-func TestArtifactState_StateData(t *testing.T) {
- expectedData := "this is the data"
- artifact := &Artifact{
- StateData: map[string]interface{}{"state_data": expectedData},
- }
-
- // Valid state
- result := artifact.State("state_data")
- if result != expectedData {
- t.Fatalf("Bad: State data was %s instead of %s", result, expectedData)
- }
-
- // Invalid state
- result = artifact.State("invalid_key")
- if result != nil {
- t.Fatalf("Bad: State should be nil for invalid state data name")
- }
-
- // Nil StateData should not fail and should return nil
- artifact = &Artifact{}
- result = artifact.State("key")
- if result != nil {
- t.Fatalf("Bad: State should be nil for nil StateData")
- }
-}
diff --git a/builder/openstack/builder.go b/builder/openstack/builder.go
deleted file mode 100644
index 70cf61fa4..000000000
--- a/builder/openstack/builder.go
+++ /dev/null
@@ -1,200 +0,0 @@
-//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
-}
diff --git a/builder/openstack/builder.hcl2spec.go b/builder/openstack/builder.hcl2spec.go
deleted file mode 100644
index cdc9e1b9b..000000000
--- a/builder/openstack/builder.hcl2spec.go
+++ /dev/null
@@ -1,322 +0,0 @@
-// 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
-}
diff --git a/builder/openstack/builder_test.go b/builder/openstack/builder_test.go
deleted file mode 100644
index 15ed1c37c..000000000
--- a/builder/openstack/builder_test.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package openstack
-
-import (
- "testing"
-
- packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
-)
-
-func TestBuilder_ImplementsBuilder(t *testing.T) {
- var raw interface{}
- raw = &Builder{}
- if _, ok := raw.(packersdk.Builder); !ok {
- t.Fatalf("Builder should be a builder")
- }
-}
-
-func TestBuilder_Prepare_BadType(t *testing.T) {
- b := &Builder{}
- c := map[string]interface{}{
- "password": []string{},
- }
-
- _, warns, err := b.Prepare(c)
- if len(warns) > 0 {
- t.Fatalf("bad: %#v", warns)
- }
- if err == nil {
- t.Fatalf("prepare should fail")
- }
-}
diff --git a/builder/openstack/image_config.go b/builder/openstack/image_config.go
deleted file mode 100644
index 5c23c2dee..000000000
--- a/builder/openstack/image_config.go
+++ /dev/null
@@ -1,83 +0,0 @@
-//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
-}
diff --git a/builder/openstack/image_config_test.go b/builder/openstack/image_config_test.go
deleted file mode 100644
index 4d81ecd94..000000000
--- a/builder/openstack/image_config_test.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package openstack
-
-import (
- "testing"
-)
-
-func testImageConfig() *ImageConfig {
- return &ImageConfig{
- ImageName: "foo",
- }
-}
-
-func TestImageConfigPrepare_Region(t *testing.T) {
- c := testImageConfig()
- if err := c.Prepare(nil); err != nil {
- t.Fatalf("shouldn't have err: %s", err)
- }
-
- c.ImageName = ""
- if err := c.Prepare(nil); err == nil {
- t.Fatal("should have error")
- }
-}
diff --git a/builder/openstack/networks.go b/builder/openstack/networks.go
deleted file mode 100644
index 3f510d6de..000000000
--- a/builder/openstack/networks.go
+++ /dev/null
@@ -1,180 +0,0 @@
-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
-}
diff --git a/builder/openstack/networks_test.go b/builder/openstack/networks_test.go
deleted file mode 100644
index fe0a591ea..000000000
--- a/builder/openstack/networks_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package openstack
-
-import (
- "net"
- "testing"
-)
-
-func testYes(t *testing.T, a, b string) {
- var m, n *net.IPNet
- _, m, _ = net.ParseCIDR(a)
- _, n, _ = net.ParseCIDR(b)
- if !containsNet(m, n) {
- t.Errorf("%s expected to contain %s", m, n)
- }
-}
-
-func testNot(t *testing.T, a, b string) {
- var m, n *net.IPNet
- _, m, _ = net.ParseCIDR(a)
- _, n, _ = net.ParseCIDR(b)
- if containsNet(m, n) {
- t.Errorf("%s expected to not contain %s", m, n)
- }
-}
-
-func TestNetworkDiscovery_SubnetContainsGood_IPv4(t *testing.T) {
- testYes(t, "192.168.0.0/23", "192.168.0.0/24")
- testYes(t, "192.168.0.0/24", "192.168.0.0/24")
- testNot(t, "192.168.0.0/25", "192.168.0.0/24")
-
- testYes(t, "192.168.101.202/16", "192.168.202.101/16")
- testNot(t, "192.168.101.202/24", "192.168.202.101/24")
- testNot(t, "192.168.202.101/24", "192.168.101.202/24")
-
- testYes(t, "0.0.0.0/0", "192.168.0.0/24")
- testYes(t, "0.0.0.0/0", "0.0.0.0/1")
- testNot(t, "192.168.0.0/24", "0.0.0.0/0")
- testNot(t, "0.0.0.0/1", "0.0.0.0/0")
-}
-
-func TestNetworkDiscovery_SubnetContainsGood_IPv6(t *testing.T) {
- testYes(t, "2001:db8::/63", "2001:db8::/64")
- testYes(t, "2001:db8::/64", "2001:db8::/64")
- testNot(t, "2001:db8::/65", "2001:db8::/64")
-
- testYes(t, "2001:db8:fefe:b00b::/32", "2001:db8:b00b:fefe::/32")
- testNot(t, "2001:db8:fefe:b00b::/64", "2001:db8:b00b:fefe::/64")
- testNot(t, "2001:db8:b00b:fefe::/64", "2001:db8:fefe:b00b::/64")
-
- testYes(t, "::/0", "2001:db8::/64")
- testYes(t, "::/0", "::/1")
- testNot(t, "2001:db8::/64", "::/0")
- testNot(t, "::/1", "::/0")
-}
diff --git a/builder/openstack/run_config.go b/builder/openstack/run_config.go
deleted file mode 100644
index 34ca7b7a4..000000000
--- a/builder/openstack/run_config.go
+++ /dev/null
@@ -1,345 +0,0 @@
-//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)
-}
diff --git a/builder/openstack/run_config_test.go b/builder/openstack/run_config_test.go
deleted file mode 100644
index af14715dc..000000000
--- a/builder/openstack/run_config_test.go
+++ /dev/null
@@ -1,284 +0,0 @@
-package openstack
-
-import (
- "os"
- "regexp"
- "testing"
-
- "github.com/gophercloud/gophercloud/openstack/imageservice/v2/images"
- "github.com/hashicorp/packer-plugin-sdk/communicator"
- "github.com/mitchellh/mapstructure"
-)
-
-func init() {
- // Clear out the openstack env vars so they don't
- // affect our tests.
- os.Setenv("SDK_USERNAME", "")
- os.Setenv("SDK_PASSWORD", "")
- os.Setenv("SDK_PROVIDER", "")
-}
-
-func testRunConfig() *RunConfig {
- return &RunConfig{
- SourceImage: "abcd",
- Flavor: "m1.small",
-
- Comm: communicator.Config{
- SSH: communicator.SSH{
- SSHUsername: "foo",
- },
- },
- }
-}
-
-func TestRunConfigPrepare(t *testing.T) {
- c := testRunConfig()
- err := c.Prepare(nil)
- if len(err) > 0 {
- t.Fatalf("err: %s", err)
- }
-}
-
-func TestRunConfigPrepare_InstanceType(t *testing.T) {
- c := testRunConfig()
- c.Flavor = ""
- if err := c.Prepare(nil); len(err) != 1 {
- t.Fatalf("err: %s", err)
- }
-}
-
-func TestRunConfigPrepare_SourceImage(t *testing.T) {
- c := testRunConfig()
- c.SourceImage = ""
- if err := c.Prepare(nil); len(err) != 1 {
- t.Fatalf("err: %s", err)
- }
-}
-
-func TestRunConfigPrepare_SSHPort(t *testing.T) {
- c := testRunConfig()
- c.Comm.SSHPort = 0
- if err := c.Prepare(nil); len(err) != 0 {
- t.Fatalf("err: %s", err)
- }
-
- if c.Comm.SSHPort != 22 {
- t.Fatalf("invalid value: %d", c.Comm.SSHPort)
- }
-
- c.Comm.SSHPort = 44
- if err := c.Prepare(nil); len(err) != 0 {
- t.Fatalf("err: %s", err)
- }
-
- if c.Comm.SSHPort != 44 {
- t.Fatalf("invalid value: %d", c.Comm.SSHPort)
- }
-}
-
-func TestRunConfigPrepare_BlockStorage(t *testing.T) {
- c := testRunConfig()
- c.UseBlockStorageVolume = true
- c.VolumeType = "fast"
- if err := c.Prepare(nil); len(err) != 0 {
- t.Fatalf("err: %s", err)
- }
- if c.VolumeType != "fast" {
- t.Fatalf("invalid value: %s", c.VolumeType)
- }
-
- c.AvailabilityZone = "RegionTwo"
- if err := c.Prepare(nil); len(err) != 0 {
- t.Fatalf("err: %s", err)
- }
-
- if c.VolumeAvailabilityZone != "RegionTwo" {
- t.Fatalf("invalid value: %s", c.VolumeAvailabilityZone)
- }
-
- c.VolumeAvailabilityZone = "RegionOne"
- if err := c.Prepare(nil); len(err) != 0 {
- t.Fatalf("err: %s", err)
- }
-
- if c.VolumeAvailabilityZone != "RegionOne" {
- t.Fatalf("invalid value: %s", c.VolumeAvailabilityZone)
- }
-
- c.VolumeName = "PackerVolume"
- if c.VolumeName != "PackerVolume" {
- t.Fatalf("invalid value: %s", c.VolumeName)
- }
-}
-
-func TestRunConfigPrepare_FloatingIPPoolCompat(t *testing.T) {
- c := testRunConfig()
- c.FloatingIPPool = "uuid1"
- if err := c.Prepare(nil); len(err) != 0 {
- t.Fatalf("err: %s", err)
- }
-
- if c.FloatingIPNetwork != "uuid1" {
- t.Fatalf("invalid value: %s", c.FloatingIPNetwork)
- }
-
- c.FloatingIPNetwork = "uuid2"
- c.FloatingIPPool = "uuid3"
- if err := c.Prepare(nil); len(err) != 0 {
- t.Fatalf("err: %s", err)
- }
-
- if c.FloatingIPNetwork != "uuid2" {
- t.Fatalf("invalid value: %s", c.FloatingIPNetwork)
- }
-}
-
-func TestRunConfigPrepare_ExternalSourceImageURL(t *testing.T) {
- c := testRunConfig()
- // test setting both ExternalSourceImageURL and SourceImage causes an error
- c.ExternalSourceImageURL = "http://example.com/image.qcow2"
- if err := c.Prepare(nil); len(err) != 1 {
- t.Fatalf("err: %s", err)
- }
-
- c = testRunConfig()
- // test setting both ExternalSourceImageURL and SourceImageName causes an error
- c.SourceImage = ""
- c.SourceImageName = "abcd"
- c.ExternalSourceImageURL = "http://example.com/image.qcow2"
- if err := c.Prepare(nil); len(err) != 1 {
- t.Fatalf("err: %s", err)
- }
-
- c = testRunConfig()
- // test neither setting SourceImage, SourceImageName or ExternalSourceImageURL causes an error
- c.SourceImage = ""
- c.SourceImageName = ""
- c.ExternalSourceImageURL = ""
- if err := c.Prepare(nil); len(err) != 1 {
- t.Fatalf("err: %s", err)
- }
-
- c = testRunConfig()
- // test setting only ExternalSourceImageURL passes
- c.SourceImage = ""
- c.SourceImageName = ""
- c.ExternalSourceImageURL = "http://example.com/image.qcow2"
- if err := c.Prepare(nil); len(err) != 0 {
- t.Fatalf("err: %s", err)
- }
-
- // test default values
- if c.ExternalSourceImageFormat != "qcow2" {
- t.Fatalf("ExternalSourceImageFormat should have been set to default: qcow2")
- }
-
- p := `packer_[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`
- if matches, _ := regexp.MatchString(p, c.SourceImageName); !matches {
- t.Fatalf("invalid format for SourceImageName: %s", c.SourceImageName)
- }
-
- c = testRunConfig()
- // test setting a filter passes
- c.SourceImage = ""
- c.SourceImageName = ""
- c.ExternalSourceImageURL = ""
- c.SourceImageFilters = ImageFilter{
- Filters: ImageFilterOptions{
- Name: "Ubuntu 16.04",
- Visibility: "public",
- Owner: "1234567890",
- Tags: []string{"prod", "ready"},
- Properties: map[string]string{"os_distro": "ubuntu", "os_version": "16.04"},
- },
- MostRecent: true,
- }
-
- if err := c.Prepare(nil); len(err) != 0 {
- t.Fatalf("Should not error if everything but filter is empty: %s", err)
- }
-
-}
-
-// This test case confirms that only allowed fields will be set to values
-// The checked values are non-nil for their target type
-func TestBuildImageFilter(t *testing.T) {
-
- filters := ImageFilterOptions{
- Name: "Ubuntu 16.04",
- Visibility: "public",
- Owner: "1234567890",
- Tags: []string{"prod", "ready"},
- Properties: map[string]string{"os_distro": "ubuntu", "os_version": "16.04"},
- }
-
- listOpts, err := filters.Build()
- if err != nil {
- t.Errorf("Building filter failed with: %s", err)
- }
-
- if listOpts.Name != "Ubuntu 16.04" {
- t.Errorf("Name did not build correctly: %s", listOpts.Name)
- }
-
- if listOpts.Visibility != images.ImageVisibilityPublic {
- t.Errorf("Visibility did not build correctly: %s", listOpts.Visibility)
- }
-
- if listOpts.Owner != "1234567890" {
- t.Errorf("Owner did not build correctly: %s", listOpts.Owner)
- }
-}
-
-func TestBuildBadImageFilter(t *testing.T) {
- filterMap := map[string]interface{}{
- "limit": "3",
- "size_min": "25",
- }
-
- filters := ImageFilterOptions{}
- mapstructure.Decode(filterMap, &filters)
- listOpts, err := filters.Build()
-
- if err != nil {
- t.Errorf("Error returned processing image filter: %s", err.Error())
- return // we cannot trust listOpts to not cause unexpected behaviour
- }
-
- if listOpts.Limit == filterMap["limit"] {
- t.Errorf("Limit was parsed into ListOpts: %d", listOpts.Limit)
- }
-
- if listOpts.SizeMin != 0 {
- t.Errorf("SizeMin was parsed into ListOpts: %d", listOpts.SizeMin)
- }
-
- if listOpts.Sort != "created_at:desc" {
- t.Errorf("Sort was not applied: %s", listOpts.Sort)
- }
-
- if !filters.Empty() {
- t.Errorf("The filters should be empty due to lack of input")
- }
-}
-
-// Tests that the Empty method on ImageFilterOptions works as expected
-func TestImageFiltersEmpty(t *testing.T) {
- filledFilters := ImageFilterOptions{
- Name: "Ubuntu 16.04",
- Visibility: "public",
- Owner: "1234567890",
- Tags: []string{"prod", "ready"},
- Properties: map[string]string{"os_distro": "ubuntu", "os_version": "16.04"},
- }
-
- if filledFilters.Empty() {
- t.Errorf("Expected filled filters to be non-empty: %v", filledFilters)
- }
-
- emptyFilters := ImageFilterOptions{}
-
- if !emptyFilters.Empty() {
- t.Errorf("Expected default filter to be empty: %v", emptyFilters)
- }
-}
diff --git a/builder/openstack/server.go b/builder/openstack/server.go
deleted file mode 100644
index 61a198ad1..000000000
--- a/builder/openstack/server.go
+++ /dev/null
@@ -1,93 +0,0 @@
-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)
- }
-}
diff --git a/builder/openstack/ssh.go b/builder/openstack/ssh.go
deleted file mode 100644
index ff716b57d..000000000
--- a/builder/openstack/ssh.go
+++ /dev/null
@@ -1,116 +0,0 @@
-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 ""
-}
diff --git a/builder/openstack/step_add_image_members.go b/builder/openstack/step_add_image_members.go
deleted file mode 100644
index 9c475bfef..000000000
--- a/builder/openstack/step_add_image_members.go
+++ /dev/null
@@ -1,63 +0,0 @@
-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...
-}
diff --git a/builder/openstack/step_allocate_ip.go b/builder/openstack/step_allocate_ip.go
deleted file mode 100644
index c677d437e..000000000
--- a/builder/openstack/step_allocate_ip.go
+++ /dev/null
@@ -1,178 +0,0 @@
-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))
- }
-}
diff --git a/builder/openstack/step_create_image.go b/builder/openstack/step_create_image.go
deleted file mode 100644
index 2d8a92416..000000000
--- a/builder/openstack/step_create_image.go
+++ /dev/null
@@ -1,152 +0,0 @@
-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)
- }
-}
diff --git a/builder/openstack/step_create_volume.go b/builder/openstack/step_create_volume.go
deleted file mode 100644
index cfbc8fbb6..000000000
--- a/builder/openstack/step_create_volume.go
+++ /dev/null
@@ -1,134 +0,0 @@
-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))
- }
-}
diff --git a/builder/openstack/step_detach_volume.go b/builder/openstack/step_detach_volume.go
deleted file mode 100644
index f80d9e8a2..000000000
--- a/builder/openstack/step_detach_volume.go
+++ /dev/null
@@ -1,54 +0,0 @@
-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.
-}
diff --git a/builder/openstack/step_discover_network.go b/builder/openstack/step_discover_network.go
deleted file mode 100644
index 642abdfa6..000000000
--- a/builder/openstack/step_discover_network.go
+++ /dev/null
@@ -1,55 +0,0 @@
-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) {}
diff --git a/builder/openstack/step_get_password.go b/builder/openstack/step_get_password.go
deleted file mode 100644
index 7f0745888..000000000
--- a/builder/openstack/step_get_password.go
+++ /dev/null
@@ -1,85 +0,0 @@
-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) {}
diff --git a/builder/openstack/step_key_pair.go b/builder/openstack/step_key_pair.go
deleted file mode 100644
index 4c6ebdbb8..000000000
--- a/builder/openstack/step_key_pair.go
+++ /dev/null
@@ -1,190 +0,0 @@
-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))
- }
-}
diff --git a/builder/openstack/step_key_pair_test.go b/builder/openstack/step_key_pair_test.go
deleted file mode 100644
index 9e0579f32..000000000
--- a/builder/openstack/step_key_pair_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package openstack
-
-import (
- "bytes"
- "os/exec"
- "testing"
-
- packersdk "github.com/hashicorp/packer-plugin-sdk/packer"
- "golang.org/x/crypto/ssh"
-)
-
-var ber_encoded_key = `
------BEGIN RSA PRIVATE KEY-----
-MIIEqQIBAAKCAQEAo1C/iQtu7iSUJROyQgKm5VtJbaPg5GEP7IC+7TTZ2kVnWa9V
-MU37sXKWk0J7oRHcSZ/dhRyNDcsvscFAU6V3FRpZxiTEvCqIBgLyoV3g+wkan+gD
-AqNmqm77ldUIwMXT7n3AeWipw0uOBzZNjANhAf8qHIT2PXoT6LfaCof4PlPucCTx
-eC+oT5zA/MbQoGneNkHnR26ijMac2vRC90+1WpZ5KwCVE6qd2kERlb9gbAGsNLE/
-WrqR7bx4d8esLtE3l5zwkBTB63KdojMCrX/ZBiHT15TBQVsFPQqhdBT3BXfssnut
-MkCf6+X0PIQkQcW6RqKR5nOHMFdB1kEChaKYMwIDAQABAoIBAQCRBPv/opJvjzWp
-stLAgQBYe/Y5EKN7yKDOPwjLM/obMzPx1JqOvJO6X2lL/GYxgd2d1wJq2A586CdC
-7brETBLxP0PmifHUsOO2itmO5wEHiW8F/Yzmw9g/kWuAAfrSyxhFF49Zf9H3ZFkL
-GHJF2R5EGqP3TS4nKwcQyGkqntCV7p+QmPvlB05jT3vsc2jLn2yXXVEbu1X5Zj82
-cdFwH4ZSc2BDv8ixBHy+zOGLx0TMF0hxEHdNIeAjGTyYfiyDr5mgMP4w6igGvP8q
-pB5fE60ZKCEPLGyxMw69nDbXJK6YAKyNCVAD79FEl1yLYJlWfYAtOI6UeE0RUPeD
-i42vINyBAoIAgQC9zZzl4kJUTdzYLYrxQ7f51B+eOF621kleTYLprr8I4VZUExht
-KtsHdHWzMkxVtd7Q7UcG9L+Pqa3DVnD3S2C1IuUkO3eCpa93jhiwCIRYQfR7EzuR
-ntVrQHfYaCgr8ahmWdoeZUr8lSDDp0/h+MamEksNEjZA+XwFWg6ycLgGwQKCAIEA
-3EYz5iHqaOdekBYXcFpkq8uk+Sn27Cmnd7pLfK/YWEAb3Pie9X0pftA1O8z0tW9p
-vEBS+pA27S55a38avmZSkZAiXMJOZ3HV99VZi5WU0Pg9A9oIiQvW6td9b1uuVl5q
-o9fCK/r17E7NCFimtqSNtrNR/X08g+l4Dp1Ourgm7/MCggCAIGMohbWhGd+bcqv6
-zIaAqzm+F3KI/uv74wKY9yUhZfOFlp0XivFIJLKDrwtDKVD6b249s3sqAOq0QuPK
-LPiIzP/iV9dp4jpBgcYWgltBsgm3HRVAEe4nfsCmcp/7UtxOnwBwDsW8EPOlfp1b
-LTUVOJtggR99cILh3cvrPBmt3UECggCBALsRH7g8a1+lxmglashu6/n+G1/DZMER
-avjCDKOajuf7oe4acpzXK6tX1S2xFM0VDj3iftXuLcdl5ZYGPsceDNc0CgqutXki
-cu1jkgV6BgUmHGMuAnuow19znEI7ISaWTohQjsVc/wctsPB6oTKRMwzK40Gc3wzD
-9MKsk5T9GYxDAoIAgAFW9agGS4th1RrVwngjKHCPenQR1QSvHDHv3E3EQta1d0K3
-Cx7RCdBpDNeNIpHVq8uTaWjSZ+Em6YDja7CqIfPj0HcykizxS4L7Dh0gt7I5+kuy
-yBgJhEgIw3mK7U47nm9MmR/67RI4IS978ekVXP2oGdYqVUhHvMuOix8a72xk
------END RSA PRIVATE KEY-----
-`
-
-var der_encoded_key = `
------BEGIN RSA PRIVATE KEY-----
-MIIEpAIBAAKCAQEAmfhARpOHdm5i551GEtOO2NEH36Dm2NjJp6eHQpnrwDW0bqd6
-qHJSmhocSb/3r2WwPOsfBKa135aCEOqXKcrc2gY+IsgGMF8bFG//UVcHA0KGG9A8
-mWwYBIU4lpb7DY54IllDDO11BERrxOEsPqmNgGzE0yOYApC2t8OcXsUE15kznhLn
-d4V53kN//jRGR44KrGMcL8nrKDIBudz90ae7tRGBHYiMWPB89yYdUt06dXPLqqbf
-JIUhuOBwle3IOuwJPmPEwsXFXkxKCYzKDAyi+weCDKtqeUl3HNbCB0f7g/uxUDhQ
-wRzZ1v4g35LHh5zQR26dizl4Uj2dNLL6K5xkPwIDAQABAoIBABdpTPSuSAG1BSrs
-mhQQwP6swgK553//bqIkcgepedRPFjFhG+BzCaZO5BA+tT2hO6v3oE7Hvo3Rx9Mk
-qHl9VBl+q4IEYhSG0YpJAUxv7CwNuHCQODan3fsJ+rHDIUdNa2zln7FehdVxReW4
-y05334EwiLkGB34UXQQSJTuvv228rF5F7wCs0luZWJ9IpGNWpwI2K6pg3ME/bIb6
-xFMf6jKWUUZht1m+40UG2bhsMPPknde2zAq4DjJTl7bJuuhceqp3ylm5h+oUDCl2
-twyGhn8gpHrMXVCcEvxwno8aJfNJ18iZu+lIQTT1DVuRrFprGDc9dFW6zVFauv6W
-ZG5AUXkCgYEAyoBUPVFaf3E6nDqq26J5x6+DBe6afS7tqNn5E/z2cI54KLiUDwjs
-P9axjdVMqqUiNw0jJRi8nUe1psDLguSZo4tFSgsWbsdnZQutQz2fy87jIgZOzZpj
-fqud64O/fMm8fpGBz5LQjo27FLRfQcr23KVjWeQgBSMfMk2oe8Mw8+sCgYEAwqWc
-abQBpG5TqtgvR3M9W0tAK51SSZF5okKif8KMoDJ67pXX2iTgh4bnUugaYObcG3qV
-5VH9xlOuN4td4RfBybzRcKJDN/oinwZhMjQKKjyhpjLInDqnF4ogLPevT6cCq8I+
-rHfK6DhtqLUG4BYDD7QWOBZpCTwd3n+7Xk39v/0CgYEAoaM9mpRNgFyJRBswNpDC
-VDosg5epiTLkUVtsDiBlNgMCtr5esIGW0n40y9nukGevn/HEk9/i7khHHwvVZm3C
-lWCdtjSTe2l/hpCDhKCz5KMHeik+za7mrD2gmFVZi+obo4vR6jZuctt+8U/omUPB
-OO5rF12YkYEvbZ+/VMrBUHECgYEArWGpvvpJ0Dc6Ld9d1e5PxCd2pKMBLmj4CNIE
-P3uDmhr9J9KvsC/TFMXU/iOjg5eAjrWWGev7+pKFiBKLcDqiMtoPUZ4n9A/KkQ60
-u2xhdZgGga2QxqD0P+KYoJWMQo5IschX3XbjdhD1lSaTVj4lQfKvLAzCSSiUjqIG
-u40LL90CgYB9t3CISlVJ+l1zy7QPIvAul5VuhwjxBHaotdLuXzvXFJXZH2dQ9j/a
-/p2GMUq+VZbKq45x4I5Z3ax462qMRDmpx9yjtrwNavDV47uf34pQrNpuWO7kQfsM
-mKMH6Gf6COfSIbLuejdzSOUAmjkFpm+nwBkka1eHdAy4ALn9wNQz3w==
------END RSA PRIVATE KEY-----
-`
-
-func TestBerToDer(t *testing.T) {
- _, err := exec.LookPath("openssl")
- if err != nil {
- t.Skipf("OpenSSL not available skipping test.")
- }
-
- msg := new(bytes.Buffer)
- ui := &packersdk.BasicUi{
- Reader: new(bytes.Buffer),
- Writer: msg,
- }
-
- // Test - a DER encoded key comes back unchanged.
- newKey := string(berToDer([]byte(der_encoded_key), ui))
- if newKey != der_encoded_key {
- t.Errorf("Trying to convert a DER encoded key should return the same key.")
- }
- if string(msg.Bytes()) != "" {
- t.Errorf("Doing nothing with a DER encoded key result in no messages to the UI .")
- }
-
- // Test - a BER encoded key should be converted to DER.
- newKey = string(berToDer([]byte(ber_encoded_key), ui))
- _, err = ssh.ParsePrivateKey([]byte(newKey))
- if err != nil {
- t.Errorf("Trying to convert a BER encoded key should return a DER encoded key parsable by Go.")
- }
- if string(msg.Bytes()) != "Successfully converted BER encoded SSH key to DER encoding.\n" {
- t.Errorf("Trying to convert a BER encoded key should tell the UI about the success.")
- }
-}
diff --git a/builder/openstack/step_load_flavor.go b/builder/openstack/step_load_flavor.go
deleted file mode 100644
index 59b460cab..000000000
--- a/builder/openstack/step_load_flavor.go
+++ /dev/null
@@ -1,63 +0,0 @@
-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) {
-}
diff --git a/builder/openstack/step_run_source_server.go b/builder/openstack/step_run_source_server.go
deleted file mode 100644
index 94c777d55..000000000
--- a/builder/openstack/step_run_source_server.go
+++ /dev/null
@@ -1,173 +0,0 @@
-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)
-}
diff --git a/builder/openstack/step_source_image_info.go b/builder/openstack/step_source_image_info.go
deleted file mode 100644
index ae3071f68..000000000
--- a/builder/openstack/step_source_image_info.go
+++ /dev/null
@@ -1,201 +0,0 @@
-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
- }
- }
-}
diff --git a/builder/openstack/step_stop_server.go b/builder/openstack/step_stop_server.go
deleted file mode 100644
index 3fbc3ed2a..000000000
--- a/builder/openstack/step_stop_server.go
+++ /dev/null
@@ -1,59 +0,0 @@
-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) {}
diff --git a/builder/openstack/step_update_image_mindisk.go b/builder/openstack/step_update_image_mindisk.go
deleted file mode 100644
index 03a2cd918..000000000
--- a/builder/openstack/step_update_image_mindisk.go
+++ /dev/null
@@ -1,58 +0,0 @@
-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...
-}
diff --git a/builder/openstack/step_update_image_tags.go b/builder/openstack/step_update_image_tags.go
deleted file mode 100644
index 979136ef5..000000000
--- a/builder/openstack/step_update_image_tags.go
+++ /dev/null
@@ -1,58 +0,0 @@
-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...
-}
diff --git a/builder/openstack/step_update_image_visibility.go b/builder/openstack/step_update_image_visibility.go
deleted file mode 100644
index 5c3b2605d..000000000
--- a/builder/openstack/step_update_image_visibility.go
+++ /dev/null
@@ -1,57 +0,0 @@
-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...
-}
diff --git a/builder/openstack/step_wait_for_rackconnect.go b/builder/openstack/step_wait_for_rackconnect.go
deleted file mode 100644
index 63bf1cf87..000000000
--- a/builder/openstack/step_wait_for_rackconnect.go
+++ /dev/null
@@ -1,54 +0,0 @@
-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) {
-}
diff --git a/builder/openstack/version/version.go b/builder/openstack/version/version.go
deleted file mode 100644
index 3161752de..000000000
--- a/builder/openstack/version/version.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package version
-
-import (
- "github.com/hashicorp/packer-plugin-sdk/version"
- packerVersion "github.com/hashicorp/packer/version"
-)
-
-var OpenstackPluginVersion *version.PluginVersion
-
-func init() {
- OpenstackPluginVersion = version.InitializePluginVersion(
- packerVersion.Version, packerVersion.VersionPrerelease)
-}
diff --git a/builder/openstack/volume.go b/builder/openstack/volume.go
deleted file mode 100644
index 162ed59c4..000000000
--- a/builder/openstack/volume.go
+++ /dev/null
@@ -1,76 +0,0 @@
-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
-}
diff --git a/website/content/docs/builders/openstack.mdx b/website/content/docs/builders/openstack.mdx
deleted file mode 100644
index da60b0c8e..000000000
--- a/website/content/docs/builders/openstack.mdx
+++ /dev/null
@@ -1,334 +0,0 @@
----
-description: >
- The openstack Packer builder is able to create new images for use with
-
- OpenStack. The builder takes a source image, runs any provisioning necessary
- on
-
- the image after launching it, then creates a new reusable image. This reusable
-
- image can then be used as the foundation of new servers that are launched
-
- within OpenStack.
-page_title: OpenStack - Builders
----
-
-# OpenStack Builder
-
-Type: `openstack`
-Artifact BuilderId: `mitchellh.openstack`
-
-The `openstack` Packer builder is able to create new images for use with
-[OpenStack](http://www.openstack.org). The builder takes a source image, runs
-any provisioning necessary on the image after launching it, then creates a new
-reusable image. This reusable image can then be used as the foundation of new
-servers that are launched within OpenStack. The builder will create temporary
-keypairs that provide temporary access to the server while the image is being
-created. This simplifies configuration quite a bit.
-
-The builder does _not_ manage images. Once it creates an image, it is up to you
-to use it or delete it.
-
-~> **Note:** To use OpenStack builder with the OpenStack Newton (Oct 2016)
-or earlier, we recommend you use Packer v1.1.2 or earlier version.
-
-~> **OpenStack Liberty or later requires OpenSSL!** To use the OpenStack
-builder with OpenStack Liberty (Oct 2015) or later you need to have OpenSSL
-installed _if you are using temporary key pairs_, i.e. don't use
-[`ssh_keypair_name`](#ssh_keypair_name) nor
-[`ssh_password`](#ssh_password). All major
-OS'es have OpenSSL installed by default except Windows. This have been resolved
-in OpenStack Ocata(Feb 2017).
-
-~> **Note:** OpenStack Block Storage volume support is available only for
-V3 Block Storage API. It's available in OpenStack since Mitaka release (Apr
-2016).
-
-## Configuration Reference
-
-There are many configuration options available for the builder. They are
-segmented below into two categories: required and optional parameters. Within
-each category, the available configuration keys are alphabetized.
-
-In addition to the options listed here, a
-[communicator](/docs/templates/legacy_json_templates/communicator) can be configured for this
-builder.
-
-### Required:
-
-@include 'builder/openstack/AccessConfig-required.mdx'
-
-@include 'builder/openstack/ImageConfig-required.mdx'
-
-@include 'builder/openstack/RunConfig-required.mdx'
-
-### Optional:
-
-@include 'builder/openstack/AccessConfig-not-required.mdx'
-
-@include 'builder/openstack/ImageConfig-not-required.mdx'
-
-@include 'builder/openstack/RunConfig-not-required.mdx'
-
-### Communicator Configuration
-
-#### Optional:
-
-@include 'packer-plugin-sdk/communicator/Config-not-required.mdx'
-
-@include 'packer-plugin-sdk/communicator/SSH-not-required.mdx'
-
-@include 'packer-plugin-sdk/communicator/SSHTemporaryKeyPair-not-required.mdx'
-
-@include 'packer-plugin-sdk/communicator/SSH-Key-Pair-Name-not-required.mdx'
-
-@include 'packer-plugin-sdk/communicator/SSH-Private-Key-File-not-required.mdx'
-
-@include 'packer-plugin-sdk/communicator/SSH-Agent-Auth-not-required.mdx'
-
-@include 'packer-plugin-sdk/communicator/SSHInterface-not-required.mdx'
-
-## Basic Example: DevStack
-
-Here is a basic example. This is a example to build on DevStack running in a
-VM.
-
-
-
-
-```json
-{
- "type": "openstack",
- "identity_endpoint": "http://:5000/v3",
- "tenant_name": "admin",
- "domain_name": "Default",
- "username": "admin",
- "password": "",
- "region": "RegionOne",
- "ssh_username": "root",
- "image_name": "Test image",
- "source_image": "",
- "flavor": "m1.tiny",
- "insecure": "true"
-}
-```
-
-
-
-
-```hcl
-builders "openstack" {
- identity_endpoint = "http://:5000/v3"
- tenant_name = "admin"
- domain_name = "Default"
- username = "admin"
- password = ""
- region = "RegionOne"
- ssh_username = "root"
- image_name = "Test image"
- source_image = ""
- flavor = "m1.tiny"
- insecure = "true"
-}
-```
-
-
-
-
-## Basic Example: Rackspace public cloud
-
-Here is a basic example. This is a working example to build a Ubuntu 12.04 LTS
-(Precise Pangolin) on Rackspace OpenStack cloud offering.
-
-
-
-
-```json
-{
- "type": "openstack",
- "username": "foo",
- "password": "foo",
- "region": "DFW",
- "ssh_username": "root",
- "image_name": "Test image",
- "source_image": "23b564c9-c3e6-49f9-bc68-86c7a9ab5018",
- "flavor": "2"
-}
-```
-
-
-
-
-
-```hcl
-builders "openstack" {
- username = "foo"
- password = "foo"
- region = "DFW"
- ssh_username = "root"
- image_name = "Test image"
- source_image = "23b564c9-c3e6-49f9-bc68-86c7a9ab5018"
- flavor = "2"
-}
-```
-
-
-
-
-## Basic Example: Private OpenStack cloud
-
-This example builds an Ubuntu 14.04 image on a private OpenStack cloud, powered
-by Metacloud.
-
-
-
-
-```json
-{
- "type": "openstack",
- "ssh_username": "root",
- "image_name": "ubuntu1404_packer_test_1",
- "source_image": "91d9c168-d1e5-49ca-a775-3bfdbb6c97f1",
- "flavor": "2"
-}
-```
-
-
-
-
-
-```hcl
-builders "openstack" {
- ssh_username = "root"
- image_name = "ubuntu1404_packer_test_1"
- source_image = "91d9c168-d1e5-49ca-a775-3bfdbb6c97f1"
- flavor = "2"
-}
-```
-
-
-
-
-In this case, the connection information for connecting to OpenStack doesn't
-appear in the template. That is because I source a standard OpenStack script
-with environment variables set before I run this. This script is setting
-environment variables like:
-
-- `OS_AUTH_URL`
-- `OS_TENANT_ID`
-- `OS_USERNAME`
-- `OS_PASSWORD`
-
-This is slightly different when identity v3 is used:
-
-- `OS_AUTH_URL`
-- `OS_USERNAME`
-- `OS_PASSWORD`
-- `OS_DOMAIN_NAME`
-- `OS_TENANT_NAME`
-
-This will authenticate the user on the domain and scope you to the project. A
-tenant is the same as a project. It's optional to use names or IDs in v3. This
-means you can use `OS_USERNAME` or `OS_USERID`, `OS_TENANT_ID` or
-`OS_TENANT_NAME` and `OS_DOMAIN_ID` or `OS_DOMAIN_NAME`.
-
-The above example would be equivalent to an RC file looking like this :
-
-```shell
-export OS_AUTH_URL="https://identity.myprovider/v3"
-export OS_USERNAME="myuser"
-export OS_PASSWORD="password"
-export OS_USER_DOMAIN_NAME="mydomain"
-export OS_PROJECT_DOMAIN_NAME="mydomain"
-```
-
-## Basic Example: Instance with Block Storage root volume
-
-A basic example of Instance with a remote root Block Storage service volume.
-This is a working example to build an image on private OpenStack cloud powered
-by Selectel VPC.
-
-
-
-
-```json
-{
- "type": "openstack",
- "identity_endpoint": "https://api.selvpc.com/identity/v3",
- "tenant_id": "2e90c5c04c7b4c509be78723e2b55b77",
- "username": "foo",
- "password": "foo",
- "region": "ru-3",
- "ssh_username": "root",
- "image_name": "Test image",
- "source_image": "5f58ea7e-6264-4939-9d0f-0c23072b1132",
- "networks": "9aab504e-bedf-48af-9256-682a7fa3dabb",
- "flavor": "1001",
- "availability_zone": "ru-3a",
- "use_blockstorage_volume": true,
- "volume_type": "fast.ru-3a"
-}
-```
-
-
-
-
-
-```hcl
-builders "openstack" {
- identity_endpoint = "https://api.selvpc.com/identity/v3"
- tenant_id = "2e90c5c04c7b4c509be78723e2b55b77"
- username = "foo"
- password = "foo"
- region = "ru-3"
- ssh_username = "root"
- image_name = "Test image"
- source_image = "5f58ea7e-6264-4939-9d0f-0c23072b1132"
- networks = "9aab504e-bedf-48af-9256-682a7fa3dabb"
- flavor = "1001"
- availability_zone = "ru-3a"
- use_blockstorage_volume = true
- volume_type = "fast.ru-3a"
-}
-```
-
-
-
-
-## Notes on OpenStack Authorization
-
-The simplest way to get all settings for authorization against OpenStack is to
-go into the OpenStack Dashboard (Horizon) select your _Project_ and navigate
-_Project, Access & Security_, select _API Access_ and _Download OpenStack RC
-File v3_. Source the file, and select your wanted region by setting environment
-variable `OS_REGION_NAME` or `OS_REGION_ID` and
-`export OS_TENANT_NAME=$OS_PROJECT_NAME` or
-`export OS_TENANT_ID=$OS_PROJECT_ID`.
-
-~> `OS_TENANT_NAME` or `OS_TENANT_ID` must be used even with Identity v3,
-`OS_PROJECT_NAME` and `OS_PROJECT_ID` has no effect in Packer.
-
-To troubleshoot authorization issues test you environment variables with the
-OpenStack cli. It can be installed with
-
- $ pip install --user python-openstackclient
-
-### Authorize Using Tokens
-
-To authorize with a access token only `identity_endpoint` and `token` is
-needed, and possibly `tenant_name` or `tenant_id` depending on your token type.
-Or use the following environment variables:
-
-- `OS_AUTH_URL`
-- `OS_TOKEN`
-- One of `OS_TENANT_NAME` or `OS_TENANT_ID`
-
-### Authorize Using Application Credential
-
-To authorize with an application credential, only `identity_endpoint`,
-`application_credential_id`, and `application_credential_secret` are needed.
-Or use the following environment variables:
-
-- `OS_AUTH_URL`
-- `OS_APPLICATION_CREDENTIAL_ID`
-- `OS_APPLICATION_CREDENTIAL_SECRET`