Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ tmp
*~
.direnv/
.envrc
.ruby-version
.ruby-version
.ruby-gemset
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,15 @@ kitchen-openstack is a Test Kitchen driver for OpenStack. It provisions and dest
## Architecture

- Driver class: `Kitchen::Driver::Openstack` in `lib/kitchen/driver/openstack.rb` — extends `Kitchen::Driver::Base` (Driver API v2)
- 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
- Server configuration: `Kitchen::Driver::Openstack::Config` in `lib/kitchen/driver/openstack/config.rb` — server naming helpers
- Helpers: `Kitchen::Driver::Openstack::Helpers` in `lib/kitchen/driver/openstack/helpers.rb` — ohai hints, SSL, server wait
- Networking: `Kitchen::Driver::Openstack::Networking` in `lib/kitchen/driver/openstack/networking.rb` — floating IP allocation, IP resolution
- Server creation: `Kitchen::Driver::Openstack::ServerHelper` in `lib/kitchen/driver/openstack/server_helper.rb` — server creation, image/flavor/network finders
- Volume handling: `Kitchen::Driver::Openstack::Volume` in `lib/kitchen/driver/openstack/volume.rb`
- Version constant: `OPENSTACK_VERSION` in `lib/kitchen/driver/openstack_version.rb` — used by gemspec and release automation
- Configuration uses `default_config` declarations; raise `Kitchen::ActionFailed` for driver errors
- Supports `clouds.yaml` via `openstack_cloud` config or `OS_CLOUD` env var — see `Clouds` module

## Build and Test

Expand Down
123 changes: 121 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Kitchen::OpenStack

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

A Test Kitchen Driver for OpenStack.

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

## Status

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.
This software project is actively maintained by the [OSU Open Source Lab](https://osuosl.org/).

## Requirements

Expand Down Expand Up @@ -47,6 +47,125 @@ gem install kitchen-openstack

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

### Using `clouds.yaml`

This driver supports OpenStack's standard
[`clouds.yaml`](https://docs.openstack.org/python-openstackclient/latest/configuration/index.html)
client configuration file. This allows you to use the same credentials and
endpoint configuration that other OpenStack tools (like the `openstack` CLI)
already use, instead of duplicating them in `kitchen.yml`.

The driver searches for `clouds.yaml` in the standard locations:

1. `OS_CLIENT_CONFIG_FILE` environment variable (if set)
2. `clouds_yaml_path` driver config option (if set)
3. Current directory (`./clouds.yaml`)
4. `~/.config/openstack/clouds.yaml`
5. `/etc/openstack/clouds.yaml`

The first file found is used. A `secure.yaml` file in the same search
locations is also loaded and merged, so you can split secrets out of
`clouds.yaml` following the
[standard convention](https://docs.openstack.org/openstacksdk/latest/user/config/configuration.html#splitting-secrets).

#### Selecting a cloud

Specify which cloud entry to use in one of two ways:

- Set `openstack_cloud` in `kitchen.yml` (takes precedence)
- Set the `OS_CLOUD` environment variable

#### Example `kitchen.yml`

```yaml
driver:
name: openstack
openstack_cloud: mycloud
image_ref: ubuntu-22.04
flavor_ref: m1.small
key_name: my-keypair
```

Or, relying entirely on `OS_CLOUD`:

```bash
export OS_CLOUD=mycloud
```

```yaml
driver:
name: openstack
image_ref: ubuntu-22.04
flavor_ref: m1.small
key_name: my-keypair
```

Settings specified in `kitchen.yml` always take precedence over values from
`clouds.yaml`. For example, you can override just the region:

```yaml
driver:
name: openstack
openstack_cloud: mycloud
openstack_region: RegionTwo
```

#### Using `OS_*` environment variables

The driver recognizes the standard OpenStack `OS_*` environment variables
(e.g. from an `openrc` file). This means you can source your OpenStack
credentials and use them directly without any extra configuration in
`kitchen.yml`:

```bash
source openrc.sh
```

```yaml
driver:
name: openstack
image_ref: ubuntu-22.04
flavor_ref: m1.small
key_name: my-keypair
```

The supported environment variables are:

| Env var | Maps to |
|---|---|
| `OS_AUTH_URL` | `openstack_auth_url` |
| `OS_USERNAME` | `openstack_username` |
| `OS_PASSWORD` | `openstack_api_key` |
| `OS_PROJECT_NAME` | `openstack_project_name` |
| `OS_PROJECT_ID` | `openstack_project_id` |
| `OS_USER_DOMAIN_NAME` | `openstack_user_domain` |
| `OS_USER_DOMAIN_ID` | `openstack_user_domain_id` |
| `OS_PROJECT_DOMAIN_NAME` | `openstack_project_domain` |
| `OS_PROJECT_DOMAIN_ID` | `openstack_project_domain_id` |
| `OS_DOMAIN_ID` | `openstack_domain_id` |
| `OS_DOMAIN_NAME` | `openstack_domain_name` |
| `OS_REGION_NAME` | `openstack_region` |
| `OS_INTERFACE` | `openstack_endpoint_type` |
| `OS_IDENTITY_API_VERSION` | `openstack_identity_api_version` |
| `OS_APPLICATION_CREDENTIAL_ID` | `openstack_application_credential_id` |
| `OS_APPLICATION_CREDENTIAL_SECRET` | `openstack_application_credential_secret` |
| `OS_CACERT` | `ssl_ca_file` |

#### Configuration precedence

The driver follows the upstream OpenStack SDK precedence order:

1. **`kitchen.yml`** — explicit driver config always wins
2. **`OS_*` env vars** — override `clouds.yaml` values
3. **`clouds.yaml`** (merged with `secure.yaml`) — base configuration

#### New driver config options

| Option | Default | Description |
|---|---|---|
| `openstack_cloud` | `nil` | Name of the cloud entry in `clouds.yaml`. Falls back to the `OS_CLOUD` env var. |
| `clouds_yaml_path` | `nil` | Explicit path to a `clouds.yaml` file, inserted into the search path. |

## Development

Pull requests are very welcome! Make sure your patches are well tested.
Expand Down
21 changes: 18 additions & 3 deletions lib/kitchen/driver/openstack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
require "fog/openstack"
require "yaml"
require_relative "openstack_version"
require_relative "openstack/clouds"
require_relative "openstack/config"
require_relative "openstack/helpers"
require_relative "openstack/networking"
Expand All @@ -35,6 +36,7 @@ module Kitchen
module Driver
# This takes from the Base Class and creates the OpenStack driver.
class Openstack < Kitchen::Driver::Base
include Clouds
include Config
include Helpers
include Networking
Expand All @@ -43,7 +45,17 @@ class Openstack < Kitchen::Driver::Base
kitchen_driver_api_version 2
plugin_version Kitchen::Driver::OPENSTACK_VERSION

default_config :openstack_cloud, nil
default_config :clouds_yaml_path, nil
default_config :server_name, nil

# Merge clouds.yaml values into config so they are visible in
# `kitchen diagnose` and available to all driver methods.
def finalize_config!(instance)
super
apply_clouds_config
self
end
default_config :server_name_prefix, nil
default_config :key_name, nil
default_config :port, "22"
Expand Down Expand Up @@ -84,7 +96,10 @@ def create(state)
debug "Waiting for a max time of:#{config[:glance_cache_wait_timeout]} seconds for OpenStack server to be in ACTIVE state"
server.wait_for(config[:glance_cache_wait_timeout]) do
sleep(1)
raise(Kitchen::InstanceFailure, "OpenStack server ID <#{state[:server_id]}> build failed to ERROR state") if failed?
if failed?
raise(Kitchen::InstanceFailure,
"OpenStack server ID <#{state[:server_id]}> build failed to ERROR state")
end

ready?
end
Expand All @@ -98,8 +113,8 @@ def create(state)
state[:hostname] = get_ip(server)
wait_for_server(state)
add_ohai_hint(state)
rescue Fog::Errors::Error, Excon::Errors::Error => ex
raise ActionFailed, ex.message
rescue Fog::Errors::Error, Excon::Errors::Error => e
raise ActionFailed, e.message
end

def destroy(state)
Expand Down
Loading
Loading