From 3bdf1f184940ac460f5f56e69ab27b42a5a351d8 Mon Sep 17 00:00:00 2001 From: Matthew Hooker Date: Tue, 17 Jan 2017 14:38:49 -0800 Subject: [PATCH] openstack: Add support for token authorization and cloud.yaml via config options `cloud` and `token` and environment variables OS_CLOUD and OS_TOKEN. --- builder/openstack/access_config.go | 59 +++++++++++++++---- .../source/docs/builders/openstack.html.md | 49 +++++++++++---- 2 files changed, 85 insertions(+), 23 deletions(-) diff --git a/builder/openstack/access_config.go b/builder/openstack/access_config.go index 5d9c2b39b..a5ab91b60 100644 --- a/builder/openstack/access_config.go +++ b/builder/openstack/access_config.go @@ -2,14 +2,14 @@ package openstack import ( "crypto/tls" - "fmt" - "os" - "crypto/x509" + "fmt" "io/ioutil" + "os" "github.com/gophercloud/gophercloud" "github.com/gophercloud/gophercloud/openstack" + "github.com/gophercloud/utils/openstack/clientconfig" "github.com/hashicorp/go-cleanhttp" "github.com/hashicorp/packer/template/interpolate" ) @@ -30,6 +30,8 @@ type AccessConfig struct { CACertFile string `mapstructure:"cacert"` ClientCertFile string `mapstructure:"cert"` ClientKeyFile string `mapstructure:"key"` + Token string `mapstructure:"token"` + Cloud string `mapstructure:"cloud"` osClient *gophercloud.ProviderClient } @@ -42,10 +44,6 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { return []error{fmt.Errorf("Invalid endpoint type provided")} } - if c.Region == "" { - c.Region = os.Getenv("OS_REGION_NAME") - } - // Legacy RackSpace stuff. We're keeping this around to keep things BC. if c.Password == "" { c.Password = os.Getenv("SDK_PASSWORD") @@ -59,6 +57,15 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { 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") } @@ -69,8 +76,39 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { c.ClientKeyFile = os.Getenv("OS_KEY") } - // Get as much as possible from the end - ao, _ := openstack.AuthOptionsFromEnv() + 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 @@ -87,6 +125,7 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { {&c.TenantName, &ao.TenantName}, {&c.DomainID, &ao.DomainID}, {&c.DomainName, &ao.DomainName}, + {&c.Token, &ao.TokenID}, } for _, s := range overrides { if *s.From != "" { @@ -132,7 +171,7 @@ func (c *AccessConfig) Prepare(ctx *interpolate.Context) []error { client.HTTPClient.Transport = transport // Auth - err = openstack.Authenticate(client, ao) + err = openstack.Authenticate(client, *ao) if err != nil { return []error{err} } diff --git a/website/source/docs/builders/openstack.html.md b/website/source/docs/builders/openstack.html.md index cbdea1f2a..c6059da50 100644 --- a/website/source/docs/builders/openstack.html.md +++ b/website/source/docs/builders/openstack.html.md @@ -25,7 +25,7 @@ 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) +~> **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 @@ -56,7 +56,7 @@ builder. - `identity_endpoint` (string) - The URL to the OpenStack Identity service. If not specified, Packer will use the environment variables `OS_AUTH_URL`, - if set. + if set. This is not required if using `cloud.yaml`. - `source_image` (string) - 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. @@ -69,11 +69,14 @@ builder. - `username` or `user_id` (string) - 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. + variable `OS_USERNAME` or `OS_USERID`, if set. This is not required if + using access token instead of password or if using `cloud.yaml`. - `password` (string) - The password used to connect to the OpenStack service. If not specified, Packer will use the environment variables `OS_PASSWORD`, - if set. + if set. This is not required if using access token instead of password or + if using `cloud.yaml`. + ### Optional: @@ -82,14 +85,20 @@ builder. cluster will be used. This may be required for some OpenStack clusters. - `cacert` (string) - Custom CA certificate file path. - If omitted the OS\_CACERT environment variable can be used. + If omitted the `OS_CACERT` environment variable can be used. + +- `cert` (string) - Client certificate file path for SSL client authentication. + If omitted the `OS_CERT` environment variable can be used. + +- `cloud` (string) - 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. - `config_drive` (boolean) - Whether or not nova should use ConfigDrive for cloud-init metadata. -- `cert` (string) - Client certificate file path for SSL client authentication. - If omitted the OS\_CERT environment variable can be used. - - `domain_name` or `domain_id` (string) - 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. @@ -105,7 +114,7 @@ builder. - `image_members` (array of strings) - 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. + "tenant") with whom the image is shared. - `image_visibility` (string) - One of "public", "private", "shared", or "community". @@ -114,7 +123,7 @@ builder. done over an insecure connection. By default this is false. - `key` (string) - Client private key file path for SSL client authentication. - If omitted the OS\_KEY environment variable can be used. + If omitted the `OS_KEY` environment variable can be used. - `metadata` (object of key/value strings) - Glance metadata that will be applied to the image. @@ -181,6 +190,9 @@ builder. 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. +- `token` (string) - the token (id) to use with token based authorization. + Packer will use the environment variable `OS_TOKEN`, if set. + - `use_floating_ip` (boolean) - *Deprecated* use `floating_ip` or `floating_ip_pool` instead. @@ -280,11 +292,12 @@ export OS_PROJECT_DOMAIN_NAME="mydomain" ## Notes on OpenStack Authorization -The simplest way to get all settings for authorization agains OpenStack is to +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`. +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. @@ -293,3 +306,13 @@ 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`