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
39 changes: 39 additions & 0 deletions lib/kitchen/driver/openstack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
require "kitchen"
require "fog/openstack"
require "yaml"
require "time" unless defined?(Time.now.iso8601)
require_relative "openstack_version"
require_relative "openstack/clouds"
require_relative "openstack/config"
Expand All @@ -41,6 +42,11 @@ module Driver
# kitchen.yml, `OS_*` environment variables, or a standard
# `clouds.yaml` -- see {Clouds} for the precedence rules.
class Openstack < Kitchen::Driver::Base
# Nova server states that mean the instance is up and reachable.
#
# @return [Array<String>]
LIVE_STATES = %w{ACTIVE}.freeze

# Settings Fog requires as Strings. Fog re-coerces anything that looks
# numeric back to an Integer, so these are stringified on the way in.
#
Expand Down Expand Up @@ -191,8 +197,41 @@ def destroy(state)
state.delete(:hostname)
end

# Reports what Nova currently thinks of the server.
#
# @param state [Hash] instance state naming the server
# @return [Hash] a Test Kitchen status hash, or the base implementation's
# answer when there is no server or Nova does not know it
def status(state)
return super unless state[:server_id]

server = lookup_server(state[:server_id])
return super unless server

{
live: LIVE_STATES.include?(server.state),
state: server.state,
source: "driver",
resource_id: state[:server_id],
message: "OpenStack reports the server as #{server.state}",
checked_at: Time.now.utc.iso8601,
}
end

private

# Looks a server up without turning an unreachable cloud into a failure.
#
# @param server_id [String] the Nova server ID
# @return [Fog::OpenStack::Compute::Server, nil] the server, or nil when
# Nova does not know it or cannot be reached
def lookup_server(server_id)
disable_ssl_validation if config[:disable_ssl_validation]
compute.servers.get(server_id)
rescue ::StandardError
nil
end

# Releases a floating IP back to its pool.
#
# A floating IP that Neutron no longer knows about is not an error worth
Expand Down
56 changes: 56 additions & 0 deletions spec/kitchen/driver/openstack_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,62 @@
end
end

describe "#status" do
let(:servers) { double("Fog servers collection", get: server) }
let(:compute) { fog_compute(servers: servers) }

before { allow(driver).to receive(:compute).and_return(compute) }

it "reports an unknown status when state names no server" do
expect(driver.status({})).to include(live: nil, state: "unknown")
end

it "reports an unknown status when Nova does not know the server" do
allow(servers).to receive(:get).with("gone").and_return(nil)

expect(driver.status(server_id: "gone")).to include(state: "unknown")
end

context "with an ACTIVE server" do
let(:server) { fog_server(state: "ACTIVE") }

it "reports it as live" do
expect(driver.status(server_id: "test123")).to include(
live: true, state: "ACTIVE", source: "driver", resource_id: "test123"
)
end

it "stamps when the check happened" do
expect(driver.status(server_id: "test123")[:checked_at])
.to match(/\A\d{4}-\d{2}-\d{2}T/)
end
end

context "with a server Nova has not finished building" do
let(:server) { fog_server(state: "BUILD") }

it "reports it as not live" do
expect(driver.status(server_id: "test123"))
.to include(live: false, state: "BUILD")
end
end

context "with a server in ERROR" do
let(:server) { fog_server(state: "ERROR") }

it "reports it as not live but names the state" do
expect(driver.status(server_id: "test123"))
.to include(live: false, state: "ERROR")
end
end

it "reports an unknown status when the cloud cannot be reached" do
allow(servers).to receive(:get).and_raise(Excon::Errors::SocketError.new(StandardError.new("boom")))

expect(driver.status(server_id: "test123")).to include(state: "unknown")
end
end

describe "#destroy" do
let(:state) { { server_id: "test123", hostname: "1.2.3.4" } }
let(:servers) { double("Fog servers collection", get: server) }
Expand Down
Loading