avodeploy 0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,112 @@
1
+ =begin
2
+ AVOCADO
3
+ The flexible and easy to use deployment framework for web applications
4
+
5
+ This program is free software; you can redistribute it and/or
6
+ modify it under the terms of the GNU General Public License Version 2
7
+ as published by the Free Software Foundation.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program; if not, write to the Free Software
16
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+ =end
18
+
19
+ module Avocado
20
+ class RemoteTaskExecutionEnvironment < TaskExecutionEnvironment
21
+
22
+ attr_accessor :config
23
+
24
+ # Creates a connection between the local and the remote system over ssh
25
+ def establish_connection
26
+ Avocado::Deployment.instance.log.info "connecting to #{get(:user)}@#{get(:host)}..."
27
+
28
+ begin
29
+ @session = ::Net::SSH.start(get(:host), get(:user))
30
+ rescue ::Net::SSH::AuthenticationFailed => e
31
+ handle_abort e
32
+ end
33
+ end
34
+
35
+ # Checks, if all utilities are available for the deployment process
36
+ # to be executed
37
+ #
38
+ # @param utils [Array] array with utilities to check
39
+ def check_util_availability(utils)
40
+ super(utils, 'remotely')
41
+ end
42
+
43
+ # Executes a command via ssh
44
+ #
45
+ # @param ssh [Net::SSH::Connection::Session] ssh session
46
+ # @param command [String] the command to execute
47
+ def ssh_exec!(ssh, command)
48
+ stdout_data = ""
49
+ stderr_data = ""
50
+ exit_code = nil
51
+ exit_signal = nil
52
+ ssh.open_channel do |channel|
53
+ channel.exec(command) do |ch, success|
54
+ unless success
55
+ abort "FAILED: couldn't execute command (ssh.channel.exec)"
56
+ end
57
+ channel.on_data do |ch,data|
58
+ stdout_data+=data
59
+ end
60
+
61
+ channel.on_extended_data do |ch,type,data|
62
+ stderr_data+=data
63
+ end
64
+
65
+ channel.on_request("exit-status") do |ch,data|
66
+ exit_code = data.read_long
67
+ end
68
+
69
+ channel.on_request("exit-signal") do |ch, data|
70
+ exit_signal = data.read_long
71
+ end
72
+ end
73
+ end
74
+ ssh.loop
75
+
76
+ result = Avocado::CommandExecutionResult.new
77
+ result.stdin = command
78
+ result.stdout = stdout_data
79
+ result.stderr = stderr_data
80
+ result.retval = exit_code
81
+
82
+ result
83
+ end
84
+
85
+ # Executes a command on the remote system
86
+ #
87
+ # @param cmd [String] the command to execute
88
+ # @return [CommandExecutionResult] result of the command exection
89
+ def command(cmd)
90
+ Avocado::Deployment.instance.log.info "Executing [" + cmd.yellow + "] on remote " + get(:ssh_host).cyan
91
+
92
+ result = Avocado::CommandExecutionResult.new
93
+
94
+ begin
95
+ result = ssh_exec!(@session, cmd)
96
+
97
+ if result.stdout.nil? == false && result.stdout.empty? == false
98
+ Avocado::Deployment.instance.log.debug "Stdout@#{get(:host)}: ".cyan + result.stdout.green
99
+ end
100
+
101
+ if result.stderr.nil? == false && result.stderr.empty? == false
102
+ Avocado::Deployment.instance.log.debug "Stderr@#{get(:host)}: ".cyan + result.stderr.red
103
+ end
104
+ rescue Exception => e
105
+ handle_abort e
106
+ end
107
+
108
+ result
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,84 @@
1
+ =begin
2
+ AVOCADO
3
+ The flexible and easy to use deployment framework for web applications
4
+
5
+ This program is free software; you can redistribute it and/or
6
+ modify it under the terms of the GNU General Public License Version 2
7
+ as published by the Free Software Foundation.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program; if not, write to the Free Software
16
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+ =end
18
+
19
+ module Avocado
20
+ class Task
21
+
22
+ attr_accessor :name
23
+ attr_accessor :scope
24
+ attr_accessor :visibility
25
+ attr_accessor :block
26
+ attr_accessor :desc
27
+
28
+ # Creates a new task from a task block in the deployment configuration process
29
+ #
30
+ # @param name [Symbol] name of the task
31
+ # @param options [Hash] command options
32
+ # @param block [Block] code block of the task
33
+ # @return [Task] the task instance
34
+ def self.from_task_block(name, options, &block)
35
+ instance = self.new
36
+
37
+ instance.name = name
38
+ instance.block = block
39
+
40
+ instance.scope = :local
41
+
42
+ if options.has_key?(:scope) && options[:scope] == :remote
43
+ instance.scope = :remote
44
+ end
45
+
46
+ instance.visibility = :public
47
+
48
+ if options.has_key?(:visibility) && options[:visibility] == :private
49
+ instance.visibility = :private
50
+ end
51
+
52
+ if options.has_key?(:desc)
53
+ instance.desc = options[:desc]
54
+ end
55
+
56
+ instance
57
+ end
58
+
59
+ # Runs the code of a task
60
+ #
61
+ # @param env [TaskExecutionEnvironment] the environment to invoke the task in
62
+ # @return [mixed] result of the code block
63
+ def invoke(env)
64
+ raise ArgumentError 'env must be a valid TaskExecutionEnvironment' unless env.kind_of?(Avocado::TaskExecutionEnvironment)
65
+
66
+ avo = Avocado::Deployment.instance
67
+
68
+ avo.log.debug "Running task #{@name}"
69
+
70
+ env.instance_eval(&@block)
71
+ end
72
+
73
+ # Prettyprints a command using a table
74
+ def pretty_print
75
+ puts Terminal::Table.new :title => 'Task overview', :rows => [
76
+ ['Name', @name.to_s],
77
+ ['Description', @desc.to_s],
78
+ ['Scope', @scope.to_s],
79
+ ['Visibility', @visibility.to_s],
80
+ ]
81
+ end
82
+
83
+ end
84
+ end
@@ -0,0 +1,25 @@
1
+ =begin
2
+ AVOCADO
3
+ The flexible and easy to use deployment framework for web applications
4
+
5
+ This program is free software; you can redistribute it and/or
6
+ modify it under the terms of the GNU General Public License Version 2
7
+ as published by the Free Software Foundation.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program; if not, write to the Free Software
16
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+ =end
18
+
19
+ module Avocado
20
+ class TaskDependency
21
+ attr_accessor :task_name
22
+ attr_accessor :dependent_task_name
23
+ attr_accessor :type
24
+ end
25
+ end
@@ -0,0 +1,86 @@
1
+ =begin
2
+ AVOCADO
3
+ The flexible and easy to use deployment framework for web applications
4
+
5
+ This program is free software; you can redistribute it and/or
6
+ modify it under the terms of the GNU General Public License Version 2
7
+ as published by the Free Software Foundation.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program; if not, write to the Free Software
16
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+ =end
18
+
19
+ module Avocado
20
+ class TaskExecutionEnvironment
21
+
22
+ # Initialized the environment
23
+ #
24
+ # @param config [Hash] deployment configuration
25
+ def initialize(config)
26
+ # @todo check
27
+ @config = config
28
+ end
29
+
30
+ # Assigns the scm provider
31
+ #
32
+ # @param scm_provider [ScmProvider] the scm provider to assign
33
+ def scm_provider=(scm_provider)
34
+ @scm = scm_provider
35
+ end
36
+
37
+ # Checks, if all utilities are available for the deployment process
38
+ # to be executed
39
+ #
40
+ # @param utils [Array] array with utilities to check
41
+ def check_util_availability(utils, system_name)
42
+ begin
43
+ utils.each do |util|
44
+ if command("command -v #{util} >/dev/null 2>&1 || exit 1;").retval == 1
45
+ msg = "command line utility '#{util}' is not installed #{system_name}"
46
+
47
+ raise RuntimeError, msg
48
+ end
49
+ end
50
+ rescue Exception => e
51
+ handle_abort e
52
+ end
53
+ end
54
+
55
+ # Returns the logger instance
56
+ #
57
+ # @return [Logger] log instance
58
+ def log
59
+ Avocado::Deployment.instance.log
60
+ end
61
+
62
+ # Sets a configuration item
63
+ #
64
+ # @param key [Symbol] configuration key
65
+ # @param value [mixed] configuration value
66
+ def set(key, value)
67
+ @config[key] = value
68
+ end
69
+
70
+ # Returns a configuration item if set
71
+ #
72
+ # @param key [Symbol] configuration key
73
+ # @return [mixed] configuration value
74
+ def get(key)
75
+ @config[key]
76
+ end
77
+
78
+ # Shorthand for exception handling
79
+ #
80
+ # @param e [Exception] the exception to handle
81
+ def handle_abort(e)
82
+ Avocado::Deployment.instance.handle_abort(e)
83
+ end
84
+
85
+ end
86
+ end
@@ -0,0 +1,183 @@
1
+ =begin
2
+ AVOCADO
3
+ The flexible and easy to use deployment framework for web applications
4
+
5
+ This program is free software; you can redistribute it and/or
6
+ modify it under the terms of the GNU General Public License Version 2
7
+ as published by the Free Software Foundation.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program; if not, write to the Free Software
16
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
+ =end
18
+
19
+ module Avocado
20
+ class TaskManager
21
+
22
+ attr_reader :dependencies
23
+ attr_reader :chains
24
+
25
+ # Initializes the task manager
26
+ def initialize
27
+ @chains = []
28
+ @remote_env = nil
29
+ @local_env = nil
30
+ end
31
+
32
+ # Adds a task to the task manager
33
+ #
34
+ # @param name [Symbol] task name
35
+ # @param options [Hash] task options
36
+ # @param block [Block] code of the task
37
+ def add_task(name, options, &block)
38
+ position = :after
39
+ standalone = true
40
+
41
+ if options.has_key?(:before)
42
+ position = :before
43
+ end
44
+
45
+ key = name
46
+
47
+ if options.has_key?(:before)
48
+ key = options[:before]
49
+ standalone = false
50
+ elsif options.has_key?(:after)
51
+ key = options[:after]
52
+ standalone = false
53
+ end
54
+
55
+ if standalone == false
56
+ idx = find_chain_index_containing(key)
57
+
58
+ @chains[idx].delete(name)
59
+ @chains[idx].insert_at(position, key, [ name, Avocado::Task.from_task_block(name, options, &block) ])
60
+
61
+ else
62
+ chain = {}
63
+ chain[name] = Avocado::Task.from_task_block(name, options, &block)
64
+ @chains << chain
65
+ end
66
+ end
67
+
68
+ # Finds a task by its name
69
+ #
70
+ # @param name [Symbol] name of the task
71
+ # @return [Task] the task if found
72
+ def task_by_name(name)
73
+ name = name.to_sym if name.is_a?(String)
74
+
75
+ cidx = find_chain_index_containing(name)
76
+ @chains[cidx][name]
77
+ end
78
+
79
+ # Finds the chain containing a specifc task
80
+ #
81
+ # @param name [Symbol] task name
82
+ # @param [Integer] chain index
83
+ def find_chain_index_containing(name)
84
+ @chains.each_with_index do |chain, idx|
85
+ if chain.has_key?(name)
86
+ return idx
87
+ end
88
+ end
89
+
90
+ raise RuntimeError, "could not find a chain containing task #{name}"
91
+ end
92
+
93
+ # Invokes a task without dependencies
94
+ #
95
+ # @param task_name [Symbol] the task name
96
+ def invoke_task_oneshot(task_name)
97
+ task_name = task_name.to_sym if task_name.is_a?(String)
98
+
99
+ cidx = find_chain_index_containing(task_name)
100
+
101
+ begin
102
+ invoke_task(@chains[cidx][task_name])
103
+ rescue Exception => e
104
+ Avocado::Deployment.instance.handle_abort(e)
105
+ end
106
+ end
107
+
108
+ # Invokes the task chain, that contains the requested task
109
+ #
110
+ # @param task_name [Symbol] the task name
111
+ def invoke_task_chain_containing(task_name)
112
+ task_name = task_name.to_sym if task_name.is_a?(String)
113
+
114
+ cidx = find_chain_index_containing(task_name)
115
+
116
+ begin
117
+ @chains[cidx].each_pair do |name, task|
118
+ invoke_task(task)
119
+ end
120
+ rescue Exception => e
121
+ Avocado::Deployment.instance.handle_abort(e)
122
+ end
123
+ end
124
+
125
+ # Executes a task for all defined targets
126
+ #
127
+ # @param task [Task] the task to start
128
+ # @param env [RemoteTaskExecutionEnvironment] the environment
129
+ def execute_for_each_target(task, env)
130
+ raise ArgumentError, 'task must be a task' unless task.kind_of?(Avocado::Task)
131
+ raise ArgumentError, 'env must be a RemoteTaskExecutionEnvironment' unless env.kind_of?(Avocado::RemoteTaskExecutionEnvironment)
132
+
133
+ avo = Avocado::Deployment.instance
134
+
135
+ avo.config.targets.each_pair do |key, target|
136
+ avo.log.info "invoking task #{task.name} for target #{target.name}..."
137
+
138
+ env.config.merge!(target.config)
139
+
140
+ env.establish_connection
141
+
142
+ task.invoke(env)
143
+ end
144
+ end
145
+
146
+ # Invokes a task
147
+ #
148
+ # @param task [Task] the task
149
+ def invoke_task(task)
150
+ raise ArgumentError, 'task must be a task' unless task.kind_of?(Avocado::Task)
151
+
152
+ avo = Avocado::Deployment.instance
153
+ env = nil
154
+
155
+ if task.scope == :remote
156
+ if @remote_env.nil?
157
+ @remote_env = Avocado::RemoteTaskExecutionEnvironment.new(avo.config.config)
158
+ end
159
+
160
+ env = @remote_env
161
+ elsif task.scope == :local
162
+ if @local_env.nil?
163
+ @local_env = Avocado::LocalTaskExecutionEnvironment.new(avo.config.config)
164
+ end
165
+
166
+ env = @local_env
167
+ else
168
+ raise RuntimeError, 'scope must either be remote or local'
169
+ end
170
+
171
+ scm_provider = ScmProvider.new(env, avo.config.get(:scm))
172
+ env.scm_provider = scm_provider
173
+
174
+ # if remote task -> execute for each target
175
+ if task.scope == :remote
176
+ execute_for_each_target(task, env)
177
+ else
178
+ task.invoke(env)
179
+ end
180
+ end
181
+
182
+ end
183
+ end