sunshine 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (71) hide show
  1. data/History.txt +237 -0
  2. data/Manifest.txt +70 -0
  3. data/README.txt +277 -0
  4. data/Rakefile +46 -0
  5. data/bin/sunshine +5 -0
  6. data/examples/deploy.rb +61 -0
  7. data/examples/deploy_tasks.rake +112 -0
  8. data/examples/standalone_deploy.rb +31 -0
  9. data/lib/commands/add.rb +96 -0
  10. data/lib/commands/default.rb +169 -0
  11. data/lib/commands/list.rb +322 -0
  12. data/lib/commands/restart.rb +62 -0
  13. data/lib/commands/rm.rb +83 -0
  14. data/lib/commands/run.rb +151 -0
  15. data/lib/commands/start.rb +72 -0
  16. data/lib/commands/stop.rb +61 -0
  17. data/lib/sunshine/app.rb +876 -0
  18. data/lib/sunshine/binder.rb +70 -0
  19. data/lib/sunshine/crontab.rb +143 -0
  20. data/lib/sunshine/daemon.rb +380 -0
  21. data/lib/sunshine/daemons/ar_sendmail.rb +28 -0
  22. data/lib/sunshine/daemons/delayed_job.rb +30 -0
  23. data/lib/sunshine/daemons/nginx.rb +104 -0
  24. data/lib/sunshine/daemons/rainbows.rb +35 -0
  25. data/lib/sunshine/daemons/server.rb +66 -0
  26. data/lib/sunshine/daemons/unicorn.rb +26 -0
  27. data/lib/sunshine/dependencies.rb +103 -0
  28. data/lib/sunshine/dependency_lib.rb +200 -0
  29. data/lib/sunshine/exceptions.rb +54 -0
  30. data/lib/sunshine/healthcheck.rb +83 -0
  31. data/lib/sunshine/output.rb +131 -0
  32. data/lib/sunshine/package_managers/apt.rb +48 -0
  33. data/lib/sunshine/package_managers/dependency.rb +349 -0
  34. data/lib/sunshine/package_managers/gem.rb +54 -0
  35. data/lib/sunshine/package_managers/yum.rb +62 -0
  36. data/lib/sunshine/remote_shell.rb +241 -0
  37. data/lib/sunshine/repo.rb +128 -0
  38. data/lib/sunshine/repos/git_repo.rb +122 -0
  39. data/lib/sunshine/repos/rsync_repo.rb +29 -0
  40. data/lib/sunshine/repos/svn_repo.rb +78 -0
  41. data/lib/sunshine/server_app.rb +554 -0
  42. data/lib/sunshine/shell.rb +384 -0
  43. data/lib/sunshine.rb +391 -0
  44. data/templates/logrotate/logrotate.conf.erb +11 -0
  45. data/templates/nginx/nginx.conf.erb +109 -0
  46. data/templates/nginx/nginx_optimize.conf +23 -0
  47. data/templates/nginx/nginx_proxy.conf +13 -0
  48. data/templates/rainbows/rainbows.conf.erb +18 -0
  49. data/templates/tasks/sunshine.rake +114 -0
  50. data/templates/unicorn/unicorn.conf.erb +6 -0
  51. data/test/fixtures/app_configs/test_app.yml +11 -0
  52. data/test/fixtures/sunshine_test/test_upload +0 -0
  53. data/test/mocks/mock_object.rb +179 -0
  54. data/test/mocks/mock_open4.rb +117 -0
  55. data/test/test_helper.rb +188 -0
  56. data/test/unit/test_app.rb +489 -0
  57. data/test/unit/test_binder.rb +20 -0
  58. data/test/unit/test_crontab.rb +128 -0
  59. data/test/unit/test_git_repo.rb +26 -0
  60. data/test/unit/test_healthcheck.rb +70 -0
  61. data/test/unit/test_nginx.rb +107 -0
  62. data/test/unit/test_rainbows.rb +26 -0
  63. data/test/unit/test_remote_shell.rb +102 -0
  64. data/test/unit/test_repo.rb +42 -0
  65. data/test/unit/test_server.rb +324 -0
  66. data/test/unit/test_server_app.rb +425 -0
  67. data/test/unit/test_shell.rb +97 -0
  68. data/test/unit/test_sunshine.rb +157 -0
  69. data/test/unit/test_svn_repo.rb +55 -0
  70. data/test/unit/test_unicorn.rb +22 -0
  71. 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
@@ -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