mamiya 0.0.1.alpha24 → 0.0.1.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +24 -18
  3. data/example/config.agent.rb +3 -0
  4. data/example/config.rb +27 -0
  5. data/example/deploy.rb +19 -11
  6. data/lib/mamiya/agent.rb +38 -2
  7. data/lib/mamiya/agent/actions.rb +4 -0
  8. data/lib/mamiya/agent/tasks/clean.rb +35 -0
  9. data/lib/mamiya/agent/tasks/notifyable.rb +4 -1
  10. data/lib/mamiya/agent/tasks/prepare.rb +2 -0
  11. data/lib/mamiya/agent/tasks/switch.rb +123 -0
  12. data/lib/mamiya/cli.rb +1 -20
  13. data/lib/mamiya/cli/client.rb +225 -3
  14. data/lib/mamiya/configuration.rb +18 -0
  15. data/lib/mamiya/master/agent_monitor.rb +19 -4
  16. data/lib/mamiya/master/agent_monitor_handlers.rb +10 -0
  17. data/lib/mamiya/master/application_status.rb +72 -0
  18. data/lib/mamiya/master/package_status.rb +178 -0
  19. data/lib/mamiya/master/web.rb +48 -44
  20. data/lib/mamiya/steps/build.rb +1 -1
  21. data/lib/mamiya/steps/prepare.rb +1 -1
  22. data/lib/mamiya/steps/switch.rb +2 -1
  23. data/lib/mamiya/storages/filesystem.rb +1 -1
  24. data/lib/mamiya/storages/mock.rb +1 -1
  25. data/lib/mamiya/storages/s3.rb +1 -1
  26. data/lib/mamiya/storages/s3_proxy.rb +1 -1
  27. data/lib/mamiya/version.rb +1 -1
  28. data/spec/agent/actions_spec.rb +26 -1
  29. data/spec/agent/tasks/clean_spec.rb +88 -2
  30. data/spec/agent/tasks/notifyable_spec.rb +3 -3
  31. data/spec/agent/tasks/switch_spec.rb +176 -0
  32. data/spec/agent_spec.rb +142 -1
  33. data/spec/configuration_spec.rb +23 -0
  34. data/spec/master/agent_monitor_spec.rb +128 -38
  35. data/spec/master/application_status_spec.rb +171 -0
  36. data/spec/master/package_status_spec.rb +560 -0
  37. data/spec/master/web_spec.rb +116 -1
  38. data/spec/steps/build_spec.rb +1 -1
  39. data/spec/steps/prepare_spec.rb +6 -1
  40. data/spec/steps/switch_spec.rb +6 -2
  41. data/spec/storages/filesystem_spec.rb +2 -21
  42. data/spec/storages/s3_proxy_spec.rb +2 -22
  43. data/spec/storages/s3_spec.rb +2 -20
  44. metadata +11 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5cff88034ce82da05b6bbef3f14e01c3d64a0106
4
- data.tar.gz: 955b085f2d2ad3911608daf3356569b072c37af1
3
+ metadata.gz: 782cb5f0d2988afede38796bf84f3b37f3343684
4
+ data.tar.gz: 2d4f5b6102d93c5e05cbc3779364466ca171bece
5
5
  SHA512:
6
- metadata.gz: 09ac1bd2fa42fe80d9c7c0c253b387987f1bd65d73036447c1ebe76b3a7b0a35a442652dea295d6c210d8ad8a5ee8311c004822db6adb2315a1824fc1da6551c
7
- data.tar.gz: 231d431e2a6ab44a47843712edff9b9389aa0594b87915ef308ee08b7ffe914ba3abb4007cbe772c85ae9f4ceecdde43ec9f4a129bb7020ff82a3c02e51538f5
6
+ metadata.gz: 74749204a5f0f8071a55889fd1e418f3e4c9c2a721f643f1ece3dca210539b33a50627e5cd6e1d1bf3d7b04c2baf758cc6c886738feb2fc9d4547939fbee6cc7
7
+ data.tar.gz: 6d46ddc42aa6b206640edcf7d96588b56f11271d7cf606b8522d261bfe9ab64a878d4b774f70bb5485379339cb22cc0f62c3f1db27077689027dcaa65f82c8b7
data/README.md CHANGED
@@ -2,37 +2,43 @@
2
2
 
3
3
  [![Build Status](https://travis-ci.org/sorah/mamiya.png?branch=master)](https://travis-ci.org/sorah/mamiya)
4
4
 
5
- Mamiya allows you to deploy using without ssh -- tarballs in such storage (Amazon S3), and [serf](http://www.serfdom.io/).
5
+ Mamiya allows you to deploy using without ssh -- using tarballs, some storages (S3, etc), and [Serf](http://www.serfdom.io/).
6
6
 
7
- _Still developing_
7
+ ## Installation
8
8
 
9
- ## Problem for other deploy tool
9
+ $ gem install mamiya
10
10
 
11
- Existing major deploy tools (capistrano, mina, ...) use SSH to operate servers.
12
- But connecting to lot of servers via SSH makes deployment slow.
11
+ or bundle it:
13
12
 
14
- This solves such problem by using [Serf](http://www.serfdom.io/) and tarball on one storage (Amazon S3).
13
+ ``` ruby
14
+ gem 'mamiya'
15
+ ```
15
16
 
16
- Also, I'm planning to allow to distribute files before the deploy command. I guess this can skip or shorten
17
- file transferring phase in deploy.
18
-
19
- ## Installation
17
+ ## Usage
20
18
 
21
- Add this line to your application's Gemfile:
19
+ (Detailed documentation coming soon)
22
20
 
23
- gem 'mamiya'
21
+ 1. Prepare master node for `mamiya master` process
22
+ - master has HTTP API to allow control the cluster via HTTP
23
+ - master watches agents in the cluster
24
+ 2. Then install `mamiya agent` in your deployment target hosts
25
+ 3. Write deploy script for your own
26
+ - how to build package, how to prepare release, how to release, etc.
27
+ 4. Build then push package
28
+ 5. Time to deploy
24
29
 
25
- And then execute:
30
+ [example](./example) directory contains configuration files that work out-of-the-box. Try Mamiya in your local machine: `foreman start`
26
31
 
27
- $ bundle
32
+ ## Problems in existing deploy tool
28
33
 
29
- Or install it yourself as:
34
+ Existing major deploy tools (capistrano, mina, ...) use SSH to operate servers.
35
+ But connecting to lot of servers via SSH makes deployment slow.
30
36
 
31
- $ gem install mamiya
37
+ This solves such problem by using [Serf](http://www.serfdom.io/) and tarball on one storage (Amazon S3).
32
38
 
33
- ## Usage
39
+ Also, I'm planning to allow to distribute files before the deploy command. I guess this can skip or shorten
40
+ file transferring phase in deploy.
34
41
 
35
- TODO: Write usage instructions here
36
42
 
37
43
  ## Upgrade Notes
38
44
 
@@ -31,9 +31,12 @@ target = targets.join(agent_name).tap(&:mkpath)
31
31
  set :packages_dir, target.join('packages')
32
32
  set :prereleases_dir, target.join('prereleases')
33
33
 
34
+ applications[:myapp] = {deploy_to: target}
35
+
34
36
  # To test
35
37
  Dir.mkdir packages_dir unless File.exist?(packages_dir)
36
38
  Dir.mkdir prereleases_dir unless File.exist?(prereleases_dir)
39
+ deploy_to_for(:myapp).mkpath unless deploy_to_for(:myapp).exist?
37
40
 
38
41
  set :keep_packages, 3
39
42
  set :keep_prereleases, 3
data/example/config.rb CHANGED
@@ -14,10 +14,37 @@ end
14
14
  set :packages_dir, "#{File.dirname(__FILE__)}/packages"
15
15
  set :prereleases_dir, "#{File.dirname(__FILE__)}/target/prereleases"
16
16
 
17
+ applications[:myapp] = {deploy_to: File.join(File.dirname(__FILE__), 'targets', 'default')}
18
+
17
19
  # To test
18
20
  Dir.mkdir packages_dir unless File.exist?(packages_dir)
19
21
  prereleases_dir.mkpath unless File.exist?(prereleases_dir)
22
+ deploy_to_for(:myapp).mkpath unless deploy_to_for(:myapp).exist?
20
23
 
21
24
  set :keep_packages, 3
22
25
  set :keep_prereleases, 3
23
26
  set :fetch_sleep, 2
27
+
28
+ before_deploy do
29
+ p :before_deploy
30
+ end
31
+
32
+ after_deploy do |e|
33
+ p [:after_deploy, e]
34
+ end
35
+
36
+ before_rollback do
37
+ p :before_rollback
38
+ end
39
+
40
+ after_rollback do |e|
41
+ p [:after_rollback, e]
42
+ end
43
+
44
+ before_deploy_or_rollback do
45
+ p :before_deploy_or_rollback
46
+ end
47
+
48
+ after_deploy_or_rollback do |e|
49
+ p [:after_deploy_or_rollback, e]
50
+ end
data/example/deploy.rb CHANGED
@@ -13,20 +13,11 @@ set :dereference_symlinks, true
13
13
  set :build_from, "#{File.dirname(__FILE__)}/source"
14
14
  set :build_to, "#{File.dirname(__FILE__)}/builds"
15
15
 
16
- dir = File.dirname(__FILE__)
17
- if File.basename(dir) == 'example'
18
- set :deploy_to, File.join(dir, 'targets', 'default')
19
- else
20
- # target/{releases,prereleases}/<name>/.mamiya.script/../../..
21
- set :deploy_to, File.expand_path(File.join(dir, '..', '..', '..'))
22
- end
23
-
24
16
  # to test it
25
17
  Dir.mkdir build_to unless File.exist?(build_to)
26
- deploy_to.mkpath unless deploy_to.exist?
27
18
 
28
- set :bundle_without, [:development, :test]
29
- set :bundle_dir, "#{deploy_to}/shared/bundle"
19
+ #set :bundle_without, [:development, :test]
20
+ #set :bundle_dir, "#{deploy_to}/shared/bundle"
30
21
 
31
22
  #use :git, exclude_git_clean_targets: true
32
23
 
@@ -47,4 +38,21 @@ end
47
38
  # run "bundle", "exec", "rake", "assets:precompile"
48
39
  #end
49
40
 
41
+ prepare 'test' do
42
+ # run 'bundle', 'install'
43
+ logger.info "- prep/deploy_to: #{deploy_to}"
44
+ logger.info "- prep/release_path: #{release_path}"
45
+ sleep 5
46
+ File.write release_path.join('prepared_at'), Time.now
47
+ end
48
+
49
+ release 'test' do
50
+ logger.info "- prep/deploy_to: #{deploy_to}"
51
+ logger.info "- prep/release_path: #{release_path}"
52
+ sl = rand(80)
53
+ logger.info "!!!!!!!!!!!!!!! sleep #{sl} sec"
54
+ sleep sl
55
+ File.write release_path.join('released_at'), Time.now
56
+ end
57
+
50
58
 
data/lib/mamiya/agent.rb CHANGED
@@ -14,6 +14,7 @@ require 'mamiya/agent/task_queue'
14
14
  require 'mamiya/agent/tasks/fetch'
15
15
  require 'mamiya/agent/tasks/prepare'
16
16
  require 'mamiya/agent/tasks/clean'
17
+ require 'mamiya/agent/tasks/switch'
17
18
 
18
19
  require 'mamiya/agent/handlers/task'
19
20
  require 'mamiya/agent/actions'
@@ -42,6 +43,7 @@ module Mamiya
42
43
  Mamiya::Agent::Tasks::Fetch,
43
44
  Mamiya::Agent::Tasks::Prepare,
44
45
  Mamiya::Agent::Tasks::Clean,
46
+ Mamiya::Agent::Tasks::Switch,
45
47
  ])
46
48
  end
47
49
 
@@ -89,6 +91,8 @@ module Mamiya
89
91
  if packages
90
92
  s[:packages] = self.existing_packages
91
93
  s[:prereleases] = self.existing_prereleases
94
+ s[:releases] = self.releases
95
+ s[:currents] = self.currents
92
96
  end
93
97
  end
94
98
  end
@@ -147,6 +151,35 @@ module Mamiya
147
151
  ]
148
152
  end
149
153
 
154
+ def releases
155
+ Hash[config.applications.map do |name, app|
156
+ deploy_to = Pathname.new(app[:deploy_to])
157
+ releases = deploy_to.join('releases')
158
+ next [name, []] unless releases.exist?
159
+
160
+ [
161
+ name,
162
+ releases.children.map do |release|
163
+ release.basename.to_s
164
+ end.sort
165
+ ]
166
+ end.compact]
167
+ end
168
+
169
+ def currents
170
+ # TODO: when the target is in outside?
171
+ Hash[config.applications.map do |name, app|
172
+ deploy_to = Pathname.new(app[:deploy_to])
173
+ current = deploy_to.join('current')
174
+ next unless current.exist?
175
+
176
+ [
177
+ name,
178
+ current.realpath.basename.to_s
179
+ ]
180
+ end.compact]
181
+ end
182
+
150
183
  def trigger(type, action: nil, coalesce: true, **payload)
151
184
  name = "mamiya:#{type}"
152
185
  name << ":#{action}" if action
@@ -173,8 +206,11 @@ module Mamiya
173
206
  end
174
207
 
175
208
  serf.respond('mamiya:packages') do |event|
176
- {'packages' => self.existing_packages,
177
- 'prereleases' => self.existing_prereleases,
209
+ {
210
+ 'packages' => self.existing_packages,
211
+ 'prereleases' => self.existing_prereleases,
212
+ 'releases' => self.releases,
213
+ 'currents' => self.currents,
178
214
  }.to_json
179
215
  end
180
216
  end
@@ -17,6 +17,10 @@ module Mamiya
17
17
  def prepare(application, package, labels: nil)
18
18
  order_task('prepare', app: application, pkg: package, labels: labels)
19
19
  end
20
+
21
+ def switch(application, package, labels: nil, no_release: false)
22
+ order_task('switch', app: application, pkg: package, labels: labels, no_release: no_release)
23
+ end
20
24
  end
21
25
  end
22
26
  end
@@ -9,8 +9,11 @@ module Mamiya
9
9
  # XXX:
10
10
  clean_packages
11
11
  clean_prereleases
12
+ clean_releases
12
13
  end
13
14
 
15
+ ###
16
+
14
17
  def clean_packages
15
18
  package_victims.each do |app, victim|
16
19
  @logger.info "Cleaning package: remove #{victim}"
@@ -45,6 +48,8 @@ module Mamiya
45
48
  end
46
49
  end
47
50
 
51
+ ###
52
+
48
53
  def clean_prereleases
49
54
  prerelease_victims.each do |app, victim|
50
55
  @logger.info "Cleaning prerelease: remove #{victim}"
@@ -70,6 +75,36 @@ module Mamiya
70
75
  end
71
76
  end
72
77
 
78
+ ###
79
+
80
+ def clean_releases
81
+ release_victims.each do |app, victim|
82
+ @logger.info "Cleaning release: remove #{victim}"
83
+ package_name = File.basename(victim)
84
+ FileUtils.remove_entry_secure victim
85
+
86
+ agent.trigger('release', action: 'remove',
87
+ app: app,
88
+ pkg: package_name,
89
+ coalesce: false,
90
+ )
91
+ end
92
+ end
93
+
94
+ def release_victims
95
+ config.applications.flat_map do |name, app|
96
+ next unless app[:deploy_to]
97
+ releases = Dir[File.join(app[:deploy_to], 'releases', '*')].sort_by { |_| [File.mtime(_), _] }
98
+ current = File.join(app[:deploy_to], 'current')
99
+ current_realpath = File.exist?(current) ? File.realpath(current) : nil
100
+
101
+ releases.reject!{ |rel| File.realpath(rel) == current_realpath }
102
+
103
+ releases[0...-(config[:keep_releases])].map do |victim|
104
+ [name, victim]
105
+ end
106
+ end
107
+ end
73
108
  end
74
109
  end
75
110
  end
@@ -6,7 +6,8 @@ module Mamiya
6
6
  class Notifyable < Abstract
7
7
  def execute
8
8
  agent.trigger('task', action: 'start',
9
- task: task
9
+ task: task,
10
+ coalesce: false,
10
11
  )
11
12
 
12
13
  super
@@ -16,10 +17,12 @@ module Mamiya
16
17
  agent.trigger('task', action: 'error',
17
18
  error: error.class.name,
18
19
  task: task,
20
+ coalesce: false,
19
21
  )
20
22
  else
21
23
  agent.trigger('task', action: 'finish',
22
24
  task: task,
25
+ coalesce: false,
23
26
  )
24
27
  end
25
28
  end
@@ -41,6 +41,8 @@ module Mamiya
41
41
 
42
42
  extract_step.run!
43
43
  prepare_step.run!
44
+
45
+ task_queue.enqueue(:clean, {})
44
46
  end
45
47
 
46
48
  private
@@ -0,0 +1,123 @@
1
+ require 'fileutils'
2
+
3
+ require 'mamiya/agent/tasks/notifyable'
4
+ require 'mamiya/steps/switch'
5
+
6
+ module Mamiya
7
+ class Agent
8
+ module Tasks
9
+ class Switch < Notifyable
10
+ class PrereleaseMissing < Exception; end
11
+
12
+ def execute
13
+ return unless check
14
+ super
15
+ end
16
+
17
+ def check
18
+ return true if prerelease_path.exist?
19
+ return true if release_prepared?
20
+
21
+ unless package_path.exist?
22
+ new_chain = ['prepare', 'switch'] + (task['_chain'] || [])
23
+ logger.info "Package not fetched, enqueueing fetch task with #{new_chain.inspect}"
24
+ task_queue.enqueue(
25
+ :fetch,
26
+ task.merge('_chain' => new_chain)
27
+ )
28
+ return false
29
+ end
30
+
31
+ unless prerelease_path.exist?
32
+ new_chain = ['switch'] + (task['_chain'] || [])
33
+ logger.info "Package not prepared, enqueueing prepare task with #{new_chain.inspect}"
34
+ task_queue.enqueue(
35
+ :prepare,
36
+ task.merge('_chain' => new_chain)
37
+ )
38
+ return false
39
+ end
40
+
41
+ true
42
+ end
43
+
44
+ def run
45
+ case
46
+ when prerelease_path.exist? && release_path.exist? && !release_path.join('.mamiya.prepared').exist?
47
+ logger.info "Removing existing release (not prepared)"
48
+ FileUtils.remove_entry_secure release_path
49
+ when !prerelease_path.exist? && prelease_path.exist? && !release_path.join('.mamiya.prepared').exist?
50
+ # this condition may be a bug
51
+ logger.error "Existing release is not prepared but prerelease doesn't exist"
52
+ raise PrereleaseMissing, "Existing release is not prepared but prerelease doesn't exist"
53
+ end
54
+
55
+ logger.info "Copying #{prerelease_path} -> #{release_path}"
56
+ FileUtils.cp_r prerelease_path, release_path
57
+
58
+ logger.info "Switching"
59
+ switch_step.run!
60
+
61
+ task_queue.enqueue(:clean, {})
62
+ end
63
+
64
+ private
65
+
66
+ def application
67
+ task['app']
68
+ end
69
+
70
+ def package
71
+ task['pkg']
72
+ end
73
+
74
+ def release_name
75
+ task['release'] || package
76
+ end
77
+
78
+ def packages_dir
79
+ @packages_dir ||= config.packages_dir
80
+ end
81
+
82
+ def prereleases_dir
83
+ @prereleases_dir ||= config.prereleases_dir
84
+ end
85
+
86
+ def package_path
87
+ packages_dir.join(application, "#{package}.tar.gz")
88
+ end
89
+
90
+ def prerelease_path
91
+ prereleases_dir.join(application, release_name)
92
+ end
93
+
94
+ def releases_dir
95
+ config.deploy_to_for(application).join('releases').tap(&:mkpath)
96
+ end
97
+
98
+ def release_path
99
+ releases_dir.join(release_name)
100
+ end
101
+
102
+ def release_prepared?
103
+ release_path.exist? && release_path.join('.mamiya.prepared').exist?
104
+ end
105
+
106
+ def labels
107
+ @labels ||= agent.labels
108
+ end
109
+
110
+ def switch_step
111
+ @switch_step ||= Mamiya::Steps::Switch.new(
112
+ target: release_path,
113
+ config: config,
114
+ logger: logger,
115
+ config: config,
116
+ labels: agent.labels,
117
+ no_release: !!task['no_release']
118
+ )
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end