From: Greg Blomquist<gblomqui(a)redhat.com>
This commit adds the ability to manage config server data as an object
associated to provider accounts. Specifically, provider accounts can have
0-or-1 config servers.
Now, a user can add, edit, delete, and test a config server from the provider account
view.
Signed-off-by: Greg Blomquist<gblomqui(a)redhat.com>
---
src/app/controllers/config_servers_controller.rb | 74 ++++++++++
src/app/models/config_server.rb | 146 ++++++++++++++++++++
src/app/models/provider_account.rb | 2 +
.../views/config_servers/_config_server_form.haml | 22 +++
src/app/views/config_servers/_section_header.haml | 3 +
src/app/views/config_servers/edit.haml | 21 +++
src/app/views/config_servers/new.haml | 16 ++
src/app/views/provider_accounts/_properties.haml | 22 +++
src/config/locales/en.yml | 14 ++
src/config/routes.rb | 1 +
.../migrate/20110606141425_create_config_server.rb | 18 +++
src/features/config_server.feature | 109 +++++++++++++++
.../step_definitions/config_server_steps.rb | 37 +++++
src/features/support/env.rb | 3 +
src/features/support/paths.rb | 8 +-
.../controllers/config_servers_controller_spec.rb | 104 ++++++++++++++
src/spec/factories/config_server.rb | 36 +++++
src/spec/models/config_server_spec.rb | 42 ++++++
src/spec/models/provider_account_spec.rb | 7 +
19 files changed, 684 insertions(+), 1 deletions(-)
create mode 100644 src/app/controllers/config_servers_controller.rb
create mode 100644 src/app/models/config_server.rb
create mode 100644 src/app/views/config_servers/_config_server_form.haml
create mode 100644 src/app/views/config_servers/_section_header.haml
create mode 100644 src/app/views/config_servers/edit.haml
create mode 100644 src/app/views/config_servers/new.haml
create mode 100644 src/db/migrate/20110606141425_create_config_server.rb
create mode 100644 src/features/config_server.feature
create mode 100644 src/features/step_definitions/config_server_steps.rb
create mode 100644 src/spec/controllers/config_servers_controller_spec.rb
create mode 100644 src/spec/factories/config_server.rb
create mode 100644 src/spec/models/config_server_spec.rb
diff --git a/src/app/controllers/config_servers_controller.rb
b/src/app/controllers/config_servers_controller.rb
new file mode 100644
index 0000000..dd53be1
--- /dev/null
+++ b/src/app/controllers/config_servers_controller.rb
@@ -0,0 +1,74 @@
+require 'rest_client'
+
+class ConfigServersController< ApplicationController
+ before_filter :require_user
+ layout 'application'
+
+ def top_section
+ :administer
+ end
+
+ def edit
+ @config_server = ConfigServer.find(params[:id])
+ @provider_account = @config_server.provider_account
+ require_privilege(Privilege::MODIFY, @provider_account)
+ end
+
+ def new
+ @provider_account = ProviderAccount.find(params[:provider_account_id])
+ require_privilege(Privilege::MODIFY, @provider_account)
+ @config_server = ConfigServer.new()
+ end
+
+ def test
+ config_server = ConfigServer.find(params[:id])
+ if not config_server.connection_valid?
+ flash[:error] = config_server.connection_error_msg
+ else
+ flash[:notice] = "Test successful"
+ end
+ redirect_to provider_account_path(config_server.provider_account.id)
+ end
+
+ def create
+ @provider_account = ProviderAccount.find(params[:provider_account_id])
+ # for now the privileges required to create, modify, or delete a config
+ # server are tied to modifying the particular associated provider account
+ require_privilege(Privilege::MODIFY, @provider_account)
+
+ @config_server = ConfigServer.new(params[:config_server])
+ @config_server.provider_account = @provider_account
+ if @config_server.invalid?
+ flash[:error] = "The config server information is invalid."
+ render :action => 'new' and return
+ end
+ @config_server.save!
+ flash[:notice] = "Config server added."
+ redirect_to provider_account_path(@provider_account)
+ end
+
+ def update
+ @config_server = ConfigServer.find(params[:id])
+ @provider_account = @config_server.provider_account
+ require_privilege(Privilege::MODIFY, @provider_account)
+
+ if @config_server.update_attributes(params[:config_server])
+ flash[:notice] = "Config server updated."
+ redirect_to provider_account_path(@provider_account)
+ else
+ flash[:error] = "Config server was not updated"
+ render :action => :edit
+ end
+ end
+
+ def destroy
+ @config_server = ConfigServer.find(params[:id])
+ require_privilege(Privilege::MODIFY, @config_server.provider_account)
+ if ConfigServer.destroy(params[:id])
+ flash[:notice] = "Config server was deleted."
+ else
+ flash[:error] = "Config server was not deleted"
+ end
+ redirect_to provider_account_path((a)config_server.provider_account)
+ end
+end
diff --git a/src/app/models/config_server.rb b/src/app/models/config_server.rb
new file mode 100644
index 0000000..9ccba5f
--- /dev/null
+++ b/src/app/models/config_server.rb
@@ -0,0 +1,146 @@
+# == Schema Information
+# Schema version: 20110517095823
+#
+# Table name: config_servers
+#
+# id :integer not null, primary key
+# host :string(255) not null
+# port :string(255) not null
+# username :string(255) null
+# password :string(255) null
+# certificate :string(255) null
+# provider_id :integer not null, fk_provider
+#
+
+## Copyright (C) 2011 Red Hat, Inc.
+## Written by Greg Blomquist<gblomqui(a)redhat.com>
+##
+## This program is free software; you can redistribute it and/or modify
+## it under the terms of the GNU General Public License as published by
+## the Free Software Foundation; version 2 of the License.
+##
+## This program is distributed in the hope that it will be useful,
+## but WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+## MA 02110-1301, USA. A copy of the GNU General Public License is
+## also available at
http://www.gnu.org/copyleft/gpl.html.
+#
+
+class ConfigServer< ActiveRecord::Base
+
+ class ConnectionStatus
+ attr_reader :state, :message
+ def initialize
+ @state = :UNTESTED
+ end
+
+ def untested?
+ return :UNTESTED == @state
+ end
+
+ def fail!(message)
+ @state = :FAILURE
+ @message = message
+ end
+ def fail?
+ return :FAILURE == @state
+ end
+
+ def success!(message=nil)
+ @state = :SUCCESS
+ @message = message
+ end
+ def success?
+ return :SUCCESS == @state
+ end
+ end
+ @@status_fields = [:host, :port, :username, :password, :certificate]
+
+ belongs_to :provider_account
+
+ validates_presence_of :host
+ validates_presence_of :port
+ validate :validate_connection
+
+ # Utility method to piece together the connection information for this config
+ # server.
+ def base_url
+ scheme = (certificate.blank?) ? "http" : "https"
+ domain = host
+ if not port.blank?
+ domain += ":#{port}"
+ end
+ "#{scheme}://#{domain}"
+ end
+
+ # Reports the error message (if any) produced by testing the connection to
+ # this config server.
+ def connection_error_msg
+ if not connection_valid?
+ return status.message
+ end
+ end
+
+ # Determines if the connection represented by #host, #port, #username,
+ # #password, and #certificate is a valid connection. If the connection is
+ # invalid, calling #connection_error_msg will return the error that was
+ # generated when testing the connection.
+ def connection_valid?
+ if status.untested? or (changed? and (changes.keys& @(a)status_fields).empty?)
+ begin
+ test_connection
+ status.success!
+ rescue => e
+ error_str = map_connection_exception_to_error(e)
+ status.fail!(error_str)
+ end
+ end
+ status.success?
+ end
+
+ private
+ def status
+ # smells like a factory in here
+ @status ||= ConnectionStatus.new
+ end
+
+ def validate_connection
+ # for validation hook
+ if not connection_valid?
+ errors.add_to_base(status.message)
+ end
+ end
+
+ # Test the connection to this config server. Return nil on success, or throw
+ # an exception on errors. See
+ #
http://rubydoc.info/gems/rest-client/1.6.3/RestClient#STATUSES-constant
+ # for more information on the types of exceptions.
+ def test_connection
+ args = {:method => :get}
+ if not username.blank?
+ args[:user] = username
+ args[:password] = password
+ end
+ if not certificate.blank?
+ args[:ssl_client_cert] = OpenSSL::X509::Certificate.new(certificate)
+ end
+ args[:url] = "#{base_url}/version"
+ args[:raw_response] = true
+ RestClient::Request.execute(args)
+ end
+
+ def map_connection_exception_to_error(ex)
+ if ex.class == OpenSSL::X509::CertificateError
+ error_string =
I18n.translate("config_servers.errors.connection.certificate", :url =>
base_url, :msg => ex.message)
+ elsif ex.kind_of?(RestClient::ExceptionWithResponse) or
ex.kind_of?(RestClient::Exception) or ex.class == Errno::ETIMEDOUT
+ error_string =
I18n.translate("config_servers.errors.connection.generic_with_message", :url
=> base_url, :msg => ex.message)
+ elsif not ex.nil?
+ error_string =
I18n.translate("config_servers.errors.connection.generic", :url =>
base_url)
+ end
+ end
+end
diff --git a/src/app/models/provider_account.rb b/src/app/models/provider_account.rb
index 812d5a1..3cd3691 100644
--- a/src/app/models/provider_account.rb
+++ b/src/app/models/provider_account.rb
@@ -51,6 +51,8 @@ class ProviderAccount< ActiveRecord::Base
# validation of credentials is done in provider_account validation, :validate =>
false prevents nested_attributes from validation
has_many :credentials, :dependent => :destroy, :validate => false
accepts_nested_attributes_for :credentials
+ # eventually, this might be "has_many", but first pass is one-to-one
+ has_one :config_server, :dependent => :destroy
# Helpers
attr_accessor :x509_cert_priv_file, :x509_cert_pub_file
diff --git a/src/app/views/config_servers/_config_server_form.haml
b/src/app/views/config_servers/_config_server_form.haml
new file mode 100644
index 0000000..5e60896
--- /dev/null
+++ b/src/app/views/config_servers/_config_server_form.haml
@@ -0,0 +1,22 @@
+%fieldset.clearfix
+ - if not @config_server.id.nil?
+ = hidden_field_tag "config_server[id]", @config_server.id
+ = hidden_field_tag "provider_account_id", @provider_account.id
+ = label_tag "config_server[host]", "Host: "
+ = text_field_tag "config_server[host]", @config_server.host
+%fieldset.clearfix
+ = label_tag "config_server[port]", "Port: "
+ = text_field_tag "config_server[port]", @config_server.port
+%fieldset.clearfix
+ = label_tag "config_server[username]", "Username: "
+ = text_field_tag "config_server[username]", @config_server.username
+%fieldset.clearfix
+ = label_tag "config_server[password]", "Password: "
+ = password_field_tag "config_server[password]", @config_server.password
+%fieldset.clearfix
+ = label_tag "config_server[certificate]", "Certificate: "
+ = text_area_tag "config_server[certificate]", @config_server.certificate,
:rows => 20, :cols => 64
+ = t('config_servers.certificate_help')
+%fieldset.clearfix
+ .grid_13.alpha.omega
+ = submit_tag t(:save), :class => "ra nomargin dialogbutton"
diff --git a/src/app/views/config_servers/_section_header.haml
b/src/app/views/config_servers/_section_header.haml
new file mode 100644
index 0000000..d8e75fe
--- /dev/null
+++ b/src/app/views/config_servers/_section_header.haml
@@ -0,0 +1,3 @@
+%header.page-header
+ %h1{:class => controller.controller_name} Config Server
+ .corner
diff --git a/src/app/views/config_servers/edit.haml
b/src/app/views/config_servers/edit.haml
new file mode 100644
index 0000000..42e40f8
--- /dev/null
+++ b/src/app/views/config_servers/edit.haml
@@ -0,0 +1,21 @@
+= render :partial => 'layouts/admin_header'
+%header.page-header
+ %h1{:class => controller.controller_name}= "#{(a)provider_account.name} Config
Server"
+ #obj_actions.button-container
+ %div.button-group
+ = link_to 'Cancel Editing', provider_account_path(@provider_account),
:class => 'button pill danger', :id =>
'cancel_config_server_edit'
+ .corner
+
+%section.content-section.config_server
+ %header
+ %h2 Edit Config Server
+
+ .content
+ = error_messages_for :config_server
+ %h2
+ = t('config_servers.edit.edit_config_server')
+ - form_tag(config_server_path, { :method => :put }) do
+ = render :partial => 'config_server_form', :locals => {:from =>
"edit"}
+ -#%fieldset.clearfix
+ .grid_13.alpha.omega
+ = submit_tag t(:save), :class => "ra nomargin dialogbutton"
diff --git a/src/app/views/config_servers/new.haml
b/src/app/views/config_servers/new.haml
new file mode 100644
index 0000000..c21aee4
--- /dev/null
+++ b/src/app/views/config_servers/new.haml
@@ -0,0 +1,16 @@
+= render :partial => 'layouts/admin_header'
+= render :partial => 'section_header'
+
+%section.content-section.config_server
+ %header
+ %h2 New Config Server
+
+ .content
+ = error_messages_for :config_server
+ %h2
+ = t('config_servers.new.new_config_server')
+ - form_for(@config_server, :url => config_servers_path) do |f|
+ = render :partial => 'config_server_form', :locals => {:from =>
"new"}
+ -#%fieldset
+ .grid_13.alpha.omega
+ = f.submit t(:add), :class => "ra nomargin dialogbutton"
diff --git a/src/app/views/provider_accounts/_properties.haml
b/src/app/views/provider_accounts/_properties.haml
index 559bdf1..6990a17 100644
--- a/src/app/views/provider_accounts/_properties.haml
+++ b/src/app/views/provider_accounts/_properties.haml
@@ -17,3 +17,25 @@
= label_tag "Account number:"
%td
= @account_id
+ %tr
+ %td
+ %label Config Server:
+ %td
+ - missing_config_server = @account.config_server.nil?
+ %span#config_server
+ = missing_config_server ? "None" : @account.config_server.base_url
+ %span#config_server_control
+ - if missing_config_server
+ [
+ = link_to 'Add', new_config_server_url +
"?provider_account_id=#{(a)account.id}"
+ ]
+ - else
+ [
+ = link_to 'Edit', edit_config_server_path((a)account.config_server)
+ ]
+ [
+ = link_to 'Test', test_config_servers_path +
"?id=#{(a)account.config_server.id}"
+ ]
+ [
+ = link_to 'Delete', config_server_path((a)account.config_server),
:method => 'delete', :confirm => "Are you sure you want to delete
this config server?"
+ ]
diff --git a/src/config/locales/en.yml b/src/config/locales/en.yml
index 1564983..9af4ab8 100644
--- a/src/config/locales/en.yml
+++ b/src/config/locales/en.yml
@@ -237,6 +237,8 @@ en:
account_not_added:
one: "Account %{list} could not be added"
other: "Accounts %{list} could not be added"
+ config_server_saved: "Config Server data was successfully saved"
+ config_server_not_saved: "An error occurred while saving the Config Server
data."
new:
new_provider_account: New Account
required_field: Required field.
@@ -254,4 +256,16 @@ en:
account_number: AWS Account ID
account_private_cert: EC2 x509 private key
account_public_cert: EC2 x509 public key
+ config_servers:
+ certificate_help: Provide a certificate to enable https support
+ new:
+ new_config_server: New Config Server
+ edit:
+ edit_config_server: Edit Config Server
+ errors:
+ connection:
+ generic: "Could not validate config server connection (url: %{url}). Check
the connection parameters and try again."
+ generic_with_message: "Could not validate config server connection (url:
%{url}). %{msg}"
+ certificate: "Could not validate config server connection (url: %{url}).
Certificate error: %{msg}"
+ unhandled_response: "Could not validate config server connection (url:
%{url}). HTTP Status Code: %{code}"
uptime: Uptime
diff --git a/src/config/routes.rb b/src/config/routes.rb
index 7f83338..18ae28d 100644
--- a/src/config/routes.rb
+++ b/src/config/routes.rb
@@ -46,6 +46,7 @@ ActionController::Routing::Routes.draw do |map|
map.resources :provider_types, :only => :index
map.resources :users, :collection => { :multi_destroy => :delete }
map.resources :provider_accounts, :collection => { :multi_destroy => :delete,
:set_selected_provider => :get}
+ map.resources :config_servers, :collection => { :test => :get }
map.resources :roles, :collection => { :multi_destroy => :delete }
map.resources :settings, :collection => { :self_service => :get,
:general_settings => :get }
map.resources :pool_families, :collection => { :multi_destroy => :delete,
:add_provider_account => :post, :multi_destroy_provider_accounts => :delete }
diff --git a/src/db/migrate/20110606141425_create_config_server.rb
b/src/db/migrate/20110606141425_create_config_server.rb
new file mode 100644
index 0000000..e10ce78
--- /dev/null
+++ b/src/db/migrate/20110606141425_create_config_server.rb
@@ -0,0 +1,18 @@
+class CreateConfigServer< ActiveRecord::Migration
+ def self.up
+ create_table :config_servers do |t|
+ t.string :host, :null => false
+ t.string :port, :null => false
+ t.string :username, :null => true
+ t.string :password, :null => true
+ t.string :certificate, :null => true, :limit => 2048
+ t.integer :provider_account_id, :null => false
+
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :config_servers
+ end
+end
diff --git a/src/features/config_server.feature b/src/features/config_server.feature
new file mode 100644
index 0000000..9beecf8
--- /dev/null
+++ b/src/features/config_server.feature
@@ -0,0 +1,109 @@
+Feature: Config Servers
+ In order to administer configuration management on systems
+ As a user
+ I want to manage config servers as part of provider accounts
+
+ Background:
+ Given I am an authorised user
+ And I am logged in
+
+ # This scenario relies on a stubbed version of ConfigServer
+ # that never fails on the connection test
+ Scenario: I am able to add a config server to a provider account
+ Given I am on the homepage
+ And there is mock provider account "mock_account"
+ And I want to add a new config server
+ When I go to mock_account's provider account page
+ Then I should see "None" within "#config_server"
+ And I should see "[ Add ]" within "#config_server_control"
+ When I follow "Add"
+ Then I should be on the new config server page
+ When I fill in "config_server[host]" with "valid_host"
+ When I fill in "config_server[port]" with "valid_port"
+ And I press "Save"
+ Then I should be on mock_account's provider account page
+ And I should see "Config server added"
+ And I should see "[ Edit ]" within "#config_server_control"
+
+ # This is essentially the same scenario as the first, but creates
+ # a different stubbed ConfigServer, so it fails
+ Scenario: I cannot add a config server with invalid host or port information
+ Given I am on the homepage
+ And there is mock provider account "mock_account"
+ And I am not sure about the config server host
+ When I go to mock_account's provider account page
+ Then I should see "None" within "#config_server"
+ And I should see "[ Add ]" within "#config_server_control"
+ When I follow "Add"
+ Then I should be on the new config server page
+ When I fill in "config_server[host]" with "invalid"
+ When I fill in "config_server[port]" with "invalid"
+ And I press "Save"
+ Then I should see "The config server information is invalid"
+ And I should see "Could not validate config server connection"
+
+ # This is essentially the same scenario as the first, but creates
+ # a different stubbed ConfigServer, so it fails
+ Scenario: I cannot add a config server with invalid credentials
+ Given I am on the homepage
+ And there is mock provider account "mock_account"
+ And I am not sure about the config server credentials
+ When I go to mock_account's provider account page
+ Then I should see "None" within "#config_server"
+ And I should see "[ Add ]" within "#config_server_control"
+ When I follow "Add"
+ Then I should be on the new config server page
+ When I fill in "config_server[host]" with "valid"
+ When I fill in "config_server[port]" with "valid"
+ When I fill in "config_server[username]" with "invalid"
+ When I fill in "config_server[password]" with "invalid"
+ And I press "Save"
+ Then I should see "The config server information is invalid"
+ And I should see "Could not validate config server connection"
+
+ Scenario: I should be able to edit a config server
+ Given I am on the homepage
+ And there is a mock config server "https://mock:443" for account
"mock_account"
+ When I go to mock_account's provider account page
+ Then I should see "[ Edit ]" within "#config_server_control"
+ When I follow "Edit" within "#config_server_control"
+ Then I should be on the edit config server page for account
"mock_account"
+ And I press "Save"
+ Then I should be on mock_account's provider account page
+ And I should see "Config server updated"
+
+ Scenario: I should be able to delete an existing config server
+ Given I am on the homepage
+ And there is a mock config server "https://mock:443" for account
"mock_account"
+ When I go to mock_account's provider account page
+ Then I should see "[ Delete ]" within "#config_server_control"
+ When I follow "Delete" within "#config_server_control"
+ Then I should see "Config server was deleted"
+ And I should be on mock_account's provider account page
+
+ Scenario: I should be able to test a correctly configured and available config server
+ Given I am on the homepage
+ And there is a mock config server "https://mock:443" for account
"mock_account"
+ When I go to mock_account's provider account page
+ Then I should see "[ Test ]" within "#config_server_control"
+ When I follow "Test" within "#config_server_control"
+ Then I should see "Test successful"
+ And I should be on mock_account's provider account page
+
+ Scenario: I should see an error when I test a config server with invalid credentials
+ Given I am on the homepage
+ And there is a mock config server "https://bad_credentials:443" for
account "mock_account"
+ When I go to mock_account's provider account page
+ Then I should see "[ Test ]" within "#config_server_control"
+ When I follow "Test" within "#config_server_control"
+ Then I should see "Could not validate config server connection"
+ And I should see "Unauthorized"
+
+ Scenario: I should see an error when I test a config server with an invalid host
+ Given I am on the homepage
+ And there is a mock config server "https://bad_host:443" for account
"mock_account"
+ When I go to mock_account's provider account page
+ Then I should see "[ Test ]" within "#config_server_control"
+ When I follow "Test" within "#config_server_control"
+ Then I should see "Could not validate config server connection"
+ And I should see "Connection timed out"
diff --git a/src/features/step_definitions/config_server_steps.rb
b/src/features/step_definitions/config_server_steps.rb
new file mode 100644
index 0000000..438ad00
--- /dev/null
+++ b/src/features/step_definitions/config_server_steps.rb
@@ -0,0 +1,37 @@
+Given /^I want to add a new config server$/ do
+ c = Factory.build(:mock_config_server)
+ ConfigServer.stub!(:new).and_return(c)
+end
+
+Given /^I am not sure about the config server (host|port)$/ do |host_or_port|
+ c = Factory.build(:invalid_host_or_port_config_server)
+ ConfigServer.stub!(:new).and_return(c)
+end
+
+Given /^I am not sure about the config server credentials$/ do
+ c = Factory.build(:invalid_credentials_config_server)
+ ConfigServer.stub!(:new).and_return(c)
+end
+
+Given /^there is a mock config server "(http|https):\/\/(.*):(.*)" for account
"(.*)"$/ do |scheme,host,port,acc|
+ provider = Factory :mock_provider, :name => "mock_provider"
+ mock_account = Factory :mock_provider_account, :label => acc, :provider =>
provider
+ params = {:host => host, :port => port, :provider_account =>
mock_account}
+ if "https" == scheme
+ params[:certificate] = "cert"
+ end
+ @config_server = Factory :mock_config_server, params
+ # Don't particularly like this next bit, but the problem is the
"create"
+ # default_strategy used when instantiating the Factory leaves the
+ # @config_server in a "SUCCESS" status. This bit effectively
"resets" the
+ # status to "UNTESTED" so the :test_connection method(stub) has to get
called
+ @config_server.stub!(:status).and_return(ConfigServer::ConnectionStatus.new())
+ case host
+ when "bad_credentials"
+ @config_server.stub!(:test_connection).and_raise(RestClient::Unauthorized)
+ when "bad_host"
+ @config_server.stub!(:test_connection).and_raise(Errno::ETIMEDOUT)
+ end
+ # ensure that the :mock_config_server (with stubbed methods) is returned
+ ConfigServer.stub!(:find).and_return(@config_server)
+end
diff --git a/src/features/support/env.rb b/src/features/support/env.rb
index 33f9ed2..7357eda 100644
--- a/src/features/support/env.rb
+++ b/src/features/support/env.rb
@@ -19,6 +19,9 @@ require File.expand_path(File.dirname(__FILE__) +
'../../../spec/vcr_setup.rb')
require 'webrat'
require 'webrat/core/matchers'
+# Pull in the ability to stub out methods
+require 'spec/stubs/cucumber'
+
Webrat.configure do |config|
config.mode = :rails
config.open_error_files = false # Set to true if you want error pages to pop up in
the browser
diff --git a/src/features/support/paths.rb b/src/features/support/paths.rb
index 12a72d5..3a86c52 100644
--- a/src/features/support/paths.rb
+++ b/src/features/support/paths.rb
@@ -98,6 +98,13 @@ module NavigationHelpers
when /^(.*)'s provider account page$/
provider_account_path(ProviderAccount.find_by_label($1))
+ when /the new config server page/
+ url_for new_config_server_path
+
+ when /^the edit config server page/
+ @config_server.stub!(:connection_valid?).and_return(true)
+ edit_config_server_path(@config_server)
+
when /the operational status of deployment page/
deployment_path(@deployment, :details_tab => 'operation')
@@ -123,7 +130,6 @@ module NavigationHelpers
when /^the (.*)'s edit user page$/
edit_user_path(User.find_by_login($1))
-
# Add more mappings here.
# Here is an example that pulls values out of the Regexp:
#
diff --git a/src/spec/controllers/config_servers_controller_spec.rb
b/src/spec/controllers/config_servers_controller_spec.rb
new file mode 100644
index 0000000..9b2f1a0
--- /dev/null
+++ b/src/spec/controllers/config_servers_controller_spec.rb
@@ -0,0 +1,104 @@
+require 'spec_helper'
+
+describe ConfigServersController do
+ fixtures :all
+
+ before(:each) do
+ @admin_permission = Factory :admin_permission
+ @admin = @admin_permission.user
+ activate_authlogic
+ @session = UserSession.create(@admin)
+ end
+
+ context "editing config servers" do
+ before(:each) do
+ @config_server = Factory :mock_config_server
+ @provider_account = @config_server.provider_account
+ end
+
+ it "should provide UI to edit an existing Config Server" do
+ get :edit, :id => @config_server.id
+ response.should be_success
+ response.should render_template("edit")
+ end
+
+ it "should allow users with account modify permissions to update a Config
Server" do
+ post :update, :id => @config_server.id, :config_server => {:host =>
"host", :port => "port"}
+ response.should be_success
+ end
+ end
+
+ context "creating config servers" do
+ before(:each) do
+ @provider_account = Factory :mock_provider_account
+ end
+
+ it "should provide UI to create a new Config Server" do
+ get :new, :provider_account_id => @provider_account.id
+ response.should be_success
+ response.should render_template("new")
+ end
+
+ it "should allow users with account modify permissions to create a Config
Server" do
+ config_server = Factory :mock_config_server, :host => "host", :port
=> "port"
+ ConfigServer.stub!(:new).and_return(config_server)
+ post :create, :provider_account_id => @provider_account.id,
+ :config_server => {
+ :host => "host",
+ :port => "port"
+ }
+ response.should redirect_to(provider_account_path((a)provider_account.id))
+ response.flash[:error].should be_nil
+ end
+
+ it "should fail creating a config server when the username or password is
invalid" do
+ config_server = Factory :invalid_credentials_config_server, :host =>
"host", :port => "port", :username => "invalid",
:password => "invalid"
+ ConfigServer.stub!(:new).and_return(config_server)
+ post :create, :provider_account_id => @provider_account.id,
+ :config_server => {
+ :host => "host",
+ :port => "port",
+ :username => "invalid",
+ :password => "invalid"
+ }
+ response.should be_success
+ response.should render_template("new")
+ response.flash[:error].should == "The config server information is
invalid."
+ end
+
+ it "should fail creating a config server when the host and port are
invalid" do
+ config_server = Factory :invalid_host_or_port_config_server, :host =>
"invalid", :port => "invalid"
+ ConfigServer.stub!(:new).and_return(config_server)
+ post :create, :provider_account_id => @provider_account.id,
+ :config_server => {
+ :host => "invalid",
+ :port => "invalid"
+ }
+ response.should be_success
+ response.should render_template("new")
+ response.flash[:error].should == "The config server information is
invalid."
+ end
+
+ it "should require that port is provided" do
+ post :create, :provider_account_id => @provider_account.id,
+ :config_server => {
+ :host => "host",
+ :port => ""
+ }
+ response.should be_success
+ response.should render_template("new")
+ response.flash[:error].should == "The config server information is
invalid."
+ end
+
+ it "should require that host is provided" do
+ post :create, :provider_account_id => @provider_account.id,
+ :config_server => {
+ :host => "",
+ :port => "port"
+ }
+ response.should be_success
+ response.should render_template("new")
+ response.flash[:error].should == "The config server information is
invalid."
+ end
+ end
+end
diff --git a/src/spec/factories/config_server.rb b/src/spec/factories/config_server.rb
new file mode 100644
index 0000000..91e07f0
--- /dev/null
+++ b/src/spec/factories/config_server.rb
@@ -0,0 +1,36 @@
+Factory.define :config_server do |f|
+ f.sequence(:host) {|n| "config_server#{n}" }
+end
+
+Factory.define :base_config_server, :parent => :config_server do |f|
+ f.host "localhost"
+ f.port "80"
+ f.username "username"
+ f.password "password"
+end
+
+Factory.define :mock_config_server, :parent => :base_config_server do |f|
+ f.association :provider_account, :factory => :mock_provider_account
+ f.after_build do |cs|
+ cs.stub!(:test_connection).and_return(nil) if cs.respond_to? :stub!
+ end
+end
+
+Factory.define :invalid_credentials_config_server, :parent => :base_config_server,
:default_strategy => :build do |f|
+ f.port "443"
+ f.username "bad_username"
+ f.password "bad_password"
+ f.certificate "cert"
+ f.after_build do |cs|
+ cs.stub!(:test_connection).and_raise(RestClient::Unauthorized) if cs.respond_to?
:stub!
+ end
+end
+
+Factory.define :invalid_host_or_port_config_server, :parent => :base_config_server,
:default_strategy => :build do |f|
+ f.host "bad_host"
+ f.port "443"
+ f.certificate "cert"
+ f.after_build do |cs|
+ cs.stub!(:test_connection).and_raise(Errno::ETIMEDOUT) if cs.respond_to? :stub!
+ end
+end
diff --git a/src/spec/models/config_server_spec.rb
b/src/spec/models/config_server_spec.rb
new file mode 100644
index 0000000..350f036
--- /dev/null
+++ b/src/spec/models/config_server_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+
+describe ConfigServer do
+ describe "standard behavior" do
+ before(:each) do
+ @config_server = Factory.build :mock_config_server
+ end
+
+ it "should require a host" do
+ @config_server.should be_valid
+ @config_server.host = nil
+ @config_server.should_not be_valid
+ end
+
+ it "should require a port" do
+ @config_server.should be_valid
+ @config_server.port = nil
+ @config_server.should_not be_valid
+ end
+
+ it "should suggest https when a cert is present" do
+ @config_server.certificate = "abc"
+ @config_server.base_url.should =~ /https:\/\/.*/
+ end
+
+ it "should suggest http when a cert is not present" do
+ @config_server.certificate = nil
+ @config_server.base_url.should =~ /http:\/\/.*/
+ end
+ end
+
+ describe "error behavior: invalid credentials" do
+ before(:each) do
+ @config_server = Factory.build :invalid_credentials_config_server
+ end
+
+ it "should report an error when unauthorized" do
+ @config_server.should_not be_valid
+ @config_server.errors.full_messages.join(" ").should
include_text("Could not validate config server connection")
+ end
+ end
+end
diff --git a/src/spec/models/provider_account_spec.rb
b/src/spec/models/provider_account_spec.rb
index 370a132..d3c53b8 100644
--- a/src/spec/models/provider_account_spec.rb
+++ b/src/spec/models/provider_account_spec.rb
@@ -18,6 +18,13 @@ describe ProviderAccount do
@provider_account.should be_frozen
end
+ it "should be destroyable if it has a config server" do
+ @provider_account.config_server = ConfigServer.new
+ @provider_account.destroyable?.should be_true
+ @provider_account.destroy.equal?((a)provider_account).should be_true
+ @provider_account.should be_frozen
+ end
+
it "should check the validitiy of the cloud account login credentials" do
mock_provider = Factory :mock_provider
ACK and pushed.
Great work!
Thomas
--
No trees were killed to send this message, but a large number of
electrons were terribly inconvenienced.