On 07/14/2011 05:14 PM, gblomqui(a)redhat.com wrote:
> 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
I think there was a little miscommunication here. I believe this work
was intended to land shortly after the community release (likely
sometime next week). Can you revert this change in the public repo for
now & we'll work with Greg to get it pushed after things settle down
with the community release, etc.
Thanks in advance,
Mike