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.
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