thundercat 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # Deploy & Monitor RAP Packaged Rack Applications
2
+
3
+ ThunderCat is a ruby application container for Rack based applications. It is very similar in concept to war files and Tomcat. When you drop a .rap file into ThunderCats webapps directory it will auto expand and start the webapp.
4
+ There is a web admin panel which lets you upload a .rap archive and also see which webapps are running and start/stop/remove them.
5
+
6
+ [![Build Status](https://travis-ci.org/masterthought/thundercat.png?branch=master)](https://travis-ci.org/masterthought/thundercat)
7
+
8
+ ## Background
9
+
10
+ ThunderCat aims to be a sort of rack application container. It's not really a container but more of a place to deploy all your sinatra/rails/rack applications in one place and start/stop/remove them. The core
11
+ philosophy is to have a single artifact (a .rap archive) which can be dropped into ThunderCat and auto startup to make deployment extremely simple.
12
+
13
+ It might not suit everyone but it could be very useful for deploying internal applications inside a big corporation or even just your blog up to your website if you control the server it lives on. Instead of using something
14
+ like capistrano you just drop a .rap archive into ThunderCat and it does the rest.
15
+
16
+ Currently thin and unicorn are supported but raise and issue if you want any others supported.
17
+
18
+ ThunderCat is designed to be used in conjunction with [Rappa](https://github.com/masterthought/rappa). Using Rappa you create your .rap archive and then upload it to ThunderCat via the admin panel or use the rappa deploy command.
19
+
20
+ ## Install
21
+
22
+ gem install thundercat
23
+
24
+ ## Usage
25
+
26
+ First you need to create your ThunderCat file structure:
27
+
28
+ thundercat my_apps
29
+
30
+ This will create a directory structure with the path you provide it - the structure is as follows:
31
+
32
+ ![file structure]
33
+ (https://github.com/masterthought/thundercat/raw/master/.README/thundercat_structure.jpg)
34
+
35
+ The start.sh and stop.sh scripts are used to start and stop ThunderCat. Logs go into the logs directory and webapps go into the webapps directory. The archive directory holds backups when you deploy/remove apps via the admin panel.
36
+ The monitor.rb file is what gets started and monitors the webapps directory for changes.
37
+
38
+ When you start ThunderCat for the first time it starts on port 9898 with the following credentials:
39
+
40
+ username: thundercat
41
+ password: thundercat
42
+
43
+ ## Config
44
+
45
+ You can configure the username and password and the port ThunderCat starts on in the config.yml in webapps/thundercat. You have to startup thundercat first in order for the thundercat.rap to be expanded and installed - but after that you can go and edit the config.yml and restart thundercat.
46
+ You can also set the api_key - this is what you need in order to deploy from the rappa command as shown below.
47
+
48
+ ### deploy
49
+
50
+ This deploys a rap archive to a thundercat server e.g.
51
+
52
+ rappa deploy -r myapp.rap -u http://thundercat/api/deploy -k your_api_key
53
+
54
+ -r is to specify your rap archive and -u is the url of the deploy api where your thundercat instance is running. -k is your api_key which is configured in your
55
+ thundercat server.
56
+
57
+ ## Screenshots
58
+
59
+ ### Admin Page
60
+
61
+ ![admin page]
62
+ (https://github.com/masterthought/thundercat/raw/master/.README/thundercat_admin.jpg)
63
+
64
+ ### Login Page
65
+
66
+ ![login page]
67
+ (https://github.com/masterthought/thundercat/raw/master/.README/thundercat_login.jpg)
68
+
69
+ ## Additional Info
70
+
71
+ The bootstrap.sh could be used to set an environment variable like RAILS_ENV and the start.sh could contain a rake command in which the rakefile contains a start command that uses the environment variable to deploy with the correct environment.
72
+ This does mean you will have to expand the rap, update the bootstrap script and then package again if deploying to different environments needs different config. Any suggestions around this area are welcome and since this is
73
+ in the early stages it's fairly experimental.
74
+
75
+
76
+ ## Develop
77
+
78
+ Interested in contributing? Great just let me know how you want to help.
79
+
data/bin/thundercat ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fileutils'
3
+
4
+ name = ARGV[0]
5
+
6
+ if name.nil? or name.empty?
7
+ puts '[ThunderCat] Error you must supply a target directory name'
8
+ else
9
+
10
+ begin
11
+ rap = File.dirname(File.expand_path(__FILE__)) + '/../src/rap/thundercat.rap'
12
+ monitor = File.dirname(File.expand_path(__FILE__)) + '/../src/monitor/monitor.rb'
13
+ start_script = File.dirname(File.expand_path(__FILE__)) + '/../src/monitor/start.sh'
14
+ stop_script = File.dirname(File.expand_path(__FILE__)) + '/../src/monitor/stop.sh'
15
+
16
+ target_dir = "#{name}"
17
+ if File.exists?(target_dir)
18
+ puts "[ThunderCat] The directory you supplied: #{target_dir} already exists"
19
+ else
20
+ FileUtils.mkpath(target_dir)
21
+ FileUtils.mkpath("#{target_dir}/webapps")
22
+ FileUtils.mkpath("#{target_dir}/log")
23
+ FileUtils.mkpath("#{target_dir}/archive")
24
+ FileUtils.cp(rap, "#{target_dir}/webapps")
25
+ FileUtils.cp(monitor, target_dir)
26
+ FileUtils.cp(start_script, target_dir)
27
+ FileUtils.cp(stop_script, target_dir)
28
+ puts "[ThunderCat] Successfully created ThunderCat structure at: #{target_dir}"
29
+ end
30
+ rescue => e
31
+ puts "[ThunderCat] OOps something went wrong: #{e}"
32
+ end
33
+ end
34
+
35
+
36
+
37
+
38
+
39
+
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fssm'
3
+ require 'dante'
4
+ require 'yaml'
5
+ require 'rappa'
6
+
7
+ class Decision
8
+
9
+ def decide(option, base, relative, type)
10
+ rap_file = base + '/' + relative
11
+ rap_dir = path_without_ext(rap_file, base)
12
+ if option == :create
13
+ clean_rap(rap_dir, rap_file, base) if rap_already_deployed?(rap_dir)
14
+ deploy_rap(rap_file, rap_dir, base)
15
+ end
16
+ end
17
+
18
+ def initial(webapps_dir)
19
+ Dir.entries(webapps_dir).each do |entry|
20
+ if entry.match(/.rap$/)
21
+ decide(:create, webapps_dir, entry, 'file')
22
+ end
23
+ end
24
+ `cd #{webapps_dir}/thundercat; ./start.sh`
25
+ end
26
+
27
+ private
28
+
29
+ def deploy_rap(rap_file, rap_dir, base)
30
+ Rappa.new(:input_archive => rap_file, :output_archive => base).expand
31
+ rap_config = rap_dir + '/rap.yml'
32
+ if File.exists?(rap_config)
33
+ rap = YAML.load_file(rap_config)
34
+ start_script = rap[:start_script]
35
+ stop_script = rap[:stop_script]
36
+ bootstrap_script = rap[:bootstrap]
37
+ p "1. found boostrap script: #{bootstrap_script}"
38
+ `cd #{rap_dir} ; chmod +x #{start_script}`
39
+ `cd #{rap_dir} ; chmod +x #{stop_script}`
40
+ if !bootstrap_script.nil?
41
+ p "2. found boostrap script: #{bootstrap_script}"
42
+ `cd #{rap_dir} ; chmod +x #{bootstrap_script}`
43
+ puts "[Thundercat] executing boostrap: #{bootstrap_script}"
44
+ puts `cd #{rap_dir} ; ./#{bootstrap_script}`
45
+ end
46
+ `cd #{rap_dir} ; ./#{start_script}`
47
+ puts "[ThunderCat] Successfully deployed and started app at: #{rap_file}"
48
+ archive_rap(rap_file,base)
49
+ else
50
+ puts '[ThunderCat] no rap file found so could not start app'
51
+ end
52
+
53
+ end
54
+
55
+ def stop_app(rap_dir)
56
+ rap_file = rap_dir + '/rap.yml'
57
+ if File.exists?(rap_file)
58
+ rap = YAML.load_file(rap_file)
59
+ stop_script = rap[:stop_script]
60
+ `cd #{rap_dir} ; chmod +x #{stop_script}`
61
+ `cd #{rap_dir} ; ./#{stop_script}`
62
+ else
63
+ puts '[ThunderCat] no rap file found so could not stop app'
64
+ end
65
+ end
66
+
67
+ def archive_existing_app(rap_dir, rap_file, base)
68
+ FileUtils.mv(rap_dir, "#{base}/../archive/#{file_without_ext(rap_file)}.#{Time.now.to_i}")
69
+ end
70
+
71
+ def archive_rap(rap_file,base)
72
+ FileUtils.mv(rap_file, "#{base}/../archive/#{file_without_ext(rap_file)}.#{Time.now.to_i}.rap")
73
+ puts "[ThunderCat] archived rap file: #{rap_file}"
74
+ end
75
+
76
+ def rap_already_deployed?(rap_dir)
77
+ File.exists?(rap_dir)
78
+ end
79
+
80
+ def clean_rap(rap_dir, rap_file, base)
81
+ stop_app(rap_dir)
82
+ archive_existing_app(rap_dir, rap_file, base)
83
+ end
84
+
85
+ def path_without_ext(file, base)
86
+ base_name = File.basename(file)
87
+ base + '/' + base_name.chomp(File.extname(base_name))
88
+ end
89
+
90
+ def file_without_ext(file)
91
+ base_name = File.basename(file)
92
+ base_name.chomp(File.extname(base_name))
93
+ end
94
+
95
+ end
96
+
97
+
98
+ class Monitor
99
+
100
+ def self.go
101
+ begin
102
+ webapps_dir = File.dirname(__FILE__) + '/webapps'
103
+ Decision.new.initial(webapps_dir)
104
+ FSSM.monitor(webapps_dir, '**/*.rap', :directories => true) do
105
+ update { |base, relative, type| puts "updated #{base}, #{relative}, #{type}" }
106
+ delete { |base, relative, type| puts "delete #{base}, #{relative}, #{type}" }
107
+ create { |base, relative, type| Decision.new.decide(:create, base, relative, type) }
108
+ end
109
+ rescue => e
110
+ puts "[ThunderCat] Monitor encountered an error: #{e}"
111
+ end
112
+ end
113
+
114
+ end
115
+
116
+ Dante.run('monitor') do |opts|
117
+ Monitor.go
118
+ end
119
+
120
+
121
+
@@ -0,0 +1 @@
1
+ ./monitor.rb -P ./log/monitor.pid -d -l ./log/monitor.log
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fileutils'
3
+
4
+ begin
5
+
6
+ pid = File.read(File.dirname(__FILE__) + '/log/monitor.pid').strip
7
+ `kill -9 #{pid}`
8
+ FileUtils.rm(File.dirname(__FILE__) + '/log/monitor.pid')
9
+ puts "[ThunderCat] stopped thundercat monitor with process: #{pid}"
10
+
11
+ thunder_dir = File.dirname(__FILE__) + '/webapps/thundercat'
12
+ `cd #{thunder_dir} ; chmod +x ./stop.sh`
13
+ puts `cd #{thunder_dir} ; ./stop.sh`
14
+
15
+ puts "[ThunderCat] stopped thundercat webapp"
16
+
17
+ rescue => e
18
+ puts "[ThunderCat] Oops something went wrong: #{e}"
19
+ end
Binary file
metadata ADDED
@@ -0,0 +1,231 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thundercat
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kingsley Hendrickse
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: sinatra
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: sinatra-formhelpers
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: sinatra-single-user-auth
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: sucker_punch
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: fssm
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: thin
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: log4r
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: rake
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :runtime
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: sys-proctable
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :runtime
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: jeweler
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :runtime
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ description: ! 'Easy way to deploy and monitor Rack applications as .rap archives
191
+
192
+ '
193
+ email: kingsley@masterthought.net
194
+ executables:
195
+ - /thundercat
196
+ extensions: []
197
+ extra_rdoc_files:
198
+ - README.md
199
+ files:
200
+ - src/rap/thundercat.rap
201
+ - src/monitor/monitor.rb
202
+ - src/monitor/start.sh
203
+ - src/monitor/stop.sh
204
+ - README.md
205
+ - bin/thundercat
206
+ homepage: https://github.com/masterthought/thundercat
207
+ licenses:
208
+ - Apache 2.0
209
+ post_install_message:
210
+ rdoc_options: []
211
+ require_paths:
212
+ - src
213
+ required_ruby_version: !ruby/object:Gem::Requirement
214
+ none: false
215
+ requirements:
216
+ - - ! '>='
217
+ - !ruby/object:Gem::Version
218
+ version: '0'
219
+ required_rubygems_version: !ruby/object:Gem::Requirement
220
+ none: false
221
+ requirements:
222
+ - - ! '>='
223
+ - !ruby/object:Gem::Version
224
+ version: '0'
225
+ requirements: []
226
+ rubyforge_project:
227
+ rubygems_version: 1.8.25
228
+ signing_key:
229
+ specification_version: 3
230
+ summary: Thundercat Rack deployment and monitoring
231
+ test_files: []