evrone-ci-worker 0.2.0.pre0

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 (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +29 -0
  7. data/Rakefile +24 -0
  8. data/bin/evrone-ci-worker +22 -0
  9. data/docker/Dockerfile +61 -0
  10. data/docker/sv-enable +26 -0
  11. data/docker/sv-gen +50 -0
  12. data/evrone-ci-worker.gemspec +32 -0
  13. data/lib/evrone/ci/worker/configuration.rb +58 -0
  14. data/lib/evrone/ci/worker/consumers/job_logs_consumer.rb +15 -0
  15. data/lib/evrone/ci/worker/consumers/job_status_consumer.rb +16 -0
  16. data/lib/evrone/ci/worker/consumers/jobs_consumer.rb +27 -0
  17. data/lib/evrone/ci/worker/docker.rb +35 -0
  18. data/lib/evrone/ci/worker/ext/string.rb +10 -0
  19. data/lib/evrone/ci/worker/helper/config.rb +14 -0
  20. data/lib/evrone/ci/worker/helper/logger.rb +14 -0
  21. data/lib/evrone/ci/worker/initializers/amqp.rb +53 -0
  22. data/lib/evrone/ci/worker/job.rb +42 -0
  23. data/lib/evrone/ci/worker/local.rb +37 -0
  24. data/lib/evrone/ci/worker/middlewares/docker_before_script.rb +38 -0
  25. data/lib/evrone/ci/worker/middlewares/docker_fetch_repo.rb +76 -0
  26. data/lib/evrone/ci/worker/middlewares/docker_script.rb +39 -0
  27. data/lib/evrone/ci/worker/middlewares/docker_start_container.rb +81 -0
  28. data/lib/evrone/ci/worker/middlewares/local_before_script.rb +29 -0
  29. data/lib/evrone/ci/worker/middlewares/local_create_dirs.rb +46 -0
  30. data/lib/evrone/ci/worker/middlewares/local_fetch_repo.rb +47 -0
  31. data/lib/evrone/ci/worker/middlewares/local_script.rb +29 -0
  32. data/lib/evrone/ci/worker/middlewares/log_job.rb +25 -0
  33. data/lib/evrone/ci/worker/middlewares/update_job_status.rb +64 -0
  34. data/lib/evrone/ci/worker/runner/docker.rb +0 -0
  35. data/lib/evrone/ci/worker/runner/local.rb +52 -0
  36. data/lib/evrone/ci/worker/version.rb +7 -0
  37. data/lib/evrone/ci/worker.rb +84 -0
  38. data/spec/lib/worker/configuration_spec.rb +40 -0
  39. data/spec/lib/worker/docker_spec.rb +29 -0
  40. data/spec/lib/worker/job_spec.rb +54 -0
  41. data/spec/lib/worker/local_spec.rb +29 -0
  42. data/spec/lib/worker/middlewares/docker_before_script_spec.rb +30 -0
  43. data/spec/lib/worker/middlewares/docker_fetch_repo_spec.rb +26 -0
  44. data/spec/lib/worker/middlewares/docker_script_spec.rb +30 -0
  45. data/spec/lib/worker/middlewares/docker_start_container_spec.rb +21 -0
  46. data/spec/lib/worker/middlewares/local_before_script_spec.rb +35 -0
  47. data/spec/lib/worker/middlewares/local_create_dirs_spec.rb +42 -0
  48. data/spec/lib/worker/middlewares/local_fetch_repo_spec.rb +56 -0
  49. data/spec/lib/worker/middlewares/local_script_spec.rb +35 -0
  50. data/spec/lib/worker/middlewares/log_job_spec.rb +15 -0
  51. data/spec/lib/worker/middlewares/update_job_status_spec.rb +70 -0
  52. data/spec/lib/worker_spec.rb +39 -0
  53. data/spec/spec_helper.rb +26 -0
  54. data/spec/support/create.rb +33 -0
  55. data/spec/support/shared_examples/update_job_status_message_spec.rb +7 -0
  56. metadata +257 -0
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe Evrone::CI::Worker::Job do
4
+ let(:message) { create :message, 'PerformJob' }
5
+ let(:job) { described_class.new message }
6
+
7
+ subject { job }
8
+
9
+ context "just created" do
10
+ its(:message) { should eq message }
11
+ end
12
+
13
+ context "create_job_log_message" do
14
+ let(:tm) { Time.new(2012, 12, 10, 15, 45) }
15
+ let(:data) { 'log' }
16
+ subject { job.create_job_log_message data }
17
+
18
+ before do
19
+ mock(Time).now { tm }
20
+ end
21
+
22
+ it { should be_an_instance_of(Evrone::CI::Message::JobLog) }
23
+ its(:job_id) { should eq job.message.job_id }
24
+ its(:build_id) { should eq job.message.id }
25
+ its(:tm) { should eq tm.to_i }
26
+ its(:log) { should eq data }
27
+ end
28
+
29
+ context "add_to_output" do
30
+ let(:data) { 'data' }
31
+ let(:messages) { Evrone::CI::Worker::JobLogsConsumer.messages }
32
+ subject { job.add_to_output(data) ; job }
33
+
34
+ its(:output) { should eq data }
35
+ it "should delivery message" do
36
+ expect {
37
+ subject
38
+ }.to change(messages, :size).by(1)
39
+ end
40
+ end
41
+
42
+ context "add_command_to_output" do
43
+ let(:data) { 'data' }
44
+ let(:messages) { Evrone::CI::Worker::JobLogsConsumer.messages }
45
+ subject { job.add_command_to_output(data) ; job }
46
+
47
+ its(:output) { should eq "$ #{data}\n" }
48
+ it "should delivery message" do
49
+ expect {
50
+ subject
51
+ }.to change(messages, :size).by(1)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+
5
+ describe Evrone::CI::Worker::Local do
6
+ let(:options) { { } }
7
+ let(:job) { create :job, options }
8
+ let(:path) { '/tmp/.ci' }
9
+ let(:local) { described_class.new job, path }
10
+ subject { local }
11
+
12
+ after { FileUtils.rm_rf path }
13
+
14
+ context "perform", local_run: true do
15
+ subject { local.perform }
16
+ it { should eq 0 }
17
+
18
+ context "when fail before_script" do
19
+ let(:options) { { before_script: "/bin/false" } }
20
+ it { should eq(-1) }
21
+ end
22
+
23
+ context "when fail script" do
24
+ let(:options) { { script: "/bin/false" } }
25
+ it { should satisfy { |n| [1,127].include?(n) } }
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Evrone::CI::Worker::DockerBeforeScript, run_docker: true do
4
+ let(:exit_code) { 0 }
5
+ let(:app) { ->(_) { exit_code } }
6
+ let(:script) { "echo before_script" }
7
+ let(:job) { create :job, before_script: script }
8
+ let(:env) { OpenStruct.new job: job }
9
+ let(:mid) { described_class.new app }
10
+ let(:docker_mid) { Evrone::CI::Worker::DockerStartContainer.new(mid) }
11
+
12
+ subject { docker_mid.call env }
13
+
14
+ before do
15
+ stub(env).docker_repo_dir { '/tmp' }
16
+ end
17
+
18
+ it "should be" do
19
+ expect(subject).to eq 0
20
+ expect(job.output).to match("before_script")
21
+ end
22
+
23
+ context "when fail to execute" do
24
+ let(:script) { "/bin/false" }
25
+ it "should be" do
26
+ expect(subject).to eq(-1)
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe Evrone::CI::Worker::DockerFetchRepo, run_docker: true do
4
+ let(:exit_code) { 0 }
5
+ let(:app) { ->(_) { exit_code } }
6
+ let(:options) { {} }
7
+ let(:job) { create :job, options }
8
+ let(:env) { OpenStruct.new job: job }
9
+ let(:mid) { described_class.new app }
10
+ let(:docker_mid) { Evrone::CI::Worker::DockerStartContainer.new(mid) }
11
+
12
+ subject { docker_mid.call env }
13
+
14
+ it "should be" do
15
+ expect(subject).to eq 0
16
+ end
17
+
18
+ context "when fail to fetch repo" do
19
+ let(:options) { { src: "/not-exists-repo.git" } }
20
+ it "should be" do
21
+ expect(subject).to eq(-1)
22
+ expect(job.output).to be_include("does not exist")
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe Evrone::CI::Worker::DockerScript, run_docker: true do
4
+ let(:exit_code) { 0 }
5
+ let(:app) { ->(_) { exit_code } }
6
+ let(:script) { "echo script" }
7
+ let(:job) { create :job, script: script }
8
+ let(:env) { OpenStruct.new job: job }
9
+ let(:mid) { described_class.new app }
10
+ let(:docker_mid) { Evrone::CI::Worker::DockerStartContainer.new(mid) }
11
+
12
+ subject { docker_mid.call env }
13
+
14
+ before do
15
+ stub(env).docker_repo_dir { '/tmp' }
16
+ end
17
+
18
+ it "should be" do
19
+ expect(subject).to eq 0
20
+ expect(job.output).to match("script")
21
+ end
22
+
23
+ context "when fail to execute" do
24
+ let(:script) { "/bin/false" }
25
+ it "should be" do
26
+ expect(subject).to eq(1)
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Evrone::CI::Worker::DockerStartContainer, run_docker: true do
4
+ let(:exit_code) { 0 }
5
+ let(:app) { ->(_) { exit_code } }
6
+ let(:job) { create :job }
7
+ let(:env) { OpenStruct.new job: job }
8
+ let(:mid) { described_class.new app }
9
+
10
+ subject { mid.call env }
11
+
12
+ it "should be" do
13
+ expect(subject).to eq 0
14
+
15
+ expect(env.ssh).to be
16
+ expect(env.container).to be
17
+ expect(env.docker_repo_dir).to eq "/home/ci/evrone/test-repo"
18
+ end
19
+
20
+ end
21
+
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+
5
+ describe Evrone::CI::Worker::LocalBeforeScript do
6
+ let(:command) { "echo before_script" }
7
+ let(:app) { ->(_) { 0 } }
8
+ let(:job) { create :job, before_script: command }
9
+ let(:work_dir) { Pathname.new '/tmp/.ci' }
10
+ let(:env) { OpenStruct.new job: job, work_dir: work_dir }
11
+ let(:mid) { described_class.new app }
12
+
13
+ subject { mid.call env }
14
+
15
+ after do
16
+ FileUtils.rm_rf work_dir
17
+ end
18
+
19
+ before do
20
+ FileUtils.mkdir_p work_dir
21
+ end
22
+
23
+ it { should eq 0 }
24
+
25
+ it "should capture output" do
26
+ subject
27
+ expect(job.output).to eq "before_script\n"
28
+ end
29
+
30
+ context "when script failed" do
31
+ let(:command) { '/bin/false' }
32
+ it { should eq(-1) }
33
+ end
34
+ end
35
+
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+
5
+ describe Evrone::CI::Worker::LocalCreateDirs do
6
+ let(:app) { ->(_) { 0 } }
7
+ let(:job) { create :job }
8
+ let(:path_prefix) { Pathname.new '/tmp/.ci' }
9
+ let(:env) { OpenStruct.new job: job, path_prefix: path_prefix }
10
+ let(:mid) { described_class.new app }
11
+
12
+ subject { mid.call env }
13
+
14
+ after do
15
+ FileUtils.rm_rf path_prefix
16
+ end
17
+
18
+ it { should eq 0 }
19
+
20
+ context "create and assign directories" do
21
+
22
+ before do
23
+ subject
24
+ end
25
+
26
+ it "work_dir" do
27
+ expect(env.work_dir.to_s).to eq '/tmp/.ci/work/evrone/test-repo'
28
+ expect(File.directory? env.work_dir).to be
29
+ end
30
+
31
+ it "tmp_dir" do
32
+ expect(env.tmp_dir.to_s).to eq '/tmp/.ci/tmp/evrone/test-repo'
33
+ expect(File.directory? env.tmp_dir).to be
34
+ end
35
+
36
+ it "repo_dir" do
37
+ expect(env.repo_dir.to_s).to eq '/tmp/.ci/repo/evrone/test-repo'
38
+ expect(File.directory? env.repo_dir).to be
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+
5
+ describe Evrone::CI::Worker::LocalFetchRepo do
6
+ let(:exit_code) { 0 }
7
+ let(:app) { ->(_) { exit_code } }
8
+ let(:job) { create :job }
9
+ let(:path_prefix) { Pathname.new("/tmp/.ci") }
10
+ let(:repo_dir) { path_prefix.join("repo") }
11
+ let(:work_dir) { path_prefix.join("work") }
12
+ let(:env) { OpenStruct.new job: job, repo_dir: repo_dir, work_dir: work_dir }
13
+ let(:mid) { described_class.new app }
14
+
15
+ subject { mid.call env }
16
+
17
+ after do
18
+ FileUtils.rm_rf path_prefix
19
+ end
20
+
21
+ it { should eq 0 }
22
+
23
+ it "should create repo inside repo_dir" do
24
+ subject
25
+ expect(File.directory? "/tmp/.ci/repo/.git" ).to be
26
+ end
27
+
28
+ it "should create copy of repo inside work_dir" do
29
+ subject
30
+ expect(File.readable? "/tmp/.ci/work/Gemfile").to be
31
+ end
32
+
33
+ context "when fetch failed" do
34
+ before do
35
+ any_instance_of(Evrone::CI::SCM::Git) do |git|
36
+ mock(git).fetch { 1 }
37
+ end
38
+ end
39
+
40
+ it { should eq(-1) }
41
+ end
42
+
43
+ context "when export failed" do
44
+ before do
45
+ any_instance_of(Evrone::CI::SCM::Git) do |git|
46
+ mock(git).fetch { 0 }
47
+ end
48
+ any_instance_of(described_class) do |m|
49
+ mock(m).export(anything, anything) { 1 }
50
+ end
51
+ end
52
+
53
+ it { should eq(-1) }
54
+ end
55
+ end
56
+
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+ require 'pathname'
3
+ require 'fileutils'
4
+
5
+ describe Evrone::CI::Worker::LocalScript do
6
+ let(:command) { "echo script" }
7
+ let(:app) { ->(_) { 0 } }
8
+ let(:job) { create :job, script: command }
9
+ let(:work_dir) { Pathname.new '/tmp/.ci' }
10
+ let(:env) { OpenStruct.new job: job, work_dir: work_dir }
11
+ let(:mid) { described_class.new app }
12
+
13
+ subject { mid.call env }
14
+
15
+ after do
16
+ FileUtils.rm_rf work_dir
17
+ end
18
+
19
+ before do
20
+ FileUtils.mkdir_p work_dir
21
+ end
22
+
23
+ it { should eq 0 }
24
+
25
+ it "should capture output" do
26
+ subject
27
+ expect(job.output).to eq "script\n"
28
+ end
29
+
30
+ context "when script failed" do
31
+ let(:command) { '/bin/false' }
32
+ it { should satisfy { |n| [1,127].include?(n) } }
33
+ end
34
+ end
35
+
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ describe Evrone::CI::Worker::LogJob do
4
+ let(:exit_code) { 0 }
5
+ let(:app) { ->(_) { exit_code } }
6
+ let(:job) { create :job }
7
+ let(:env) { OpenStruct.new job: job }
8
+ let(:mid) { described_class.new app }
9
+
10
+ subject { mid.call env }
11
+
12
+ it { should eq 0 }
13
+
14
+ end
15
+
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe Evrone::CI::Worker::UpdateJobStatus do
4
+ let(:exit_code) { 0 }
5
+ let(:app) { ->(_) { exit_code } }
6
+ let(:job) { create :job }
7
+ let(:env) { OpenStruct.new job: job }
8
+ let(:mid) { described_class.new app }
9
+ let(:messages) { Evrone::CI::Worker::JobStatusConsumer.messages }
10
+
11
+ subject { mid.call env }
12
+
13
+ it "should delivery 2 messages" do
14
+ expect {
15
+ subject
16
+ }.to change(messages, :size).by(2)
17
+ end
18
+
19
+ { 0 => 3, 1 => 4, -1 => 5 }.each do |code, status|
20
+ context "when exit code is #{code}" do
21
+ let(:exit_code) { code }
22
+ it { should eq code }
23
+
24
+ context "messages" do
25
+ before { mid.call env }
26
+
27
+ context "first" do
28
+ subject { messages.first }
29
+ it_should_behave_like "UpdateJobStatus message" do
30
+ its(:status) { should eq 2 }
31
+ end
32
+ end
33
+
34
+ context "last" do
35
+ subject { messages.last }
36
+ it_should_behave_like "UpdateJobStatus message" do
37
+ its(:status) { should eq status }
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+ end
44
+
45
+ context "when raise exception" do
46
+ let(:app) { ->(_) { raise "Ignore Me" } }
47
+ it { should eq(-1) }
48
+
49
+ context "messages" do
50
+ before { mid.call env }
51
+
52
+ context "first" do
53
+ subject { messages.first }
54
+ it_should_behave_like "UpdateJobStatus message" do
55
+ its(:status) { should eq 2 }
56
+ end
57
+ end
58
+
59
+ context "last" do
60
+ subject { messages.last }
61
+ it_should_behave_like "UpdateJobStatus message" do
62
+ its(:status) { should eq 5 }
63
+ end
64
+ end
65
+
66
+ end
67
+ end
68
+
69
+ end
70
+
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Evrone::CI::Worker do
4
+
5
+ context ".perform" do
6
+ let(:job) { create :job }
7
+ let(:run) { :docker }
8
+ subject { described_class.perform job, '/tmp' }
9
+
10
+ before do
11
+ described_class.configure do |c|
12
+ c.run = run
13
+ end
14
+ end
15
+
16
+ context "when run at :docker" do
17
+ let(:run) { :docker }
18
+ let(:docker) { 'docker' }
19
+ before do
20
+ mock(Evrone::CI::Worker::Docker).new(job, '/tmp') { docker }
21
+ mock(docker).perform { true }
22
+ end
23
+
24
+ it { should be }
25
+ end
26
+
27
+ context "when run at :local" do
28
+ let(:run) { :local }
29
+ let(:local) { 'local' }
30
+ before do
31
+ mock(Evrone::CI::Worker::Local).new(job, '/tmp') { local }
32
+ mock(local).perform { true }
33
+ end
34
+
35
+ it { should be }
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,26 @@
1
+ require File.expand_path '../../lib/evrone/ci/worker', __FILE__
2
+
3
+ Bundler.require(:test)
4
+ require 'rspec/autorun'
5
+ require 'evrone/common/amqp/testing'
6
+ require 'evrone/ci/message/testing'
7
+
8
+ Dir[File.expand_path("../..", __FILE__) + "/spec/support/**/*.rb"].each {|f| require f}
9
+
10
+ RSpec.configure do |config|
11
+ config.mock_with :rr
12
+ config.filter_run_excluding(:amqp => true) if ENV['REAL_AMQP']
13
+
14
+ config.before(:each) do
15
+ Evrone::Common::AMQP::Testing.clear
16
+ Evrone::CI::Worker.reset_config!
17
+
18
+ Evrone::CI::Worker.configure do |c|
19
+ c.docker.ssh.port = 2223
20
+ c.docker.ssh.host = 'localhost'
21
+ c.docker.create_options = {
22
+ 'PortSpecs' => ['2022:22']
23
+ }
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ require 'yaml'
2
+
3
+ def create(who, *args)
4
+
5
+ options = args.last.is_a?(Hash) ? args.pop : {}
6
+
7
+ case who
8
+
9
+ when :local_repo
10
+ Evrone::CI::Worker.root.join("fixtures/repo").to_s
11
+
12
+ when :message
13
+ name = args.shift
14
+
15
+ klass = Evrone::CI::Message.const_get name
16
+ klass.test_message options
17
+
18
+ when :job
19
+ message = options[:message] || create(:message, 'PerformJob', options)
20
+ Evrone::CI::Worker::Job.new message
21
+
22
+ when :working_dirs
23
+
24
+ Evrone::CI::Worker::WorkingDirs.create args.shift, args.shift || '/tmp/.test'
25
+
26
+ when :git
27
+ build = args.shift
28
+ path = args.shift
29
+ Evrone::CI::SCM::Git.new build.src, build.sha, path, deploy_key: build.deploy_key, &build.method(:add_to_output)
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,7 @@
1
+ shared_examples "UpdateJobStatus message" do
2
+ its(:build_id) { should eq job.message.id }
3
+ its(:job_id) { should eq job.message.job_id }
4
+ its(:matrix) { should eq job.message.matrix_keys }
5
+ its(:tm) { should be }
6
+ its(:tm_usec) { should be }
7
+ end