On 07/18/2011 11:36 PM, Mike Orazi wrote:
> On 07/18/2011 10:02 AM, Tomas Sedovic wrote:
>> 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
Hey Mike,
Sorry, I didn't know this shouldn't have been pushed yet. I knew Greg
didn't have the commit rights so I just went along and let it in.
The commit is reverted now.
Thomas
No worries at all. I really appreciate the fact that you took the
initiative to make sure stuff you thought needed to be in the code base
got in there.
And reverts are pretty easy so no big foul! ;-)
Thanks,
Mike