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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +16 -0
- data/Gemfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +43 -0
- data/Rakefile +6 -0
- data/bin/mamiya +17 -0
- data/config.example.yml +11 -0
- data/docs/sequences/deploy.png +0 -0
- data/docs/sequences/deploy.uml +58 -0
- data/example.rb +74 -0
- data/lib/mamiya.rb +5 -0
- data/lib/mamiya/agent.rb +181 -0
- data/lib/mamiya/agent/actions.rb +12 -0
- data/lib/mamiya/agent/fetcher.rb +137 -0
- data/lib/mamiya/agent/handlers/abstract.rb +20 -0
- data/lib/mamiya/agent/handlers/fetch.rb +68 -0
- data/lib/mamiya/cli.rb +322 -0
- data/lib/mamiya/cli/client.rb +172 -0
- data/lib/mamiya/config.rb +57 -0
- data/lib/mamiya/dsl.rb +192 -0
- data/lib/mamiya/helpers/git.rb +75 -0
- data/lib/mamiya/logger.rb +190 -0
- data/lib/mamiya/master.rb +118 -0
- data/lib/mamiya/master/agent_monitor.rb +146 -0
- data/lib/mamiya/master/agent_monitor_handlers.rb +44 -0
- data/lib/mamiya/master/web.rb +148 -0
- data/lib/mamiya/package.rb +122 -0
- data/lib/mamiya/script.rb +117 -0
- data/lib/mamiya/steps/abstract.rb +19 -0
- data/lib/mamiya/steps/build.rb +72 -0
- data/lib/mamiya/steps/extract.rb +26 -0
- data/lib/mamiya/steps/fetch.rb +24 -0
- data/lib/mamiya/steps/push.rb +34 -0
- data/lib/mamiya/storages.rb +17 -0
- data/lib/mamiya/storages/abstract.rb +48 -0
- data/lib/mamiya/storages/mock.rb +61 -0
- data/lib/mamiya/storages/s3.rb +127 -0
- data/lib/mamiya/util/label_matcher.rb +38 -0
- data/lib/mamiya/version.rb +3 -0
- data/mamiya.gemspec +35 -0
- data/misc/logger_test.rb +12 -0
- data/spec/agent/actions_spec.rb +37 -0
- data/spec/agent/fetcher_spec.rb +199 -0
- data/spec/agent/handlers/fetch_spec.rb +121 -0
- data/spec/agent_spec.rb +255 -0
- data/spec/config_spec.rb +50 -0
- data/spec/dsl_spec.rb +291 -0
- data/spec/fixtures/dsl_test_load.rb +1 -0
- data/spec/fixtures/dsl_test_use.rb +1 -0
- data/spec/fixtures/helpers/foo.rb +1 -0
- data/spec/fixtures/test-package-source/.mamiya.meta.json +1 -0
- data/spec/fixtures/test-package-source/greeting +1 -0
- data/spec/fixtures/test-package.tar.gz +0 -0
- data/spec/fixtures/test.yml +4 -0
- data/spec/logger_spec.rb +68 -0
- data/spec/master/agent_monitor_spec.rb +269 -0
- data/spec/master/web_spec.rb +121 -0
- data/spec/master_spec.rb +94 -0
- data/spec/package_spec.rb +394 -0
- data/spec/script_spec.rb +78 -0
- data/spec/spec_helper.rb +38 -0
- data/spec/steps/build_spec.rb +261 -0
- data/spec/steps/extract_spec.rb +68 -0
- data/spec/steps/fetch_spec.rb +96 -0
- data/spec/steps/push_spec.rb +73 -0
- data/spec/storages/abstract_spec.rb +22 -0
- data/spec/storages/s3_spec.rb +342 -0
- data/spec/storages_spec.rb +33 -0
- data/spec/support/dummy_serf.rb +70 -0
- data/spec/util/label_matcher_spec.rb +85 -0
- metadata +272 -0
data/spec/script_spec.rb
ADDED
@@ -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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|