mamiya 0.0.1.alpha2

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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +16 -0
  5. data/Gemfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +43 -0
  8. data/Rakefile +6 -0
  9. data/bin/mamiya +17 -0
  10. data/config.example.yml +11 -0
  11. data/docs/sequences/deploy.png +0 -0
  12. data/docs/sequences/deploy.uml +58 -0
  13. data/example.rb +74 -0
  14. data/lib/mamiya.rb +5 -0
  15. data/lib/mamiya/agent.rb +181 -0
  16. data/lib/mamiya/agent/actions.rb +12 -0
  17. data/lib/mamiya/agent/fetcher.rb +137 -0
  18. data/lib/mamiya/agent/handlers/abstract.rb +20 -0
  19. data/lib/mamiya/agent/handlers/fetch.rb +68 -0
  20. data/lib/mamiya/cli.rb +322 -0
  21. data/lib/mamiya/cli/client.rb +172 -0
  22. data/lib/mamiya/config.rb +57 -0
  23. data/lib/mamiya/dsl.rb +192 -0
  24. data/lib/mamiya/helpers/git.rb +75 -0
  25. data/lib/mamiya/logger.rb +190 -0
  26. data/lib/mamiya/master.rb +118 -0
  27. data/lib/mamiya/master/agent_monitor.rb +146 -0
  28. data/lib/mamiya/master/agent_monitor_handlers.rb +44 -0
  29. data/lib/mamiya/master/web.rb +148 -0
  30. data/lib/mamiya/package.rb +122 -0
  31. data/lib/mamiya/script.rb +117 -0
  32. data/lib/mamiya/steps/abstract.rb +19 -0
  33. data/lib/mamiya/steps/build.rb +72 -0
  34. data/lib/mamiya/steps/extract.rb +26 -0
  35. data/lib/mamiya/steps/fetch.rb +24 -0
  36. data/lib/mamiya/steps/push.rb +34 -0
  37. data/lib/mamiya/storages.rb +17 -0
  38. data/lib/mamiya/storages/abstract.rb +48 -0
  39. data/lib/mamiya/storages/mock.rb +61 -0
  40. data/lib/mamiya/storages/s3.rb +127 -0
  41. data/lib/mamiya/util/label_matcher.rb +38 -0
  42. data/lib/mamiya/version.rb +3 -0
  43. data/mamiya.gemspec +35 -0
  44. data/misc/logger_test.rb +12 -0
  45. data/spec/agent/actions_spec.rb +37 -0
  46. data/spec/agent/fetcher_spec.rb +199 -0
  47. data/spec/agent/handlers/fetch_spec.rb +121 -0
  48. data/spec/agent_spec.rb +255 -0
  49. data/spec/config_spec.rb +50 -0
  50. data/spec/dsl_spec.rb +291 -0
  51. data/spec/fixtures/dsl_test_load.rb +1 -0
  52. data/spec/fixtures/dsl_test_use.rb +1 -0
  53. data/spec/fixtures/helpers/foo.rb +1 -0
  54. data/spec/fixtures/test-package-source/.mamiya.meta.json +1 -0
  55. data/spec/fixtures/test-package-source/greeting +1 -0
  56. data/spec/fixtures/test-package.tar.gz +0 -0
  57. data/spec/fixtures/test.yml +4 -0
  58. data/spec/logger_spec.rb +68 -0
  59. data/spec/master/agent_monitor_spec.rb +269 -0
  60. data/spec/master/web_spec.rb +121 -0
  61. data/spec/master_spec.rb +94 -0
  62. data/spec/package_spec.rb +394 -0
  63. data/spec/script_spec.rb +78 -0
  64. data/spec/spec_helper.rb +38 -0
  65. data/spec/steps/build_spec.rb +261 -0
  66. data/spec/steps/extract_spec.rb +68 -0
  67. data/spec/steps/fetch_spec.rb +96 -0
  68. data/spec/steps/push_spec.rb +73 -0
  69. data/spec/storages/abstract_spec.rb +22 -0
  70. data/spec/storages/s3_spec.rb +342 -0
  71. data/spec/storages_spec.rb +33 -0
  72. data/spec/support/dummy_serf.rb +70 -0
  73. data/spec/util/label_matcher_spec.rb +85 -0
  74. metadata +272 -0
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+
4
+ require 'mamiya/script'
5
+ require 'mamiya/dsl'
6
+
7
+ describe Mamiya::Script do
8
+ it "inherits Mamiya::DSL" do
9
+ expect(described_class.ancestors).to include(Mamiya::DSL)
10
+ end
11
+
12
+ subject(:script) { described_class.new }
13
+ let(:log) { [] }
14
+
15
+ let(:logger) {
16
+ double("logger").tap do |_|
17
+ _.stub(:[]) { _ }
18
+ %i(info warn debug).each do |severity|
19
+ _.stub(severity) { |*args| log << [severity, *args]; _ }
20
+ end
21
+ end
22
+ }
23
+
24
+ before do
25
+ script.set :logger, logger
26
+ end
27
+
28
+ describe "#run" do
29
+ it "runs command" do
30
+ tmpdir = Dir.mktmpdir('akane-script-spec')
31
+ testee = File.join(tmpdir, 'test')
32
+
33
+ expect {
34
+ script.run("touch", testee)
35
+ } \
36
+ .to change { File.exists?(testee) } \
37
+ .from(false).to(true)
38
+ end
39
+
40
+ context "when the command failed" do
41
+ it "raises error" do
42
+ expect {
43
+ script.run("false")
44
+ }.to raise_error(Mamiya::Script::CommandFailed)
45
+ end
46
+
47
+ context "with allow_failure" do
48
+ it "ignores error" do
49
+ expect {
50
+ script.run("false", allow_failure: true)
51
+ }.not_to raise_error
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+ it "logs command as information" do
58
+ script.run("echo", "foo", "bar'", " baz")
59
+ expect(log).to include([:info, "$ echo foo bar\\' \\ baz"])
60
+ end
61
+
62
+ it "logs stdout as debug" do
63
+ script.run("echo", "foo")
64
+ expect(log).to include([:debug, "foo"])
65
+ end
66
+
67
+ it "logs stderr as warn" do
68
+ script.run("ruby", "-e", "warn 'bar'")
69
+ expect(log).to include([:warn, "bar"])
70
+ end
71
+
72
+ it "returns captured output as String" do
73
+ out = script.run("ruby", "-e", "puts 'foo'; warn 'bar'")
74
+ expect(out).to match(/^foo$/)
75
+ expect(out).to match(/^bar$/)
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,38 @@
1
+ require 'rack/test'
2
+ require 'mamiya/storages/mock'
3
+ require 'mamiya/logger'
4
+
5
+ module Rack
6
+ module Test
7
+ class Session
8
+ def envs
9
+ @envs ||= {}
10
+ end
11
+
12
+ alias_method :default_env_orig, :default_env
13
+ def default_env
14
+ default_env_orig.merge(envs)
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ unless ENV["ENABLE_LOG"]
21
+ Mamiya::Logger.defaults[:outputs] = []
22
+ end
23
+
24
+ RSpec.configure do |config|
25
+ config.treat_symbols_as_metadata_keys_with_true_values = true
26
+ config.run_all_when_everything_filtered = true
27
+ config.filter_run :focus
28
+
29
+ # Run specs in random order to surface order dependencies. If you find an
30
+ # order dependency and want to debug it, you can fix the order by providing
31
+ # the seed, which is printed after each run.
32
+ # --seed 1234
33
+ config.order = 'random'
34
+
35
+ config.after(:each) do
36
+ Mamiya::Storages::Mock.clear
37
+ end
38
+ end
@@ -0,0 +1,261 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+ require 'pathname'
4
+ require 'fileutils'
5
+
6
+ require 'mamiya/package'
7
+
8
+ require 'mamiya/steps/build'
9
+
10
+ describe Mamiya::Steps::Build do
11
+ let!(:tmpdir) { Dir.mktmpdir("mamiya-steps-build-spec") }
12
+ after { FileUtils.remove_entry_secure tmpdir }
13
+
14
+ let(:build_dir) { Pathname.new(tmpdir).join('build') }
15
+ let(:package_dir) { Pathname.new(tmpdir).join('pkg') }
16
+ let(:extract_dir) { Pathname.new(tmpdir).join('extract') }
17
+
18
+ let(:exclude_from_package) { [] }
19
+ let(:package_under) { nil }
20
+ let(:dereference_symlinks) { false }
21
+ let(:skip_prepare_build) { false }
22
+
23
+ let(:package_name) { nil }
24
+ let(:script) do
25
+ double('script',
26
+ application: 'app',
27
+ build_from: build_dir,
28
+ build_to: package_dir,
29
+ before_build: proc {},
30
+ prepare_build: proc {},
31
+ build: proc {},
32
+ after_build: proc {},
33
+ package_name: proc { |_| package_name || _ },
34
+ package_meta: proc { |_| _ },
35
+ package_under: package_under,
36
+ dereference_symlinks: dereference_symlinks,
37
+ exclude_from_package: exclude_from_package,
38
+ skip_prepare_build: skip_prepare_build,
39
+ )
40
+ end
41
+
42
+ subject(:build_step) { described_class.new(script: script) }
43
+
44
+ describe "#run!" do
45
+ before do
46
+ Dir.mkdir(build_dir)
47
+ Dir.mkdir(package_dir)
48
+ Dir.mkdir(extract_dir)
49
+
50
+ File.write(build_dir.join('greeting'), 'hello')
51
+ end
52
+
53
+ it "calls hooks with proper order" do
54
+ hooks = %i(before_build prepare_build build after_build)
55
+
56
+ flags = []
57
+ hooks.each do |sym|
58
+ allow(script).to receive(sym).and_return(proc { flags << sym })
59
+ end
60
+
61
+ expect { build_step.run! }.
62
+ to change { flags }.
63
+ from([]).
64
+ to(hooks)
65
+ end
66
+
67
+ it "calls after_build hook even if exception occured" do
68
+ e = Exception.new("Good bye, the cruel world")
69
+ allow(script).to receive(:build).and_return(proc { raise e })
70
+
71
+ received = nil
72
+ allow(script).to receive(:after_build).and_return(proc { |_| received = _ })
73
+
74
+ expect {
75
+ begin
76
+ build_step.run!
77
+ rescue Exception; end
78
+ }.
79
+ to change { received }.
80
+ from(nil).to(e)
81
+ end
82
+
83
+ it "calls build hook in :build_from (pwd)" do
84
+ pwd = nil
85
+ script.stub(build: proc { pwd = Dir.pwd })
86
+
87
+ expect {
88
+ build_step.run!
89
+ }.not_to change { Dir.pwd }
90
+
91
+ expect(File.realpath(pwd)).to eq script.build_from.realpath.to_s
92
+ end
93
+
94
+ it "creates package using Package after :build called" do
95
+ built = false
96
+ allow(script).to receive(:build).and_return(proc { built = true })
97
+ allow(script).to receive(:exclude_from_package).and_return(['test'])
98
+ allow(script).to receive(:dereference_symlinks).and_return(true)
99
+ allow(script).to receive(:package_under).and_return('foo')
100
+
101
+ expect_any_instance_of(Mamiya::Package).to \
102
+ receive(:build!).with(
103
+ build_dir,
104
+ hash_including(
105
+ exclude_from_package: ['test'],
106
+ dereference_symlinks: true,
107
+ package_under: 'foo',
108
+ )
109
+ ) {
110
+ expect(built).to be_true
111
+ }
112
+
113
+ build_step.run!
114
+ end
115
+
116
+ it "creates package with metadata including application" do
117
+ meta = {}
118
+ allow_any_instance_of(Mamiya::Package).to receive(:meta).and_return(meta)
119
+ expect_any_instance_of(Mamiya::Package).to receive(:build!) {
120
+ expect(meta[:application]).to eq 'app'
121
+ }
122
+
123
+ build_step.run!
124
+ end
125
+
126
+ context "with package name determiner" do
127
+ it "calls package name determiner with current candidate" do
128
+ received = nil
129
+ allow(script).to receive(:package_name).and_return(proc { |arg| received = arg })
130
+
131
+ build_step.run!
132
+
133
+ expect(received).to be_a_kind_of(Array)
134
+
135
+ # Default candidates
136
+ expect(received.size).to eq 2
137
+ expect(received[0]).to match(/\A\d{4}-\d{2}-\d{2}_\d{2}\.\d{2}\.\d{2}\z/)
138
+ expect(received[1]).to eq script.application
139
+ end
140
+
141
+ it "uses result by joining with '-' as package name to be built" do
142
+ allow(script).to receive(:package_name).and_return(proc { |arg| %w(veni vidi vici) })
143
+
144
+ build_step.run!
145
+
146
+ expect(package_dir.join('veni-vidi-vici.tar.gz')).to be_exist
147
+ end
148
+
149
+ it "calls the determiner in build dir" do
150
+ pwd = nil
151
+ allow(script).to receive(:package_name).and_return(proc { |arg| pwd = Dir.pwd; arg })
152
+
153
+ expect {
154
+ build_step.run!
155
+ }.not_to change { Dir.pwd }
156
+
157
+ expect(File.realpath(pwd)).to eq script.build_from.realpath.to_s
158
+ end
159
+
160
+ context "when the determiner returned non-Array" do
161
+ it "wraps with Array before calling next determiner"
162
+ end
163
+ end
164
+
165
+ context "with package meta determiner" do
166
+ it "calls determiners with current candidate" do
167
+ received = nil
168
+ allow(script).to receive(:package_meta).and_return(proc { |arg| received = arg })
169
+
170
+ build_step.run!
171
+
172
+ expect(received).to be_a_kind_of(Hash)
173
+ end
174
+
175
+ it "uses result as package metadata" do
176
+ meta = {}
177
+ allow_any_instance_of(Mamiya::Package).to receive(:meta).and_return(meta)
178
+ allow_any_instance_of(Mamiya::Package).to receive(:build!) { }
179
+
180
+ allow(script).to receive(:package_meta).and_return(proc { |arg| {'test' => 'hello'} })
181
+
182
+ build_step.run!
183
+
184
+ expect(meta['test']).to eq 'hello'
185
+ end
186
+
187
+ it "calls the determiner in build dir" do
188
+ pwd = nil
189
+ allow(script).to receive(:package_meta).and_return(proc { |arg| pwd = Dir.pwd; arg })
190
+
191
+ expect {
192
+ build_step.run!
193
+ }.not_to change { Dir.pwd }
194
+
195
+ expect(File.realpath(pwd)).to eq script.build_from.realpath.to_s
196
+ end
197
+ end
198
+
199
+ context "when build_from directory exist" do
200
+ it "calls prepare_build with update=true" do
201
+ arg = nil
202
+ allow(script).to receive(:prepare_build).and_return(proc { |update| arg = update })
203
+
204
+ expect {
205
+ build_step.run!
206
+ }.to change { arg }.
207
+ from(nil).to(true)
208
+ end
209
+ end
210
+
211
+ context "when build_from directory doesn't exist" do
212
+ before do
213
+ FileUtils.remove_entry_secure(build_dir)
214
+ end
215
+
216
+ it "calls prepare_build with update=false" do
217
+ arg = nil
218
+ allow(script).to receive(:prepare_build).and_return(proc { |update| arg = update })
219
+
220
+ expect {
221
+ begin
222
+ build_step.run!
223
+ rescue Errno::ENOENT; end
224
+ }.to change { arg }.
225
+ from(nil).to(false)
226
+ end
227
+
228
+ it "raises error" do
229
+ expect {
230
+ build_step.run!
231
+ }.to raise_error(Errno::ENOENT)
232
+ end
233
+ end
234
+
235
+
236
+ context "with skip_prepare_build option" do
237
+ context "when the option is false" do
238
+ let(:skip_prepare_build) { false }
239
+
240
+ it "calls prepare_build" do
241
+ flag = false
242
+ allow(script).to receive(:prepare_build).and_return(proc { flag = true })
243
+
244
+ expect { build_step.run! }.to change { flag }.
245
+ from(false).to(true)
246
+ end
247
+ end
248
+
249
+ context "when the option is true" do
250
+ let(:skip_prepare_build) { true }
251
+
252
+ it "doesn't call prepare_build" do
253
+ flag = false
254
+ allow(script).to receive(:prepare_build).and_return(proc { flag = true })
255
+
256
+ expect { build_step.run! }.not_to change { flag }
257
+ end
258
+ end
259
+ end
260
+ end
261
+ end
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+ require 'pathname'
4
+ require 'fileutils'
5
+
6
+ require 'mamiya/package'
7
+ require 'mamiya/steps/extract'
8
+
9
+ describe Mamiya::Steps::Extract do
10
+ let!(:tmpdir) { Dir.mktmpdir("mamiya-steps-extract-spec") }
11
+ after { FileUtils.remove_entry_secure tmpdir }
12
+
13
+ let(:package_dir) { Pathname.new(tmpdir).join('pkg').tap(&:mkdir) }
14
+
15
+ let(:target_package) { package_dir.join('test-package.tar.gz').to_s }
16
+ let(:script) do
17
+ double('script',
18
+ application: 'myapp',
19
+ )
20
+ end
21
+
22
+ let(:destination) { Pathname.new(tmpdir).join('dst') }
23
+
24
+ let(:options) do
25
+ {
26
+ package: target_package,
27
+ destination: destination.to_s,
28
+ }
29
+ end
30
+
31
+ subject(:extract_step) { described_class.new(script: script, **options) }
32
+
33
+ describe "#run!" do
34
+ before do
35
+ FileUtils.cp File.join(__dir__, '..', 'fixtures', 'test-package.tar.gz'), target_package
36
+ File.write target_package.gsub(/\.tar\.gz$/,'.json'), "{}\n"
37
+ end
38
+
39
+ context "when destination exists" do
40
+ before do
41
+ destination.mkdir()
42
+ allow_any_instance_of(Mamiya::Package).to receive(:name).and_return('package-name')
43
+ end
44
+
45
+ it "extracts package on sub-directory named as same as package name" do
46
+ extract_step.run!
47
+ expect(destination.join('package-name')).to be_a_directory
48
+ expect(destination.join('package-name', 'greeting')).to be_exist
49
+ end
50
+ end
51
+
52
+ context "when destination not exists" do
53
+ it "extracts package on destination" do
54
+ extract_step.run!
55
+ expect(destination).to be_a_directory
56
+ expect(destination.join('greeting')).to be_exist
57
+ end
58
+ end
59
+
60
+ context "with verify option" do
61
+ it "verifies"
62
+ end
63
+
64
+ context "when package not exists" do
65
+ it "-"
66
+ end
67
+ end
68
+ end