Skip to content

Commit 66db1c2

Browse files
authored
feat: add OpenStack clouds.yaml and OS_* env var support (#236)
Add support for OpenStack's standard clouds.yaml client configuration file and OS_* environment variables, following the upstream openstacksdk precedence order: 1. kitchen.yml (explicit driver config always wins) 2. OS_* environment variables (override clouds.yaml) 3. clouds.yaml merged with secure.yaml (base configuration) The driver reads clouds.yaml from standard search paths and maps entries to Fog OpenStack config keys. Standard OS_* env vars (OS_AUTH_URL, OS_USERNAME, OS_PASSWORD, etc.) are also recognized so that sourcing an openrc file works without duplicating values in kitchen.yml. New driver config options: - openstack_cloud: selects a cloud entry (falls back to OS_CLOUD) - clouds_yaml_path: explicit path to a clouds.yaml file Closes #212 Signed-off-by: Lance Albertson <lance@osuosl.org>
1 parent 633e8f1 commit 66db1c2

7 files changed

Lines changed: 1094 additions & 6 deletions

File tree

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,5 @@ tmp
2020
*~
2121
.direnv/
2222
.envrc
23-
.ruby-version
23+
.ruby-version
24+
.ruby-gemset

AGENTS.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,15 @@ kitchen-openstack is a Test Kitchen driver for OpenStack. It provisions and dest
1414
## Architecture
1515

1616
- Driver class: `Kitchen::Driver::Openstack` in `lib/kitchen/driver/openstack.rb` — extends `Kitchen::Driver::Base` (Driver API v2)
17+
- Clouds.yaml support: `Kitchen::Driver::Openstack::Clouds` in `lib/kitchen/driver/openstack/clouds.rb` — parses OpenStack `clouds.yaml`/`secure.yaml` and translates to Fog config
18+
- Server configuration: `Kitchen::Driver::Openstack::Config` in `lib/kitchen/driver/openstack/config.rb` — server naming helpers
19+
- Helpers: `Kitchen::Driver::Openstack::Helpers` in `lib/kitchen/driver/openstack/helpers.rb` — ohai hints, SSL, server wait
20+
- Networking: `Kitchen::Driver::Openstack::Networking` in `lib/kitchen/driver/openstack/networking.rb` — floating IP allocation, IP resolution
21+
- Server creation: `Kitchen::Driver::Openstack::ServerHelper` in `lib/kitchen/driver/openstack/server_helper.rb` — server creation, image/flavor/network finders
1722
- Volume handling: `Kitchen::Driver::Openstack::Volume` in `lib/kitchen/driver/openstack/volume.rb`
1823
- Version constant: `OPENSTACK_VERSION` in `lib/kitchen/driver/openstack_version.rb` — used by gemspec and release automation
1924
- Configuration uses `default_config` declarations; raise `Kitchen::ActionFailed` for driver errors
25+
- Supports `clouds.yaml` via `openstack_cloud` config or `OS_CLOUD` env var — see `Clouds` module
2026

2127
## Build and Test
2228

README.md

Lines changed: 121 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Kitchen::OpenStack
22

33
![Gem Version](https://img.shields.io/gem/v/kitchen-openstack.svg)
4-
![CI](https://github.com/test-kitchen/kitchen-openstack/workflows/CI/badge.svg)
4+
![CI](https://github.com/test-kitchen/kitchen-openstack/actions/workflows/lint.yml/badge.svg)
55

66
A Test Kitchen Driver for OpenStack.
77

@@ -11,7 +11,7 @@ Shamelessly copied from [Fletcher Nichol](https://github.com/fnichol)'s awesome
1111

1212
## Status
1313

14-
This software project is no longer under active development as it has no active maintainers. The software may continue to work for some or all use cases, but issues filed in GitHub will most likely not be triaged. If a new maintainer is interested in working on this project please come chat with us in #test-kitchen on Chef Community Slack.
14+
This software project is actively maintained by the [OSU Open Source Lab](https://osuosl.org/).
1515

1616
## Requirements
1717

@@ -47,6 +47,125 @@ gem install kitchen-openstack
4747

4848
See <https://kitchen.ci/docs/drivers/openstack/> for documentation.
4949

50+
### Using `clouds.yaml`
51+
52+
This driver supports OpenStack's standard
53+
[`clouds.yaml`](https://docs.openstack.org/python-openstackclient/latest/configuration/index.html)
54+
client configuration file. This allows you to use the same credentials and
55+
endpoint configuration that other OpenStack tools (like the `openstack` CLI)
56+
already use, instead of duplicating them in `kitchen.yml`.
57+
58+
The driver searches for `clouds.yaml` in the standard locations:
59+
60+
1. `OS_CLIENT_CONFIG_FILE` environment variable (if set)
61+
2. `clouds_yaml_path` driver config option (if set)
62+
3. Current directory (`./clouds.yaml`)
63+
4. `~/.config/openstack/clouds.yaml`
64+
5. `/etc/openstack/clouds.yaml`
65+
66+
The first file found is used. A `secure.yaml` file in the same search
67+
locations is also loaded and merged, so you can split secrets out of
68+
`clouds.yaml` following the
69+
[standard convention](https://docs.openstack.org/openstacksdk/latest/user/config/configuration.html#splitting-secrets).
70+
71+
#### Selecting a cloud
72+
73+
Specify which cloud entry to use in one of two ways:
74+
75+
- Set `openstack_cloud` in `kitchen.yml` (takes precedence)
76+
- Set the `OS_CLOUD` environment variable
77+
78+
#### Example `kitchen.yml`
79+
80+
```yaml
81+
driver:
82+
name: openstack
83+
openstack_cloud: mycloud
84+
image_ref: ubuntu-22.04
85+
flavor_ref: m1.small
86+
key_name: my-keypair
87+
```
88+
89+
Or, relying entirely on `OS_CLOUD`:
90+
91+
```bash
92+
export OS_CLOUD=mycloud
93+
```
94+
95+
```yaml
96+
driver:
97+
name: openstack
98+
image_ref: ubuntu-22.04
99+
flavor_ref: m1.small
100+
key_name: my-keypair
101+
```
102+
103+
Settings specified in `kitchen.yml` always take precedence over values from
104+
`clouds.yaml`. For example, you can override just the region:
105+
106+
```yaml
107+
driver:
108+
name: openstack
109+
openstack_cloud: mycloud
110+
openstack_region: RegionTwo
111+
```
112+
113+
#### Using `OS_*` environment variables
114+
115+
The driver recognizes the standard OpenStack `OS_*` environment variables
116+
(e.g. from an `openrc` file). This means you can source your OpenStack
117+
credentials and use them directly without any extra configuration in
118+
`kitchen.yml`:
119+
120+
```bash
121+
source openrc.sh
122+
```
123+
124+
```yaml
125+
driver:
126+
name: openstack
127+
image_ref: ubuntu-22.04
128+
flavor_ref: m1.small
129+
key_name: my-keypair
130+
```
131+
132+
The supported environment variables are:
133+
134+
| Env var | Maps to |
135+
|---|---|
136+
| `OS_AUTH_URL` | `openstack_auth_url` |
137+
| `OS_USERNAME` | `openstack_username` |
138+
| `OS_PASSWORD` | `openstack_api_key` |
139+
| `OS_PROJECT_NAME` | `openstack_project_name` |
140+
| `OS_PROJECT_ID` | `openstack_project_id` |
141+
| `OS_USER_DOMAIN_NAME` | `openstack_user_domain` |
142+
| `OS_USER_DOMAIN_ID` | `openstack_user_domain_id` |
143+
| `OS_PROJECT_DOMAIN_NAME` | `openstack_project_domain` |
144+
| `OS_PROJECT_DOMAIN_ID` | `openstack_project_domain_id` |
145+
| `OS_DOMAIN_ID` | `openstack_domain_id` |
146+
| `OS_DOMAIN_NAME` | `openstack_domain_name` |
147+
| `OS_REGION_NAME` | `openstack_region` |
148+
| `OS_INTERFACE` | `openstack_endpoint_type` |
149+
| `OS_IDENTITY_API_VERSION` | `openstack_identity_api_version` |
150+
| `OS_APPLICATION_CREDENTIAL_ID` | `openstack_application_credential_id` |
151+
| `OS_APPLICATION_CREDENTIAL_SECRET` | `openstack_application_credential_secret` |
152+
| `OS_CACERT` | `ssl_ca_file` |
153+
154+
#### Configuration precedence
155+
156+
The driver follows the upstream OpenStack SDK precedence order:
157+
158+
1. **`kitchen.yml`** — explicit driver config always wins
159+
2. **`OS_*` env vars** — override `clouds.yaml` values
160+
3. **`clouds.yaml`** (merged with `secure.yaml`) — base configuration
161+
162+
#### New driver config options
163+
164+
| Option | Default | Description |
165+
|---|---|---|
166+
| `openstack_cloud` | `nil` | Name of the cloud entry in `clouds.yaml`. Falls back to the `OS_CLOUD` env var. |
167+
| `clouds_yaml_path` | `nil` | Explicit path to a `clouds.yaml` file, inserted into the search path. |
168+
50169
## Development
51170

52171
Pull requests are very welcome! Make sure your patches are well tested.

lib/kitchen/driver/openstack.rb

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
require "fog/openstack"
2626
require "yaml"
2727
require_relative "openstack_version"
28+
require_relative "openstack/clouds"
2829
require_relative "openstack/config"
2930
require_relative "openstack/helpers"
3031
require_relative "openstack/networking"
@@ -35,6 +36,7 @@ module Kitchen
3536
module Driver
3637
# This takes from the Base Class and creates the OpenStack driver.
3738
class Openstack < Kitchen::Driver::Base
39+
include Clouds
3840
include Config
3941
include Helpers
4042
include Networking
@@ -43,7 +45,17 @@ class Openstack < Kitchen::Driver::Base
4345
kitchen_driver_api_version 2
4446
plugin_version Kitchen::Driver::OPENSTACK_VERSION
4547

48+
default_config :openstack_cloud, nil
49+
default_config :clouds_yaml_path, nil
4650
default_config :server_name, nil
51+
52+
# Merge clouds.yaml values into config so they are visible in
53+
# `kitchen diagnose` and available to all driver methods.
54+
def finalize_config!(instance)
55+
super
56+
apply_clouds_config
57+
self
58+
end
4759
default_config :server_name_prefix, nil
4860
default_config :key_name, nil
4961
default_config :port, "22"
@@ -84,7 +96,10 @@ def create(state)
8496
debug "Waiting for a max time of:#{config[:glance_cache_wait_timeout]} seconds for OpenStack server to be in ACTIVE state"
8597
server.wait_for(config[:glance_cache_wait_timeout]) do
8698
sleep(1)
87-
raise(Kitchen::InstanceFailure, "OpenStack server ID <#{state[:server_id]}> build failed to ERROR state") if failed?
99+
if failed?
100+
raise(Kitchen::InstanceFailure,
101+
"OpenStack server ID <#{state[:server_id]}> build failed to ERROR state")
102+
end
88103

89104
ready?
90105
end
@@ -98,8 +113,8 @@ def create(state)
98113
state[:hostname] = get_ip(server)
99114
wait_for_server(state)
100115
add_ohai_hint(state)
101-
rescue Fog::Errors::Error, Excon::Errors::Error => ex
102-
raise ActionFailed, ex.message
116+
rescue Fog::Errors::Error, Excon::Errors::Error => e
117+
raise ActionFailed, e.message
103118
end
104119

105120
def destroy(state)

0 commit comments

Comments
 (0)