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
49 changes: 47 additions & 2 deletions lib/kitchen/driver/openstack.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,27 @@ module Kitchen
module Driver
# This takes from the Base Class and creates the OpenStack driver.
class Openstack < Kitchen::Driver::Base
FOG_STRING_SETTINGS = %i{
openstack_username
openstack_api_key
openstack_auth_url
openstack_project_name
openstack_project_id
openstack_user_domain
openstack_user_domain_id
openstack_project_domain
openstack_project_domain_id
openstack_domain_id
openstack_domain_name
openstack_region
openstack_endpoint_type
openstack_identity_api_version
openstack_application_credential_id
openstack_application_credential_secret
openstack_tenant
openstack_tenant_id
}.freeze

include Clouds
include Config
include Helpers
Expand Down Expand Up @@ -149,8 +170,8 @@ def openstack_server
server_def = {
connection_options: {},
}
required_server_settings.each { |s| server_def[s] = config[s] }
optional_server_settings.each { |s| server_def[s] = config[s] if config[s] }
required_server_settings.each { |s| server_def[s] = normalize_fog_setting(s, config[s]) }
optional_server_settings.each { |s| server_def[s] = normalize_fog_setting(s, config[s]) if config[s] }
connection_options.each { |s| server_def[:connection_options][s] = config[s] if config[s] }
server_def
end
Expand Down Expand Up @@ -184,6 +205,30 @@ def volume
def get_bdm(config)
volume.get_bdm(config, openstack_server)
end

def normalize_fog_setting(setting, value)
return value if value.nil?
return normalize_identity_api_version(value) if setting == :openstack_identity_api_version
return value unless FOG_STRING_SETTINGS.include?(setting)

value.to_s
end

# Fog::Service#coerce_options re-coerces any value where
# `value.to_s.to_i.to_s == value.to_s` back to an Integer, which
# then breaks Fog::OpenStack::Auth::Token.build (it calls `=~`
# on the value). Prefixing with "v" keeps Fog from coercing and
# still satisfies Token.build's `/(v)*2(\.0)*/i` regex check.
def normalize_identity_api_version(value)
str = value.to_s.strip
return str if str.empty?
return str if str.start_with?("v", "V")

case str
when "2", "2.0" then "v2.0"
else "v#{str}"
end
end
end
end
end
18 changes: 15 additions & 3 deletions lib/kitchen/driver/openstack/clouds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,10 @@ module Clouds
"identity_api_version" => :openstack_identity_api_version,
}.freeze

# Fog expects these config values to be strings. YAML may parse
# unquoted scalars as integers/booleans, so normalize on ingest.
STRING_CONFIG_KEYS = (CLOUDS_YAML_AUTH_MAP.values + CLOUDS_YAML_TOP_MAP.values).freeze

# Mapping of OS_* environment variables to Fog OpenStack config keys
ENV_VAR_MAP = {
"OS_AUTH_URL" => :openstack_auth_url,
Expand Down Expand Up @@ -100,7 +104,7 @@ def load_env_vars
result = {}
ENV_VAR_MAP.each do |env_var, fog_key|
value = ENV[env_var]
result[fog_key] = value if value && !value.empty?
result[fog_key] = normalize_config_value(fog_key, value) if value && !value.empty?
end
result
end
Expand Down Expand Up @@ -175,12 +179,14 @@ def translate_cloud_config(cloud)
# Map auth section
auth = cloud["auth"] || {}
CLOUDS_YAML_AUTH_MAP.each do |yaml_key, fog_key|
result[fog_key] = auth[yaml_key] if auth[yaml_key]
value = auth[yaml_key]
result[fog_key] = normalize_config_value(fog_key, value) if value
end

# Map top-level keys
CLOUDS_YAML_TOP_MAP.each do |yaml_key, fog_key|
result[fog_key] = cloud[yaml_key] if cloud[yaml_key]
value = cloud[yaml_key]
result[fog_key] = normalize_config_value(fog_key, value) if value
end

# SSL settings
Expand All @@ -189,6 +195,12 @@ def translate_cloud_config(cloud)

result
end

def normalize_config_value(fog_key, value)
return value unless STRING_CONFIG_KEYS.include?(fog_key)

value.to_s
end
end
end
end
Expand Down
15 changes: 15 additions & 0 deletions spec/kitchen/driver/openstack/clouds_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,21 @@
result = driver.send(:translate_cloud_config, {})
expect(result).to eq({})
end

it "coerces non-string scalar values for fog string config keys" do
cloud = {
"auth" => {
"project_id" => 12_345,
"domain_id" => 9,
},
"identity_api_version" => 3,
}

result = driver.send(:translate_cloud_config, cloud)
expect(result[:openstack_project_id]).to eq("12345")
expect(result[:openstack_domain_id]).to eq("9")
expect(result[:openstack_identity_api_version]).to eq("3")
end
end

describe "#deep_merge" do
Expand Down
38 changes: 38 additions & 0 deletions spec/kitchen/driver/openstack_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,44 @@
expected = config.merge(config)
expect(driver.send(:openstack_server)).to eq(expected)
end

context "when string-like fog settings are numeric" do
let(:config) do
{
openstack_username: "a",
openstack_domain_id: 12_345,
openstack_api_key: "b",
openstack_auth_url: "http://",
openstack_project_id: 99,
openstack_identity_api_version: 3,
}
end

it "coerces them to strings before passing to fog" do
server = driver.send(:openstack_server)

expect(server[:openstack_domain_id]).to eq("12345")
expect(server[:openstack_project_id]).to eq("99")
# identity_api_version is prefixed with "v" so that
# Fog::Service#coerce_options does not turn it back into an
# Integer (which would later break Token.build's `=~`).
expect(server[:openstack_identity_api_version]).to eq("v3")
end

it "prefixes identity_api_version 2 with a v for fog v2 detection" do
config[:openstack_identity_api_version] = 2
server = driver.send(:openstack_server)

expect(server[:openstack_identity_api_version]).to eq("v2.0")
end

it "passes through identity_api_version values already prefixed" do
config[:openstack_identity_api_version] = "v3"
server = driver.send(:openstack_server)

expect(server[:openstack_identity_api_version]).to eq("v3")
end
end
end

describe "#required_server_settings" do
Expand Down
Loading