sunshine 1.0.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|