mamiya 0.0.1.alpha21 → 0.0.1.alpha22

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/example/.gitignore +5 -0
  3. data/example/Procfile +1 -1
  4. data/example/README.md +83 -0
  5. data/example/config.agent.rb +40 -0
  6. data/example/config.rb +20 -6
  7. data/example/deploy.rb +27 -11
  8. data/example/source/README.md +1 -0
  9. data/lib/mamiya/agent/actions.rb +8 -3
  10. data/lib/mamiya/agent/task_queue.rb +9 -0
  11. data/lib/mamiya/agent/tasks/abstract.rb +13 -0
  12. data/lib/mamiya/agent/tasks/clean.rb +36 -4
  13. data/lib/mamiya/agent/tasks/fetch.rb +1 -0
  14. data/lib/mamiya/agent/tasks/notifyable.rb +0 -1
  15. data/lib/mamiya/agent/tasks/prepare.rb +103 -0
  16. data/lib/mamiya/agent.rb +46 -12
  17. data/lib/mamiya/cli/client.rb +35 -7
  18. data/lib/mamiya/cli.rb +44 -5
  19. data/lib/mamiya/configuration.rb +12 -0
  20. data/lib/mamiya/dsl.rb +6 -2
  21. data/lib/mamiya/helpers/git.rb +24 -0
  22. data/lib/mamiya/master/agent_monitor.rb +22 -2
  23. data/lib/mamiya/master/agent_monitor_handlers.rb +17 -0
  24. data/lib/mamiya/master/web.rb +42 -3
  25. data/lib/mamiya/master.rb +4 -0
  26. data/lib/mamiya/script.rb +28 -8
  27. data/lib/mamiya/steps/abstract.rb +1 -0
  28. data/lib/mamiya/steps/build.rb +107 -19
  29. data/lib/mamiya/steps/extract.rb +1 -0
  30. data/lib/mamiya/steps/prepare.rb +60 -0
  31. data/lib/mamiya/steps/switch.rb +76 -0
  32. data/lib/mamiya/storages/filesystem.rb +92 -0
  33. data/lib/mamiya/storages/mock.rb +1 -0
  34. data/lib/mamiya/util/label_matcher.rb +7 -3
  35. data/lib/mamiya/version.rb +1 -1
  36. data/mamiya.gemspec +1 -1
  37. data/spec/agent/actions_spec.rb +25 -0
  38. data/spec/agent/task_queue_spec.rb +42 -6
  39. data/spec/agent/tasks/abstract_spec.rb +35 -0
  40. data/spec/agent/tasks/clean_spec.rb +94 -45
  41. data/spec/agent/tasks/fetch_spec.rb +1 -0
  42. data/spec/agent/tasks/prepare_spec.rb +127 -0
  43. data/spec/agent_spec.rb +75 -27
  44. data/spec/dsl_spec.rb +6 -8
  45. data/spec/master/agent_monitor_spec.rb +142 -4
  46. data/spec/master/web_spec.rb +43 -1
  47. data/spec/steps/build_spec.rb +101 -0
  48. data/spec/steps/prepare_spec.rb +125 -0
  49. data/spec/steps/switch_spec.rb +146 -0
  50. data/spec/storages/filesystem_spec.rb +305 -0
  51. data/spec/util/label_matcher_spec.rb +32 -0
  52. metadata +20 -6
  53. data/config.example.yml +0 -11
  54. data/example.rb +0 -74
@@ -112,13 +112,25 @@ describe Mamiya::Master::Web do
112
112
 
113
113
  describe "POST /packages/:application/:package/distribute" do
114
114
  it "dispatchs distribute request" do
115
- expect(master).to receive(:distribute).with('myapp', 'mypackage')
115
+ expect(master).to receive(:distribute).with('myapp', 'mypackage', labels: nil)
116
116
 
117
117
  post '/packages/myapp/mypackage/distribute'
118
118
 
119
119
  expect(last_response.status).to eq 204 # no content
120
120
  end
121
121
 
122
+ context "with labels" do
123
+ it "dispatchs prepare request with labels" do
124
+ expect(master).to receive(:distribute).with('myapp', 'mypackage', labels: ['foo'])
125
+
126
+ post '/packages/myapp/mypackage/distribute',
127
+ {'labels' => ["foo"]}.to_json,
128
+ 'CONTENT_TYPE' => 'application/json'
129
+
130
+ expect(last_response.status).to eq 204 # no content
131
+ end
132
+ end
133
+
122
134
  context "when package not found" do
123
135
  it "returns 404" do
124
136
  post '/packages/myapp/noexist/distribute'
@@ -127,6 +139,36 @@ describe Mamiya::Master::Web do
127
139
  end
128
140
  end
129
141
 
142
+ describe "POST /packages/:application/:package/prepare" do
143
+ it "dispatchs prepare request" do
144
+ expect(master).to receive(:prepare).with('myapp', 'mypackage', labels: nil)
145
+
146
+ post '/packages/myapp/mypackage/prepare'
147
+
148
+ expect(last_response.status).to eq 204 # no content
149
+ end
150
+
151
+ context "with labels" do
152
+ it "dispatchs prepare request with labels" do
153
+ expect(master).to receive(:prepare).with('myapp', 'mypackage', labels: ['foo'])
154
+
155
+ post '/packages/myapp/mypackage/prepare',
156
+ {'labels' => ["foo"]}.to_json,
157
+ 'CONTENT_TYPE' => 'application/json'
158
+
159
+ expect(last_response.status).to eq 204 # no content
160
+ end
161
+ end
162
+
163
+ context "when package not found" do
164
+ it "returns 404" do
165
+ post '/packages/myapp/noexist/prepare'
166
+ expect(last_response.status).to eq 404
167
+ end
168
+ end
169
+ end
170
+
171
+
130
172
  describe "GET /package/:application/:package/distribution" do
131
173
  subject(:distribution) do
132
174
  res = get('/packages/myapp/mypackage/distribution')
@@ -14,6 +14,9 @@ describe Mamiya::Steps::Build do
14
14
  let(:build_dir) { Pathname.new(tmpdir).join('build') }
15
15
  let(:package_dir) { Pathname.new(tmpdir).join('pkg') }
16
16
  let(:extract_dir) { Pathname.new(tmpdir).join('extract') }
17
+ let(:script_dir) { Pathname.new(tmpdir).join('script').tap(&:mkdir) }
18
+
19
+ let(:script_file) { script_dir.join('deploy.rb').tap { |_| File.write _, "p :script\n" } }
17
20
 
18
21
  let(:exclude_from_package) { [] }
19
22
  let(:package_under) { nil }
@@ -36,6 +39,9 @@ describe Mamiya::Steps::Build do
36
39
  dereference_symlinks: dereference_symlinks,
37
40
  exclude_from_package: exclude_from_package,
38
41
  skip_prepare_build: skip_prepare_build,
42
+ script_file: script_file,
43
+ script_additionals: [],
44
+ _file: nil,
39
45
  )
40
46
  end
41
47
 
@@ -97,6 +103,7 @@ describe Mamiya::Steps::Build do
97
103
  allow(script).to receive(:exclude_from_package).and_return(['test'])
98
104
  allow(script).to receive(:dereference_symlinks).and_return(true)
99
105
  allow(script).to receive(:package_under).and_return('foo')
106
+ build_dir.join('foo').mkdir
100
107
 
101
108
  expect_any_instance_of(Mamiya::Package).to \
102
109
  receive(:build!).with(
@@ -123,6 +130,100 @@ describe Mamiya::Steps::Build do
123
130
  build_step.run!
124
131
  end
125
132
 
133
+ it "packs script_file into .mamiya.script" do
134
+ expect_any_instance_of(Mamiya::Package).to receive(:build!) do
135
+ expect(build_dir.join('.mamiya.script')).to be_a_directory
136
+ expect(build_dir.join('.mamiya.script', 'deploy.rb').read).to match(/:script/)
137
+ end
138
+
139
+ build_step.run!
140
+
141
+ expect(build_dir.join('.mamiya.script')).not_to be_exist
142
+ end
143
+
144
+ it "writes script_file in meta" do
145
+ meta = {}
146
+ allow_any_instance_of(Mamiya::Package).to receive(:meta).and_return(meta)
147
+ expect_any_instance_of(Mamiya::Package).to receive(:build!) do
148
+ expect(meta[:script]).to eq 'deploy.rb'
149
+ end
150
+
151
+ build_step.run!
152
+
153
+ expect(build_dir.join('.mamiya.script')).not_to be_exist
154
+ end
155
+
156
+ context "when package_under is specified" do
157
+ let(:package_under) { 'hoge' }
158
+
159
+ before do
160
+ build_dir.join('hoge').mkdir
161
+ File.write build_dir.join('hoge', 'test'), "hello\n"
162
+ end
163
+
164
+ it "places .mamiya.script under :package_under" do
165
+ expect_any_instance_of(Mamiya::Package).to receive(:build!) do
166
+ expect(build_dir.join('hoge', '.mamiya.script')).to be_a_directory
167
+ expect(build_dir.join('hoge', '.mamiya.script', 'deploy.rb').read).to match(/:script/)
168
+ end
169
+
170
+ build_step.run!
171
+
172
+ expect(build_dir.join('.mamiya.script')).not_to be_exist
173
+ end
174
+ end
175
+
176
+ context "with :script_additionals" do
177
+ before do
178
+ allow(script).to receive(:script_additionals).and_return(%w(test))
179
+ end
180
+
181
+ it "packs them into .mamiya.script" do
182
+ File.write script_dir.join('test'), "this is test\n"
183
+
184
+ expect_any_instance_of(Mamiya::Package).to receive(:build!) do
185
+ expect(build_dir.join('.mamiya.script')).to be_a_directory
186
+ expect(build_dir.join('.mamiya.script', 'deploy.rb').read).to match(/:script/)
187
+ expect(build_dir.join('.mamiya.script', 'test').read).to match(/this is test/)
188
+ end
189
+
190
+ build_step.run!
191
+
192
+ expect(build_dir.join('.mamiya.script')).not_to be_exist
193
+ end
194
+ end
195
+
196
+ context "without script_file and when script is loaded from a file" do
197
+ before do
198
+ allow(script).to receive(:_file).and_return(script_file)
199
+ allow(script).to receive(:script_file).and_return(nil)
200
+ end
201
+
202
+ it "assumes loaded file's directory as script_dir and loaded file as script_file" do
203
+ expect_any_instance_of(Mamiya::Package).to receive(:build!) do
204
+ expect(build_dir.join('.mamiya.script')).to be_a_directory
205
+ expect(build_dir.join('.mamiya.script', 'deploy.rb').read).to match(/:script/)
206
+ end
207
+
208
+ build_step.run!
209
+
210
+ expect(build_dir.join('.mamiya.script')).not_to be_exist
211
+ end
212
+ end
213
+
214
+ context "without script_file and when script is not loaded from a file" do
215
+ before do
216
+ allow(script).to receive(:_file).and_return(nil)
217
+ allow(script).to receive(:script_file).and_return(nil)
218
+ end
219
+
220
+ it "raises an error" do
221
+ expect {
222
+ build_step.run!
223
+ }.to raise_error Mamiya::Steps::Build::ScriptFileNotSpecified
224
+ end
225
+ end
226
+
126
227
  context "with package name determiner" do
127
228
  it "calls package name determiner with current candidate" do
128
229
  received = nil
@@ -0,0 +1,125 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+ require 'pathname'
4
+ require 'fileutils'
5
+ require 'json'
6
+
7
+ require 'mamiya/script'
8
+
9
+ require 'mamiya/package'
10
+ require 'mamiya/steps/prepare'
11
+
12
+ describe Mamiya::Steps::Prepare do
13
+ let!(:tmpdir) { Dir.mktmpdir("mamiya-steps-prepare-spec") }
14
+ after { FileUtils.remove_entry_secure tmpdir }
15
+
16
+ let(:target_dir) { Pathname.new(tmpdir).join('release').tap(&:mkdir) }
17
+
18
+ let(:target_meta) do
19
+ {
20
+ script: 'script.rb',
21
+ }
22
+ end
23
+
24
+ let(:script) do
25
+ double('script',
26
+ before_prepare: proc {},
27
+ prepare: proc {},
28
+ after_prepare: proc {},
29
+ )
30
+ end
31
+
32
+ let(:config) do
33
+ double('config')
34
+ end
35
+
36
+ let(:options) do
37
+ {target: target_dir, labels: [:foo, :bar]}
38
+ end
39
+
40
+ subject(:step) { described_class.new(script: nil, config: config, **options) }
41
+
42
+ before do
43
+ File.write target_dir.join('.mamiya.meta.json'), target_meta.to_json
44
+ allow(Mamiya::Script).to receive(:new).and_return(script)
45
+ allow(script).to receive(:load!).with(target_dir.realpath.join('.mamiya.script', 'script.rb')).and_return(script)
46
+ allow(script).to receive(:set).and_return(script)
47
+ end
48
+
49
+ describe "#run!" do
50
+ it "calls hooks with proper order" do
51
+ hooks = %i(before_prepare prepare after_prepare)
52
+
53
+ flags = []
54
+ hooks.each do |sym|
55
+ allow(script).to receive(sym).and_return(proc { flags << sym })
56
+ end
57
+
58
+ expect { step.run! }.
59
+ to change { flags }.
60
+ from([]).
61
+ to(hooks)
62
+ end
63
+
64
+ context "if exception occured" do
65
+ let(:error) { Exception.new("Good bye, the cruel world") }
66
+ before do
67
+ e = error
68
+ allow(script).to receive(:prepare).and_return(proc { raise e })
69
+ end
70
+
71
+ it "calls after hook with error" do
72
+ received = nil
73
+ allow(script).to receive(:after_prepare).and_return(proc { |_| received = _ })
74
+
75
+ expect {
76
+ begin
77
+ step.run!
78
+ rescue Exception; end
79
+ }.
80
+ to change { received }.
81
+ from(nil).to(error)
82
+ end
83
+
84
+ it "does not create .mamiya.prepared" do
85
+ expect {
86
+ step.run!
87
+ }.to raise_error(error)
88
+ expect(target_dir.join('.mamiya.prepared')).not_to be_exist
89
+ end
90
+ end
91
+
92
+ it "creates .mamiya.prepared" do
93
+ expect {
94
+ step.run!
95
+ }.to change {
96
+ target_dir.join('.mamiya.prepared').exist?
97
+ }.from(false).to(true)
98
+ end
99
+
100
+ it "calls hook in :target (pwd)" do
101
+ pwd = nil
102
+ script.stub(prepare: proc { pwd = Dir.pwd })
103
+
104
+ expect {
105
+ step.run!
106
+ }.not_to change { Dir.pwd }
107
+
108
+ expect(File.realpath(pwd)).to eq target_dir.realpath.to_s
109
+ end
110
+
111
+ it "sets release_path" do
112
+ expect(script).to receive(:set).with(:release_path, target_dir.realpath)
113
+ expect(script).to receive(:set).with(:logger, step.logger)
114
+ step.run!
115
+ end
116
+
117
+ it "calls hooks using labels" do
118
+ allow(script).to receive(:before_prepare).with(%i(foo bar)).and_return(proc {})
119
+ allow(script).to receive(:prepare).with(%i(foo bar)).and_return(proc {})
120
+ allow(script).to receive(:after_prepare).with(%i(foo bar)).and_return(proc {})
121
+
122
+ step.run!
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,146 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+ require 'pathname'
4
+ require 'fileutils'
5
+ require 'json'
6
+
7
+ require 'mamiya/script'
8
+
9
+ require 'mamiya/package'
10
+ require 'mamiya/steps/switch'
11
+
12
+ describe Mamiya::Steps::Switch do
13
+ let!(:tmpdir) { Dir.mktmpdir("mamiya-steps-switch-spec") }
14
+ after { FileUtils.remove_entry_secure tmpdir }
15
+
16
+ let(:deploy_to) { Pathname.new(tmpdir) }
17
+ let(:releases_dir) { deploy_to.join('releases').tap(&:mkdir) }
18
+
19
+ let(:release_path) { releases_dir.join('20140907030907').tap(&:mkdir) }
20
+ let(:current_path) { deploy_to.join('current') }
21
+
22
+ let(:target_meta) do
23
+ {script: 'script.rb'}
24
+ end
25
+
26
+ let(:script) do
27
+ double('script',
28
+ deploy_to: deploy_to,
29
+ before_switch: proc {},
30
+ release: proc {},
31
+ after_switch: proc {},
32
+ current_path: current_path,
33
+ )
34
+ end
35
+
36
+ let(:config) do
37
+ double('config')
38
+ end
39
+
40
+ let(:options) do
41
+ {target: release_path, labels: [:foo, :bar]}
42
+ end
43
+
44
+ subject(:step) { described_class.new(script: nil, config: config, **options) }
45
+
46
+ before do
47
+ File.write release_path.join('.mamiya.meta.json'), target_meta.to_json
48
+ allow(Mamiya::Script).to receive(:new).and_return(script)
49
+
50
+ allow(script).to receive(:load!).with(release_path.realpath.join('.mamiya.script', 'script.rb')).and_return(script)
51
+ allow(script).to receive(:set).and_return(script)
52
+ end
53
+
54
+ describe "#run!" do
55
+ it "calls hooks with proper order" do
56
+ hooks = %i(before_switch release after_switch)
57
+
58
+ flags = []
59
+ hooks.each do |sym|
60
+ allow(script).to receive(sym).and_return(proc { flags << sym })
61
+ end
62
+
63
+ expect { step.run! }.
64
+ to change { flags }.
65
+ from([]).
66
+ to(hooks)
67
+ end
68
+
69
+ it "calls after hook even if exception occured" do
70
+ e = Exception.new("Good bye, the cruel world")
71
+ allow(script).to receive(:release).and_return(proc { raise e })
72
+
73
+ received = nil
74
+ allow(script).to receive(:after_switch).and_return(proc { |_| received = _ })
75
+
76
+ expect {
77
+ begin
78
+ step.run!
79
+ rescue Exception; end
80
+ }.
81
+ to change { received }.
82
+ from(nil).to(e)
83
+ end
84
+
85
+ it "calls hook in :target (pwd)" do
86
+ pwd = nil
87
+ script.stub(release: proc { pwd = Dir.pwd })
88
+
89
+ expect {
90
+ step.run!
91
+ }.not_to change { Dir.pwd }
92
+
93
+ expect(File.realpath(pwd)).to eq release_path.realpath.to_s
94
+ end
95
+
96
+ it "sets release_path" do
97
+ expect(script).to receive(:set).with(:release_path, release_path.realpath)
98
+ expect(script).to receive(:set).with(:logger, step.logger)
99
+ step.run!
100
+ end
101
+
102
+ it "calls hooks using labels" do
103
+ allow(script).to receive(:before_switch).with(%i(foo bar)).and_return(proc {})
104
+ allow(script).to receive(:release).with(%i(foo bar)).and_return(proc {})
105
+ allow(script).to receive(:after_switch).with(%i(foo bar)).and_return(proc {})
106
+
107
+ step.run!
108
+ end
109
+
110
+ it "links current to release_path" do
111
+ expect {
112
+ step.run!
113
+ }.to change {
114
+ current_path.exist? ? current_path.realpath : nil
115
+ }.from(nil).to(release_path.realpath)
116
+ end
117
+
118
+ context "when current already exists" do
119
+ before do
120
+ current_path.make_symlink('releases/20140515000707')
121
+ end
122
+
123
+ it "links current to release_path" do
124
+ expect {
125
+ step.run!
126
+ }.to change {
127
+ current_path.exist? ? current_path.realpath : nil
128
+ }.to(release_path.realpath)
129
+ end
130
+ end
131
+
132
+ context "with no_release" do
133
+ let(:options) do
134
+ {target: release_path, no_release: true}
135
+ end
136
+
137
+ it "doesn't call release" do
138
+ called = false
139
+ allow(script).to receive(:release).and_return(proc { called = true })
140
+ step.run!
141
+
142
+ expect(called).to be_false
143
+ end
144
+ end
145
+ end
146
+ end