Merge pull request #768 from devcamcar/openstack-env-support

builder/openstack: Add support for standard OpenStack environment variables
This commit is contained in:
Mitchell Hashimoto 2014-04-21 21:11:37 -07:00
commit 159587daf4
2 changed files with 34 additions and 25 deletions

View File

@ -2,17 +2,20 @@ package openstack
import ( import (
"fmt" "fmt"
"github.com/mitchellh/packer/common"
"github.com/mitchellh/packer/packer" "github.com/mitchellh/packer/packer"
"github.com/rackspace/gophercloud" "github.com/rackspace/gophercloud"
"net/http" "net/http"
"net/url" "net/url"
"os" "os"
"strings"
) )
// AccessConfig is for common configuration related to openstack access // AccessConfig is for common configuration related to openstack access
type AccessConfig struct { type AccessConfig struct {
Username string `mapstructure:"username"` Username string `mapstructure:"username"`
Password string `mapstructure:"password"` Password string `mapstructure:"password"`
ApiKey string `mapstructure:"api_key"`
Project string `mapstructure:"project"` Project string `mapstructure:"project"`
Provider string `mapstructure:"provider"` Provider string `mapstructure:"provider"`
RawRegion string `mapstructure:"region"` RawRegion string `mapstructure:"region"`
@ -22,39 +25,35 @@ type AccessConfig struct {
// Auth returns a valid Auth object for access to openstack services, or // Auth returns a valid Auth object for access to openstack services, or
// an error if the authentication couldn't be resolved. // an error if the authentication couldn't be resolved.
func (c *AccessConfig) Auth() (gophercloud.AccessProvider, error) { func (c *AccessConfig) Auth() (gophercloud.AccessProvider, error) {
username := c.Username c.Username = common.CoalesceVals(c.Username, os.Getenv("SDK_USERNAME"), os.Getenv("OS_USERNAME"))
password := c.Password c.Password = common.CoalesceVals(c.Password, os.Getenv("SDK_PASSWORD"), os.Getenv("OS_PASSWORD"))
project := c.Project c.ApiKey = common.CoalesceVals(c.ApiKey, os.Getenv("SDK_API_KEY"))
provider := c.Provider c.Project = common.CoalesceVals(c.Project, os.Getenv("SDK_PROJECT"), os.Getenv("OS_TENANT_NAME"))
proxy := c.ProxyUrl c.Provider = common.CoalesceVals(c.Provider, os.Getenv("SDK_PROVIDER"), os.Getenv("OS_AUTH_URL"))
c.RawRegion = common.CoalesceVals(c.RawRegion, os.Getenv("SDK_REGION"), os.Getenv("OS_REGION_NAME"))
if username == "" { // OpenStack's auto-generated openrc.sh files do not append the suffix
username = os.Getenv("SDK_USERNAME") // /tokens to the authentication URL. This ensures it is present when
} // specifying the URL.
if password == "" { if strings.Contains(c.Provider, "://") && !strings.HasSuffix(c.Provider, "/tokens") {
password = os.Getenv("SDK_PASSWORD") c.Provider += "/tokens"
}
if project == "" {
project = os.Getenv("SDK_PROJECT")
}
if provider == "" {
provider = os.Getenv("SDK_PROVIDER")
} }
authoptions := gophercloud.AuthOptions{ authoptions := gophercloud.AuthOptions{
Username: username, Username: c.Username,
Password: password, Password: c.Password,
ApiKey: c.ApiKey,
AllowReauth: true, AllowReauth: true,
} }
if project != "" { if c.Project != "" {
authoptions.TenantName = project authoptions.TenantName = c.Project
} }
// For corporate networks it may be the case where we want our API calls // For corporate networks it may be the case where we want our API calls
// to be sent through a separate HTTP proxy than external traffic. // to be sent through a separate HTTP proxy than external traffic.
if proxy != "" { if c.ProxyUrl != "" {
url, err := url.Parse(proxy) url, err := url.Parse(c.ProxyUrl)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -64,11 +63,11 @@ func (c *AccessConfig) Auth() (gophercloud.AccessProvider, error) {
http.DefaultTransport = &http.Transport{Proxy: http.ProxyURL(url)} http.DefaultTransport = &http.Transport{Proxy: http.ProxyURL(url)}
} }
return gophercloud.Authenticate(provider, authoptions) return gophercloud.Authenticate(c.Provider, authoptions)
} }
func (c *AccessConfig) Region() string { func (c *AccessConfig) Region() string {
return c.RawRegion return common.CoalesceVals(c.RawRegion, os.Getenv("SDK_REGION"), os.Getenv("OS_REGION_NAME"))
} }
func (c *AccessConfig) Prepare(t *packer.ConfigTemplate) []error { func (c *AccessConfig) Prepare(t *packer.ConfigTemplate) []error {
@ -83,6 +82,7 @@ func (c *AccessConfig) Prepare(t *packer.ConfigTemplate) []error {
templates := map[string]*string{ templates := map[string]*string{
"username": &c.Username, "username": &c.Username,
"password": &c.Password, "password": &c.Password,
"apiKey": &c.ApiKey,
"provider": &c.Provider, "provider": &c.Provider,
} }
@ -96,7 +96,7 @@ func (c *AccessConfig) Prepare(t *packer.ConfigTemplate) []error {
} }
} }
if c.RawRegion == "" { if c.Region() == "" {
errs = append(errs, fmt.Errorf("region must be specified")) errs = append(errs, fmt.Errorf("region must be specified"))
} }

View File

@ -194,3 +194,12 @@ func decodeConfigHook(raws []interface{}) (mapstructure.DecodeHookFunc, error) {
return v, nil return v, nil
}, nil }, nil
} }
func CoalesceVals(vals ...string) string {
for _, el := range vals {
if el != "" {
return el
}
}
return ""
}