sunshine 1.0.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +237 -0
- data/Manifest.txt +70 -0
- data/README.txt +277 -0
- data/Rakefile +46 -0
- data/bin/sunshine +5 -0
- data/examples/deploy.rb +61 -0
- data/examples/deploy_tasks.rake +112 -0
- data/examples/standalone_deploy.rb +31 -0
- data/lib/commands/add.rb +96 -0
- data/lib/commands/default.rb +169 -0
- data/lib/commands/list.rb +322 -0
- data/lib/commands/restart.rb +62 -0
- data/lib/commands/rm.rb +83 -0
- data/lib/commands/run.rb +151 -0
- data/lib/commands/start.rb +72 -0
- data/lib/commands/stop.rb +61 -0
- data/lib/sunshine/app.rb +876 -0
- data/lib/sunshine/binder.rb +70 -0
- data/lib/sunshine/crontab.rb +143 -0
- data/lib/sunshine/daemon.rb +380 -0
- data/lib/sunshine/daemons/ar_sendmail.rb +28 -0
- data/lib/sunshine/daemons/delayed_job.rb +30 -0
- data/lib/sunshine/daemons/nginx.rb +104 -0
- data/lib/sunshine/daemons/rainbows.rb +35 -0
- data/lib/sunshine/daemons/server.rb +66 -0
- data/lib/sunshine/daemons/unicorn.rb +26 -0
- data/lib/sunshine/dependencies.rb +103 -0
- data/lib/sunshine/dependency_lib.rb +200 -0
- data/lib/sunshine/exceptions.rb +54 -0
- data/lib/sunshine/healthcheck.rb +83 -0
- data/lib/sunshine/output.rb +131 -0
- data/lib/sunshine/package_managers/apt.rb +48 -0
- data/lib/sunshine/package_managers/dependency.rb +349 -0
- data/lib/sunshine/package_managers/gem.rb +54 -0
- data/lib/sunshine/package_managers/yum.rb +62 -0
- data/lib/sunshine/remote_shell.rb +241 -0
- data/lib/sunshine/repo.rb +128 -0
- data/lib/sunshine/repos/git_repo.rb +122 -0
- data/lib/sunshine/repos/rsync_repo.rb +29 -0
- data/lib/sunshine/repos/svn_repo.rb +78 -0
- data/lib/sunshine/server_app.rb +554 -0
- data/lib/sunshine/shell.rb +384 -0
- data/lib/sunshine.rb +391 -0
- data/templates/logrotate/logrotate.conf.erb +11 -0
- data/templates/nginx/nginx.conf.erb +109 -0
- data/templates/nginx/nginx_optimize.conf +23 -0
- data/templates/nginx/nginx_proxy.conf +13 -0
- data/templates/rainbows/rainbows.conf.erb +18 -0
- data/templates/tasks/sunshine.rake +114 -0
- data/templates/unicorn/unicorn.conf.erb +6 -0
- data/test/fixtures/app_configs/test_app.yml +11 -0
- data/test/fixtures/sunshine_test/test_upload +0 -0
- data/test/mocks/mock_object.rb +179 -0
- data/test/mocks/mock_open4.rb +117 -0
- data/test/test_helper.rb +188 -0
- data/test/unit/test_app.rb +489 -0
- data/test/unit/test_binder.rb +20 -0
- data/test/unit/test_crontab.rb +128 -0
- data/test/unit/test_git_repo.rb +26 -0
- data/test/unit/test_healthcheck.rb +70 -0
- data/test/unit/test_nginx.rb +107 -0
- data/test/unit/test_rainbows.rb +26 -0
- data/test/unit/test_remote_shell.rb +102 -0
- data/test/unit/test_repo.rb +42 -0
- data/test/unit/test_server.rb +324 -0
- data/test/unit/test_server_app.rb +425 -0
- data/test/unit/test_shell.rb +97 -0
- data/test/unit/test_sunshine.rb +157 -0
- data/test/unit/test_svn_repo.rb +55 -0
- data/test/unit/test_unicorn.rb +22 -0
- metadata +217 -0
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'sunshine'
|
2
|
+
|
3
|
+
namespace :sunshine do
|
4
|
+
|
5
|
+
desc "Instantiate Sunshine"
|
6
|
+
task :app do
|
7
|
+
deploy_env = ENV['env'] || ENV['RACK_ENV'] || ENV['RAILS_ENV']
|
8
|
+
deploy_env ||= "development"
|
9
|
+
|
10
|
+
Sunshine.setup 'sudo' => 'app_user',
|
11
|
+
'web_directory' => '/var/www',
|
12
|
+
'deploy_env' => deploy_env
|
13
|
+
|
14
|
+
app_hash = {
|
15
|
+
:repo => {
|
16
|
+
:type => :svn,
|
17
|
+
:url => "svn://subversion/path/to/my_app/tags/release001"
|
18
|
+
},
|
19
|
+
:remote_shells => %w{user@my_server.com}
|
20
|
+
}
|
21
|
+
|
22
|
+
@app = Sunshine::App.new app_hash
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
desc "Deploy the app"
|
27
|
+
task :deploy => :app do
|
28
|
+
Sunshine.setup 'trace' => true
|
29
|
+
|
30
|
+
@app.deploy do |app|
|
31
|
+
|
32
|
+
rainbows = Sunshine::Rainbows.new(app, :port => 5001)
|
33
|
+
|
34
|
+
nginx = Sunshine::Nginx.new(app, :point_to => rainbows, :port => 5000)
|
35
|
+
|
36
|
+
app.run_geminstaller
|
37
|
+
|
38
|
+
rainbows.setup
|
39
|
+
nginx.setup
|
40
|
+
end
|
41
|
+
|
42
|
+
@app.start :force => true
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
# Post-deploy control tasks:
|
47
|
+
|
48
|
+
desc "Run db:migrate on remote :db servers"
|
49
|
+
task :db_migrate => :app do
|
50
|
+
@app.rake 'db:migrate', :role => :db
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
desc "Run the remote start script"
|
55
|
+
task :start => :app do
|
56
|
+
@app.start
|
57
|
+
puts @app.status.to_yaml
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
desc "Run the remote stop script"
|
62
|
+
task :stop => :app do
|
63
|
+
@app.stop
|
64
|
+
puts @app.status.to_yaml
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
desc "Run the remote restart script"
|
69
|
+
task :restart => :app do
|
70
|
+
@app.restart
|
71
|
+
puts @app.status.to_yaml
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
desc "Check if the deployed app is running"
|
76
|
+
task :status => :app do
|
77
|
+
puts @app.status.to_yaml
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
desc "Get deployed app info"
|
82
|
+
task :info => :app do
|
83
|
+
puts @app.deploy_details.to_yaml
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
desc "Get the health state"
|
88
|
+
task :health => :app do
|
89
|
+
puts @app.health.to_yaml
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
namespace :health do
|
94
|
+
|
95
|
+
desc "Turn on health check"
|
96
|
+
task :enable => :app do
|
97
|
+
puts @app.health(:enable).to_yaml
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
desc "Turn off health check"
|
102
|
+
task :disable => :app do
|
103
|
+
puts @app.health(:disable).to_yaml
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
desc "Remove health check"
|
108
|
+
task :remove => :app do
|
109
|
+
puts @app.health(:remove).to_yaml
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'sunshine'
|
2
|
+
|
3
|
+
Sunshine.setup 'sudo' => 'app_user',
|
4
|
+
'trace' => true,
|
5
|
+
'web_directory' => "/var/www"
|
6
|
+
|
7
|
+
##
|
8
|
+
# Deploy!
|
9
|
+
|
10
|
+
Sunshine::App.deploy :name => 'my_app' do |app|
|
11
|
+
|
12
|
+
rainbows = Sunshine::Rainbows.new(app, :port => 5001)
|
13
|
+
|
14
|
+
nginx = Sunshine::Nginx.new(app, :point_to => rainbows, :port => 5000)
|
15
|
+
|
16
|
+
app.run_geminstaller
|
17
|
+
|
18
|
+
rainbows.restart
|
19
|
+
nginx.restart
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
__END__
|
24
|
+
|
25
|
+
:default:
|
26
|
+
:repo:
|
27
|
+
:type: svn
|
28
|
+
:url: svn://subversion/path/to/<%= name %>/tags/release001
|
29
|
+
|
30
|
+
:remote_shells:
|
31
|
+
- <%= deploy_env %>-<%= name %>.my_server.com
|
data/lib/commands/add.rb
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# Registers a path as a sunshine application for control via sunshine.
|
5
|
+
#
|
6
|
+
# Usage: sunshine add app_path [more paths...] [options]
|
7
|
+
#
|
8
|
+
# Arguments:
|
9
|
+
# app_path Path to the application to add.
|
10
|
+
# A name may be assigned to the app by specifying name:path.
|
11
|
+
# By default: name = File.basename app_path
|
12
|
+
#
|
13
|
+
# Options:
|
14
|
+
# -f, --format FORMAT Set the output format (txt, yml, json)
|
15
|
+
# -u, --user USER User to use for remote login. Use with -r.
|
16
|
+
# -r, --remote svr1,svr2 Run on one or more remote servers.
|
17
|
+
# -v, --verbose Run in verbose mode.
|
18
|
+
|
19
|
+
class AddCommand < ListCommand
|
20
|
+
|
21
|
+
##
|
22
|
+
# Takes an array and a hash, runs the command and returns:
|
23
|
+
# true: success
|
24
|
+
# false: failed
|
25
|
+
# exitcode:
|
26
|
+
# code == 0: success
|
27
|
+
# code != 0: failed
|
28
|
+
# and optionally an accompanying message.
|
29
|
+
|
30
|
+
def self.exec names, config
|
31
|
+
apps_hash = parse_app_paths(*names)
|
32
|
+
|
33
|
+
output = exec_each_server config do |shell|
|
34
|
+
server_command = new(shell)
|
35
|
+
results = server_command.add apps_hash
|
36
|
+
|
37
|
+
self.save_list server_command.app_list, shell
|
38
|
+
|
39
|
+
results
|
40
|
+
end
|
41
|
+
|
42
|
+
return output
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
##
|
47
|
+
# Takes an array of app path definitions and returns a hash:
|
48
|
+
# parse_app_paths "myapp:/path/to/app", "/path/to/otherapp"
|
49
|
+
# #=> {'myapp' => '/path/to/app', 'otherapp' => '/path/to/otherapp'}
|
50
|
+
|
51
|
+
def self.parse_app_paths(*app_paths)
|
52
|
+
apps_hash = {}
|
53
|
+
app_paths.each do |path|
|
54
|
+
name, path = path.split(":") if path.include?(":")
|
55
|
+
name ||= File.basename path
|
56
|
+
|
57
|
+
apps_hash[name] = path
|
58
|
+
end
|
59
|
+
apps_hash
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
##
|
64
|
+
# Add a registered app on a given deploy server
|
65
|
+
|
66
|
+
def add apps_hash
|
67
|
+
response_for_each(*apps_hash.keys) do |name|
|
68
|
+
path = apps_hash[name]
|
69
|
+
test_dir = @shell.call("test -d #{path}") rescue false
|
70
|
+
|
71
|
+
raise "'#{path}' is not a directory." unless test_dir
|
72
|
+
|
73
|
+
@app_list[name] = path
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
##
|
79
|
+
# Parses the argv passed to the command
|
80
|
+
|
81
|
+
def self.parse_args argv
|
82
|
+
|
83
|
+
parse_remote_args(argv) do |opt, options|
|
84
|
+
opt.banner = <<-EOF
|
85
|
+
|
86
|
+
Usage: #{opt.program_name} add app_path [more paths...] [options]
|
87
|
+
|
88
|
+
Arguments:
|
89
|
+
app_path Path to the application to add.
|
90
|
+
A name may be assigned to the app by specifying name:app_path.
|
91
|
+
By default: name = File.basename app_path
|
92
|
+
EOF
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
module Sunshine
|
2
|
+
|
3
|
+
##
|
4
|
+
# Default sunshine behavior when no command is passed. Outputs help.
|
5
|
+
|
6
|
+
class DefaultCommand
|
7
|
+
|
8
|
+
##
|
9
|
+
# Takes an array and a hash, runs the command and returns:
|
10
|
+
# true: success
|
11
|
+
# false: failed
|
12
|
+
# exitcode:
|
13
|
+
# code == 0: success
|
14
|
+
# code != 0: failed
|
15
|
+
# and optionally an accompanying message.
|
16
|
+
|
17
|
+
def self.exec argv, config
|
18
|
+
template_rakefile = "#{Sunshine::ROOT}/templates/tasks/sunshine.rake"
|
19
|
+
|
20
|
+
target_rakefile = config['rakefile']
|
21
|
+
|
22
|
+
FileUtils.cp template_rakefile, target_rakefile
|
23
|
+
|
24
|
+
return true, "Copied Sunshine template rakefile to #{target_rakefile}"
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
##
|
29
|
+
# Base option parser constructor used by all commands.
|
30
|
+
|
31
|
+
def self.opt_parser options=nil
|
32
|
+
OptionParser.new do |opt|
|
33
|
+
opt.program_name = File.basename $0
|
34
|
+
opt.version = Sunshine::VERSION
|
35
|
+
opt.release = nil
|
36
|
+
|
37
|
+
yield opt if block_given?
|
38
|
+
|
39
|
+
opt.on('-S', '--sudo [USER]',
|
40
|
+
'Run remote commands using sudo or sudo -u USER.') do |value|
|
41
|
+
options['sudo'] = value || true
|
42
|
+
end if options
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
##
|
48
|
+
# Returns the main sunshine help when no arguments are passed.
|
49
|
+
|
50
|
+
def self.parse_args argv
|
51
|
+
options = {}
|
52
|
+
|
53
|
+
opts = opt_parser do |opt|
|
54
|
+
opt.banner = <<-EOF
|
55
|
+
|
56
|
+
Sunshine is an object oriented deploy tool for rack applications.
|
57
|
+
|
58
|
+
Usage:
|
59
|
+
#{opt.program_name} -h/--help
|
60
|
+
#{opt.program_name} -v/--version
|
61
|
+
#{opt.program_name} command [arguments...] [options...]
|
62
|
+
|
63
|
+
Examples:
|
64
|
+
#{opt.program_name} deploy deploy_script.rb
|
65
|
+
#{opt.program_name} restart myapp -r user@server.com,user@host.com
|
66
|
+
#{opt.program_name} list myapp myotherapp --health -r user@server.com
|
67
|
+
#{opt.program_name} list myapp --status
|
68
|
+
|
69
|
+
Commands:
|
70
|
+
add Register an app with #{opt.program_name}
|
71
|
+
deploy Run a deploy script
|
72
|
+
list Display deployed apps
|
73
|
+
restart Restart a deployed app
|
74
|
+
rm Unregister an app with #{opt.program_name}
|
75
|
+
start Start a deployed app
|
76
|
+
stop Stop a deployed app
|
77
|
+
|
78
|
+
Options:
|
79
|
+
EOF
|
80
|
+
|
81
|
+
opt.on('--rakefile [PATH]',
|
82
|
+
'Copy the Sunshine template rakefile.') do |path|
|
83
|
+
options['rakefile'] = path || File.join(Dir.pwd, "sunshine.rake")
|
84
|
+
end
|
85
|
+
|
86
|
+
opt.separator nil
|
87
|
+
opt.separator "For more help on sunshine commands, "+
|
88
|
+
"use '#{opt.program_name} COMMAND --help'"
|
89
|
+
opt.separator nil
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
opts.parse! argv
|
94
|
+
|
95
|
+
if options.empty?
|
96
|
+
puts opts
|
97
|
+
exit 1
|
98
|
+
end
|
99
|
+
|
100
|
+
options
|
101
|
+
end
|
102
|
+
|
103
|
+
|
104
|
+
##
|
105
|
+
# Parse arguments for a command that acts remotely.
|
106
|
+
|
107
|
+
def self.parse_remote_args argv, &block
|
108
|
+
options = {}
|
109
|
+
|
110
|
+
opts = opt_parser(options) do |opt|
|
111
|
+
opt.separator nil
|
112
|
+
opt.separator "Options:"
|
113
|
+
|
114
|
+
yield(opt, options) if block_given?
|
115
|
+
|
116
|
+
opt.on('-u', '--user USER',
|
117
|
+
'User to use for ssh login. Use with -r.') do |value|
|
118
|
+
options['user'] = value
|
119
|
+
end
|
120
|
+
|
121
|
+
opt.on('-r', '--remote server1,server2', Array,
|
122
|
+
'Run on one or more remote servers.') do |servers|
|
123
|
+
options['servers'] = servers
|
124
|
+
end
|
125
|
+
|
126
|
+
formats = %w{txt yml json}
|
127
|
+
opt.on('-f', '--format FORMAT', formats,
|
128
|
+
"Set the output format (#{formats.join(', ')})") do |format|
|
129
|
+
options['format'] = "#{format}_format".to_sym
|
130
|
+
end
|
131
|
+
|
132
|
+
opt.on('-v', '--verbose',
|
133
|
+
'Run in verbose mode.') do
|
134
|
+
options['verbose'] = true
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
opts.parse! argv
|
139
|
+
|
140
|
+
|
141
|
+
if options['servers']
|
142
|
+
options['servers'].map! do |host|
|
143
|
+
RemoteShell.new host, :user => options['user']
|
144
|
+
end
|
145
|
+
else
|
146
|
+
options['servers'] = [Sunshine.shell]
|
147
|
+
end
|
148
|
+
|
149
|
+
options['format'] ||= :txt_format
|
150
|
+
|
151
|
+
|
152
|
+
if options['sudo']
|
153
|
+
options['servers'].each do |ds|
|
154
|
+
ds.sudo = options['sudo']
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
options
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
##
|
163
|
+
# Build a Sunshine command response
|
164
|
+
|
165
|
+
def self.build_response status, data
|
166
|
+
{'status' => status, 'data' => data}.to_yaml
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|