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
2 changes: 2 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
--require spec_helper
--color
74 changes: 72 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,73 @@
## 0.1.0 / Unreleased
# kitchen-cloudstack Changelog

* Initial release
## 1.0.0

### Modern Test Kitchen support

The driver was built on `Kitchen::Driver::SSHBase`, which Test Kitchen removed
in 4.0, so the gem installed successfully and then failed to load with a
`NameError`. It is now built on `Kitchen::Driver::Base` and the transport API,
and works with current Test Kitchen, Cinc Workstation and Chef Workstation.

The driver no longer opens its own SSH connections. It determines an address and
credentials, puts them into instance state, and lets the configured transport
connect.

### Windows and WinRM

Setting `transport: name: winrm` is now enough to build Windows instances. Port
forwarding and firewall rules follow the transport's port (5985/5986 rather than
22), and the password CloudStack generates for a password-enabled template is
handed to WinRM automatically.

### `kitchen list` reports real instance state

The driver implements `status`, so Test Kitchen reports state from CloudStack
rather than assuming. An instance destroyed outside Test Kitchen is now
reported accurately.

### Breaking changes

- Requires `test-kitchen >= 3.0`.
- The driver no longer copies `~/.ssh/id_rsa.pub` into every instance's
`authorized_keys`. It was a workaround for the old SSH handling; the transport
is now given real credentials instead. If you relied on it, add the key
through `cloudstack_userdata` or a CloudStack keypair.
- `username` and `port` are no longer defaulted to `root` and `22` by the
driver. Set on the driver they behave as before; left unset, your `transport:`
configuration now applies instead of being silently overridden.
- The `name` driver option has been removed. It never had any effect — the
driver set it but read `server_name` when deploying.

### Bug fixes

- Asynchronous CloudStack jobs are now polled to completion consistently. Four
of the six job checks tested `jobstatus == 0`, treating "still running" as
success and logging an error on the successful result, so failures to create
port forwarding rules, firewall rules, and to release public addresses were
silently ignored.
- Fixed the rescue clauses in teardown, which referenced
`Fog::Compute::Cloudstack::BadRequest`. No such constant exists — it is
`Fog::Cloudstack::Compute::BadRequest` — so an error during teardown raised
`NameError` from the rescue itself instead of being handled.
- Fixed an infinite loop in instance name generation. The truncation loop ran
until the name fit in 64 characters, but each branch stopped shortening at a
floor totalling 67 characters, so a login longer than 16 characters hung
`kitchen create` forever.
- Fixed a `NameError` in `associate_public_ip` when address allocation failed,
where the return value was only assigned on the success path.
- Job ids are now passed to fog as strings rather than hashes. Fog mutates a
hash argument in place, which the old code worked around by re-cloning the
hash on every poll.

### Other

- Added `cloudstack_firewall_cidr` to narrow the firewall rule's source range,
which was previously always `0.0.0.0/0`.
- Added `cloudstack_job_poll_interval` and `cloudstack_job_timeout`. CloudStack
jobs previously polled forever with no timeout.
- Added an RSpec suite. The gem previously had no tests.

## 0.24.0 and earlier

See the [commit history](https://github.com/test-kitchen/kitchen-cloudstack/commits/main).
130 changes: 97 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,39 @@

A [Test Kitchen](https://kitchen.ci/) driver for [Apache CloudStack](https://cloudstack.apache.org/) and Citrix CloudPlatform. It deploys and destroys CloudStack virtual machines so you can test your cookbooks and infrastructure code against them.

> **Compatibility warning**
>
> This driver is built on `Kitchen::Driver::SSHBase`, which was **removed in
> Test Kitchen 4.0**. The gemspec still allows `test-kitchen < 5`, so the gem
> will install alongside a current Test Kitchen and then fail at load time with
> a `NameError`.
>
> To use it today you must pin Test Kitchen below 4.0. That also means it will
> not work with the Test Kitchen bundled in current Cinc Workstation or Chef
> Workstation. Porting the driver onto the modern transport API is the real
> fix, and contributions doing so are very welcome.

<!-- -->

> This documentation uses [Cinc Workstation](https://cinc.sh/) and the `cinc` commands throughout. Everything here works identically with Chef Workstation — see [Using with Chef](#using-with-chef).

## Requirements

- An Apache CloudStack or Citrix CloudPlatform deployment
- API credentials for it: an API key, a secret key, and the API URL
- Test Kitchen older than 4.0, for the reason described above
- Test Kitchen 3.0 or newer, including the version bundled with current Cinc
Workstation and Chef Workstation
- `fog-cloudstack`, installed automatically as a dependency

## Installation

Because of the version pin described above, install this driver into a
project-local bundle rather than into Workstation:
Install the driver alongside Test Kitchen:

```sh
gem install kitchen-cloudstack
```

Or, for a project-local bundle:

```ruby
# Gemfile
source "https://rubygems.org"

gem "test-kitchen", "< 4.0"
gem "test-kitchen"
gem "kitchen-cloudstack"
```

Then:

```sh
bundle install
```

Run the commands below through `bundle exec` so the pinned Test Kitchen is used.
If you installed into a bundle, run the commands below through `bundle exec`.

## Authentication

Expand Down Expand Up @@ -159,32 +149,35 @@ These apply only when the service offering itself does not specify CPU or memory
| `cloudstack_security_group_id` | *unset* | Security group ID, for shared networks. |
| `associate_public_ip` | `false` | Acquire a public IP and set up static NAT automatically. |
| `cloudstack_vm_public_ip` | *unset* | Public IP to connect to, when you configure advanced networking and static NAT yourself. |
| `cloudstack_create_firewall_rule` | `false` | Create a firewall rule opening SSH (port 22) to the public IP. |
| `cloudstack_create_firewall_rule` | `false` | Create a firewall rule opening the transport's port to the public IP. |
| `cloudstack_firewall_cidr` | `0.0.0.0/0` | Source range the firewall rule allows. Narrow this to your own network rather than leaving it open to the internet. |

### SSH and access

| Option | Default | Description |
| --- | --- | --- |
| `username` | `"root"` | User to connect as. |
| `port` | `"22"` | SSH port. |
| `username` | *transport default* | User to connect as. Leave unset to use the `transport:` setting. |
| `port` | *transport default* | Port to connect on. Leave unset to use the `transport:` setting. |
| `password` | *generated by CloudStack* | Password to connect with. By default the driver uses the password CloudStack generates. |
| `cloudstack_ssh_keypair_name` | *unset* | Name of a CloudStack SSH keypair to deploy with. See [SSH keypairs](#ssh-keypairs). |
| `keypair_search_directory` | *see below* | Extra directory to search for the keypair's `.pem` file. |
| `cloudstack_sync_time` | `0` | Seconds to sleep after connecting, to let `cloud-set-guest-password` or `cloud-set-guest-sshkey` finish. Raise this if logins fail intermittently just after boot. |
| `cloudstack_sync_time` | `0` | Seconds to wait before connecting, to let `cloud-set-guest-password` or `cloud-set-guest-sshkey` finish. Raise this if logins fail intermittently just after boot. |

### Naming

| Option | Default | Description |
| --- | --- | --- |
| `server_name` | *generated* | Display name of the VM in CloudStack. |
| `host_name` | *generated* | Hostname set on the VM itself. Useful when long generated hostnames cause `ENAMETOOLONG` errors during a converge. |
| `name` | *generated* | Name used for the instance, generated from the suite name and your login if unset. |

### Other

| Option | Default | Description |
| --- | --- | --- |
| `cloudstack_userdata` | *unset* | User data passed to the VM. Must be a double-quoted string, so escapes such as `\n` are interpreted. |
| `cloudstack_job_poll_interval` | `10` | Seconds between checks on a running CloudStack job. |
| `cloudstack_job_timeout` | `600` | Seconds to wait for a CloudStack job before giving up. Raise this if deploys legitimately take longer. |
| `disable_ssl_validation` | `false` | Skip SSL certificate validation against the API. Only for a deployment without valid certificates. |

## SSH keypairs

Expand All @@ -193,10 +186,10 @@ matching **private** key available as a `.pem` file. The driver looks for a file
named after the keypair with a `.pem` suffix — a keypair called `TestKey` needs
`TestKey.pem` — in these locations:

1. the directory containing your `kitchen.yml`
2. your home directory (`~`)
3. your `~/.ssh` directory
4. the directory given by `keypair_search_directory`, specified without a trailing slash
1. the directory given by `keypair_search_directory`, specified without a trailing slash
2. the directory containing your `kitchen.yml`
3. your home directory (`~`)
4. your `~/.ssh` directory

Note that this file must be the **private** key, not the public key.

Expand All @@ -207,6 +200,69 @@ driver:
keypair_search_directory: /home/me/cloudstack-keys
```

## How credentials reach the instance

The driver does not connect to the instance itself. It works out an address and
a set of credentials, hands them to the configured Test Kitchen transport, and
waits for that transport to become ready. This is what lets the same driver
serve both Linux and Windows instances.

Credentials are chosen in this order:

1. a CloudStack SSH keypair, if `cloudstack_ssh_keypair_name` is set and the
matching `.pem` is found
2. the password CloudStack generates, for a password-enabled template
3. the `password` you configured on the driver

`username` and `port` are only sent to the transport when you set them on the
driver. Leave them unset and your `transport:` configuration applies, which is
usually what you want:

```yaml
driver:
name: cloudstack
# no username here

transport:
username: ubuntu # honoured, because the driver does not override it
```

## Windows instances

Set the transport to WinRM and the driver follows it. Port forwarding and
firewall rules use the transport's port (5985, or 5986 for SSL) instead of SSH's
22, and the password CloudStack generates for a password-enabled Windows
template is handed to WinRM automatically:

```yaml
driver:
name: cloudstack
cloudstack_api_key: <%= ENV['CLOUDSTACK_API_KEY'] %>
cloudstack_secret_key: <%= ENV['CLOUDSTACK_SECRET_KEY'] %>
cloudstack_api_url: https://cloudstack.example.com/client/api
associate_public_ip: true
cloudstack_create_firewall_rule: true

transport:
name: winrm

platforms:
- name: windows-2022
driver:
cloudstack_template_id: <windows template id>
cloudstack_serviceoffering_id: <offering id>
cloudstack_zone_id: <zone id>
```

The template must have password management enabled so CloudStack can set and
report the administrator password, and WinRM must be listening in the image.

## Checking instance state

`kitchen list` asks the driver whether each instance is still alive, and this
driver answers from CloudStack rather than guessing, so an instance destroyed
out from under Test Kitchen is reported accurately.

## Examples

### User data
Expand Down Expand Up @@ -278,8 +334,16 @@ driver:
## Troubleshooting

**`NameError: uninitialized constant Kitchen::Driver::SSHBase`.** You are running
Test Kitchen 4.0 or newer. See the compatibility warning at the top: pin
`test-kitchen` below 4.0.
kitchen-cloudstack 0.24.0 or older, which was built on a class Test Kitchen
removed in 4.0. Upgrade to 1.0.0 or newer.

**A CloudStack job times out.** Deploys on a busy or large template can exceed
the ten minute default. Raise `cloudstack_job_timeout`.

**WinRM never becomes ready.** Check that the template has password management
enabled, that WinRM is listening in the image, and — if you are forwarding a
public IP — that `cloudstack_create_firewall_rule` is set so the WinRM port is
actually open.

**Login fails immediately after the VM boots.** CloudStack's
`cloud-set-guest-password` and `cloud-set-guest-sshkey` scripts may not have run
Expand All @@ -297,7 +361,7 @@ provisioner:
name: chef_infra
```

The same Test Kitchen version pin applies either way.
Everything else works identically.

## Contributing

Expand Down
2 changes: 1 addition & 1 deletion kitchen-cloudstack.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
spec.files = `git ls-files`.split($/)
spec.require_paths = ["lib"]

spec.add_dependency "test-kitchen", ">= 1.0.0", "< 5"
spec.add_dependency "test-kitchen", ">= 3.0", "< 5"
spec.add_dependency "fog-cloudstack", "~> 0.1.0"

spec.add_development_dependency "rake"
Expand Down
Loading
Loading