alphonse 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ require 'alphonse/connection'
2
+ require 'alphonse/configs/operation'
3
+ require 'alphonse/configs/bundler'
4
+
5
+ module Alphonse
6
+ class Operator
7
+ include Alphonse::Configs::Tasks
8
+ include Alphonse::Configs::Bundler
9
+ attr_reader :config
10
+
11
+ def initialize(config = {})
12
+ @config = config
13
+ end
14
+
15
+ def execute(command)
16
+ hosts.each do |host_connection|
17
+ begin
18
+ Alphonse.logger.info "\e[4mRunning on #{host_connection.connection_string}\e[0m"
19
+
20
+ host_connection.send_and_execute send(command)
21
+
22
+ Alphonse.logger.success "Aaaaaay! All Actions Completed"
23
+ rescue Exception => e
24
+ Alphonse.logger.error "#{e.class.to_s} : #{e.message}\n\n#{e.backtrace.join("\n")}"
25
+ break
26
+ ensure
27
+ host_connection.close
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ # Create and setup folder on remote server
35
+ def setup
36
+ Alphonse.logger.operation config[:setup_description]
37
+ prerequisite.push config[:setup].map { |task| send task }
38
+ end
39
+
40
+ # First time deploy of app to remote server
41
+ def deploy
42
+ Alphonse.logger.operation config[:deploy_description]
43
+ prerequisite.push config[:deploy].map { |task| send(task) }
44
+ end
45
+
46
+ # Issue bundle command on remote server
47
+ def bundle_update
48
+ Alphonse.logger.operation config[:bundle_description]
49
+ prerequisite.push config[:bundle_update].map { |task| send(task) }
50
+ end
51
+
52
+ # Pull latest on remote server
53
+ def update
54
+ Alphonse.logger.operation config[:update_description]
55
+ prerequisite.push config[:update].map { |task| send(task) }
56
+ end
57
+
58
+ # Restart the app
59
+ def restart
60
+ Alphonse.logger.operation config[:restart_description]
61
+ prerequisite.push config[:restart].map { |task| send(task) }
62
+ end
63
+
64
+ def prerequisite
65
+ [cd_to_path, 'ls -al', 'which ruby']
66
+ end
67
+
68
+ def hosts
69
+ @hosts ||= begin
70
+ host_connections, hosts, user = [], (config[:hosts] rescue nil), (config[:user] rescue nil)
71
+ hosts.each {|host| host_connections << Connection.new(host, user, config) } if hosts && user;
72
+ host_connections
73
+ end
74
+ end
75
+
76
+
77
+ end
78
+ end
@@ -0,0 +1,9 @@
1
+ module Alphonse
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+ PRE = nil
7
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
8
+ end
9
+ end
data/lib/alphonse.rb ADDED
@@ -0,0 +1,46 @@
1
+ require "alphonse/version"
2
+ require "alphonse/logger"
3
+
4
+ module Alphonse
5
+
6
+ # Base class for all Alphonse exceptions
7
+ class AlphonseError < StandardError
8
+ end
9
+
10
+ # When a command(s) return a stderr stream
11
+ class StdError < AlphonseError
12
+ end
13
+
14
+ # When a configuration file is missing
15
+ class NoFonzfileError < AlphonseError
16
+ end
17
+
18
+ # When a host is not defined
19
+ class MissingUserError < AlphonseError
20
+ end
21
+
22
+ # When a host is not defined
23
+ class MissingHostError < AlphonseError
24
+ end
25
+
26
+ # When an operation is not defined
27
+ class MissingOperationError < AlphonseError
28
+ end
29
+
30
+ # When an operation is not defined
31
+ class MissingCommandError < AlphonseError
32
+ end
33
+
34
+ # When an operation is invalid
35
+ class InvalidOperationError < AlphonseError
36
+ end
37
+
38
+ # When a command is not present or formed corrently
39
+ class CliCommandError < AlphonseError
40
+ end
41
+
42
+ # When an argument is not present or well formed
43
+ class CliArgumentError < AlphonseError
44
+ end
45
+
46
+ end
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/cli'
5
+
6
+ describe "Cli" do
7
+
8
+ describe "with an operation passed" do
9
+
10
+ before do
11
+ @command = 'deploy'
12
+ @cli = Alphonse::Cli.new(@command.split(' '))
13
+ end
14
+
15
+ it "should determine the operation to run" do
16
+ @cli.command.should == :deploy
17
+ end
18
+
19
+ it "should match the default options" do
20
+ @cli.options.should == { :environment => :production }
21
+ end
22
+
23
+ end
24
+
25
+ describe "with an operation and environment passed" do
26
+
27
+ before do
28
+ @command = 'deploy -e staging'
29
+ @cli = Alphonse::Cli.new(@command.split(' '))
30
+ end
31
+
32
+ it "should determine the operation to run" do
33
+ @cli.command.should == :deploy
34
+ end
35
+
36
+ it "should match the passed options" do
37
+ @cli.options.should == { :environment => :staging }
38
+ end
39
+
40
+ end
41
+
42
+
43
+ describe "with no arguments" do
44
+ it 'should raise an error' do
45
+ lambda { @cli = Alphonse::Cli.new }.should raise_error(Alphonse::CliArgumentError)
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/config'
5
+
6
+ describe "Config" do
7
+
8
+ describe "with specified Fonzfile" do
9
+ before do
10
+ @config = Alphonse::Config.new(:file_name => 'spec/fonzfiles/default', :test_variable => 1)
11
+ end
12
+
13
+ it 'should load the config file' do
14
+ @config.config_loaded?.should be_true
15
+ end
16
+
17
+ it 'should have attributes set' do
18
+ @config[:user].should_not be_nil
19
+ @config[:hosts].should_not be_nil
20
+ end
21
+
22
+ it 'should be able to access given attributes' do
23
+ @config[:test_variable].should equal(1)
24
+ end
25
+
26
+ it 'should set new attributes' do
27
+ @config.set_attr :new_variable => 'new value'
28
+ @config[:new_variable].should == 'new value'
29
+ end
30
+
31
+ it 'should re set an attribute' do
32
+ @config.set_attr :test_variable => 2
33
+ @config[:test_variable].should equal(2)
34
+ end
35
+
36
+ describe 'calling operator' do
37
+ it 'should initialise and return new operator' do
38
+ @config.operator.should_not be_nil
39
+ @config.operator.class.should equal(Alphonse::Operator)
40
+ end
41
+ end
42
+ end
43
+
44
+
45
+ describe 'without specified Fonzfile' do
46
+ before do
47
+ @config = Alphonse::Config.new
48
+ end
49
+
50
+ it 'should use default Fonzfile' do
51
+ @config.config_loaded?.should be_true
52
+ end
53
+
54
+ it 'should have attributes set' do
55
+ @config[:user].should_not be_nil
56
+ @config[:hosts].should_not be_nil
57
+ end
58
+
59
+ end
60
+
61
+
62
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/configs/bundler'
5
+
6
+ describe "Configs::Bundler" do
7
+ before do
8
+
9
+ @klass = Class.new do
10
+ include Alphonse::Configs::Bundler
11
+ attr_reader :config
12
+
13
+ def initialize
14
+ @config = {}
15
+ end
16
+
17
+ def config=(hash)
18
+ @config = hash
19
+ end
20
+
21
+ end.new
22
+
23
+ end
24
+
25
+ after do
26
+ @klass = nil
27
+ end
28
+
29
+ describe "with a Gemfile" do
30
+ before do
31
+ @klass.should_receive(:using_bundler?).and_return(true)
32
+ end
33
+
34
+ describe "with ruby_bin_path" do
35
+ before do
36
+ @klass.config = {:ruby_bin_path => "path/to/ruby/"}
37
+ end
38
+
39
+ describe "when calling bundle_install" do
40
+ it "should return command prepended with ruby_bin_path and bundle exec" do
41
+
42
+ @klass.bundle_install.should == "path/to/ruby/bundle install --deployment --without development test"
43
+ end
44
+ end
45
+
46
+ describe "when calling rake command" do
47
+ it "should return rake command prepended with ruby_bin_path and bundle exec" do
48
+ @klass.rake("--tasks").should == "path/to/ruby/bundle exec rake --tasks"
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
56
+ describe "without a Gemfile" do
57
+ before do
58
+ @klass.should_receive(:using_bundler?).and_return(false)
59
+ end
60
+
61
+ describe "without ruby_bin_path" do
62
+ before do
63
+ @klass.config = {}
64
+ end
65
+
66
+ describe "when calling bundle_install" do
67
+ it "should return command without prepends" do
68
+ @klass.bundle_install.should be_nil
69
+ end
70
+ end
71
+
72
+ describe "when calling rake command" do
73
+ it "should return rake command without prepends" do
74
+ @klass.rake("--tasks").should == "rake --tasks"
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ end
81
+
82
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/configs/operation'
5
+
6
+ describe "Configs::Operation" do
7
+ before do
8
+ @klass = Class.new do
9
+ include Alphonse::Configs::Operation
10
+ include Alphonse::Configs::Tasks
11
+ attr_reader :config
12
+
13
+ def initialize
14
+ @config = {}
15
+ end
16
+
17
+ def config=(hash)
18
+ @config = hash
19
+ end
20
+
21
+ def set_attr(hash)
22
+ config.merge! hash
23
+ end
24
+
25
+ end.new
26
+
27
+ @test_tasks = [:source_shell_rc, :git_pull]
28
+ end
29
+
30
+ after do
31
+ @klass = nil
32
+ end
33
+
34
+ describe "Given operation and tasks" do
35
+ before do
36
+ @klass.operation :test_operation, "Test Operation Deascription"
37
+ @klass.tasks(@test_tasks)
38
+ end
39
+
40
+ it "should set given tasks in operation key of config" do
41
+ @klass.config[:test_operation].should == @test_tasks
42
+ end
43
+
44
+ it "should set given description in config" do
45
+ @klass.config[:test_operation_description].should_not be_nil
46
+ end
47
+
48
+ describe "Calling tasks" do
49
+ it "should return commands as strings" do
50
+ task = @klass.config[:test_operation].first
51
+ @klass.send(task).should == "source ~/.bashrc"
52
+
53
+ task = @klass.config[:test_operation].last
54
+ @klass.send(task).should == ["git checkout master -q", "git pull origin master -q", "git gc --aggressive"]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/configs/setting'
5
+
6
+ describe "Configs::Setting" do
7
+ before do
8
+ @klass = Class.new do
9
+ include Alphonse::Configs::Setting
10
+ attr_reader :config
11
+
12
+ def initialize
13
+ @config = {}
14
+ end
15
+
16
+ def config=(hash)
17
+ @config = hash
18
+ end
19
+
20
+ def set_attr(hash)
21
+ config.merge! hash
22
+ end
23
+
24
+ end.new
25
+ end
26
+
27
+ after do
28
+ @klass = nil
29
+ end
30
+
31
+ describe "with a single given value for an attribute" do
32
+ before do
33
+ @klass.user("user_name")
34
+ end
35
+
36
+ it "should set correct attribute" do
37
+ @klass.config[:user].should == "user_name"
38
+ end
39
+ end
40
+
41
+ describe "with an array of values given for an attribute" do
42
+ before do
43
+ @klass.hosts(["host.address", "alt_host.address"])
44
+ end
45
+
46
+ it "should set attribute with correct values" do
47
+ @klass.config[:hosts].should == ["host.address", "alt_host.address"]
48
+ end
49
+ end
50
+
51
+
52
+ end
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/connection'
5
+
6
+ describe "Connection" do
7
+ describe "with host and user" do
8
+ before do
9
+ mock_net_ssh_session!
10
+ @connection = Alphonse::Connection.new("localhost", "username", {})
11
+ end
12
+
13
+ describe "with given commands" do
14
+ it "should send and excute and return array" do
15
+ session.should_receive(:run).and_return(@result)
16
+
17
+ results = @connection.send_and_execute("date")
18
+ results.class.should equal(Array)
19
+ end
20
+
21
+ it "should set host and username" do
22
+ @connection.connection_string.should == "username@localhost"
23
+ end
24
+
25
+ it "should be able to close session" do
26
+ @connection.close.should equal(true)
27
+ end
28
+ end
29
+
30
+ describe "without given commands" do
31
+ it "should return an empty array" do
32
+ result = @connection.send_and_execute()
33
+ result.class.should equal(Array)
34
+ result.empty?.should be_true
35
+ end
36
+ end
37
+
38
+ describe "given invalid command" do
39
+ it "shoould raise Alphonse::MalformedCommandError" do
40
+ lambda { @connection.send_and_execute(12) }.should raise_error(Alphonse::MissingCommandError)
41
+ end
42
+ end
43
+ end
44
+
45
+ describe "without" do
46
+ describe "host" do
47
+ it "should raise Alphonse::MissingHostError" do
48
+ lambda { Alphonse::Connection.new(nil, "username", {}) }.should raise_error(Alphonse::MissingHostError)
49
+ end
50
+ end
51
+
52
+ describe do
53
+ it "should raise Alphonse::MissingUserError" do
54
+ lambda { Alphonse::Connection.new("localhost", nil, {}) }.should raise_error(Alphonse::MissingUserError)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'alphonse/config'
5
+ require 'alphonse/operator'
6
+
7
+ describe "Operator" do
8
+
9
+ describe "initialised with config" do
10
+ before do
11
+ @config = Alphonse::Config.new(:file_name => 'spec/fonzfiles/default')
12
+ @operator = Alphonse::Operator.new(@config)
13
+ end
14
+
15
+ it "should have array of Connections" do
16
+ @operator.send(:hosts).class.should equal(Array)
17
+ @operator.send(:hosts).first.class.should equal(Alphonse::Connection)
18
+ end
19
+
20
+ describe "with hosts" do
21
+ before do
22
+ mock_connection!
23
+ @operator.stub(:hosts).and_return([@connection])
24
+ end
25
+
26
+ it "should execute setup operation" do
27
+ @operator.execute(:setup).class.should equal(Array)
28
+ end
29
+
30
+ it "should execute deploy operation" do
31
+ @operator.execute(:deploy).class.should equal(Array)
32
+ end
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ user 'remote_user'
2
+ hosts 'stage.example.com'
3
+ path "/fullpath/to/folder"
4
+ git_repo 'git@gitaddress:repository.git'
5
+ branch 'master'
6
+
7
+ operation :setup, 'Setup server' do
8
+ tasks :clone_repository
9
+ end
10
+
11
+ operation :deploy, 'Deploy repository' do
12
+ tasks :install_gems, :setup_database, :restart_app
13
+ end
14
+
15
+ operation :update, 'Update the repository on the server' do
16
+ tasks :update_repository, :install_gems, :update_database, :restart_app
17
+ end
18
+
19
+ operation :restart, 'Restart application' do
20
+ tasks :restart_app
21
+ end
@@ -0,0 +1,37 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+
4
+ require 'alphonse'
5
+ require 'rspec'
6
+
7
+ RSpec.configure do |config|
8
+ # config
9
+ end
10
+
11
+ def mock_connection!
12
+ @connection = double('connection')
13
+ @connection.stub(:send_and_execute).and_return([])
14
+ @connection.stub(:connection_string).and_return("connection_string")
15
+ @connection.stub(:close).and_return(true)
16
+ end
17
+
18
+ def mock_net_ssh_session!
19
+ @result = double('result')
20
+ @result.stub(:failure?).and_return(:false)
21
+ @result.stub(:output).and_return("Output")
22
+ @result.stub(:duration).and_return(100)
23
+ @session = double("session")
24
+ @session.stub(:close).and_return(true)
25
+ @session.stub(:open).and_return(true)
26
+ @session.stub(:run).and_return(@result)
27
+ Net::SSH::Session.stub(:new).and_return(@session)
28
+ end
29
+
30
+
31
+ def connection
32
+ @connection
33
+ end
34
+
35
+ def session
36
+ @session
37
+ end