vx-router 0.2.0.pre28

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 (45) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +9 -0
  5. data/LICENSE.txt +276 -0
  6. data/Rakefile +46 -0
  7. data/bin/vx-router +5 -0
  8. data/fixtures/jobs_publisher +13 -0
  9. data/fixtures/travis.yml +5 -0
  10. data/lib/vx/router.rb +158 -0
  11. data/lib/vx/router/build.rb +80 -0
  12. data/lib/vx/router/cli.rb +53 -0
  13. data/lib/vx/router/configuration.rb +27 -0
  14. data/lib/vx/router/consumers/build_logs_consumer.rb +13 -0
  15. data/lib/vx/router/consumers/build_status_consumer.rb +13 -0
  16. data/lib/vx/router/consumers/builds_consumer.rb +27 -0
  17. data/lib/vx/router/consumers/job_status_consumer.rb +13 -0
  18. data/lib/vx/router/consumers/jobs_consumer.rb +13 -0
  19. data/lib/vx/router/ext/array.rb +5 -0
  20. data/lib/vx/router/ext/string.rb +9 -0
  21. data/lib/vx/router/helper/config.rb +11 -0
  22. data/lib/vx/router/helper/logger.rb +11 -0
  23. data/lib/vx/router/helper/trace_sh_command.rb +12 -0
  24. data/lib/vx/router/initializers/amqp.rb +3 -0
  25. data/lib/vx/router/script_builder.rb +98 -0
  26. data/lib/vx/router/script_builder/databases.rb +28 -0
  27. data/lib/vx/router/script_builder/env.rb +21 -0
  28. data/lib/vx/router/script_builder/prepare.rb +58 -0
  29. data/lib/vx/router/script_builder/ruby.rb +68 -0
  30. data/lib/vx/router/script_builder/script.rb +22 -0
  31. data/lib/vx/router/script_builder/webdav_cache.rb +80 -0
  32. data/lib/vx/router/version.rb +5 -0
  33. data/spec/lib/build_spec.rb +123 -0
  34. data/spec/lib/configuration_spec.rb +23 -0
  35. data/spec/lib/router_spec.rb +80 -0
  36. data/spec/lib/script_builder/prepare_spec.rb +44 -0
  37. data/spec/lib/script_builder/webdav_cache_spec.rb +80 -0
  38. data/spec/lib/script_builder_spec.rb +36 -0
  39. data/spec/spec_helper.rb +17 -0
  40. data/spec/support/create.rb +40 -0
  41. data/spec/support/fixture.rb +7 -0
  42. data/spec/support/last_build_log_message.rb +3 -0
  43. data/spec/support/shared_examples/update_build_status_message.rb +5 -0
  44. data/vx-router.gemspec +32 -0
  45. metadata +225 -0
@@ -0,0 +1,80 @@
1
+ require 'vx/message'
2
+ require 'vx/common'
3
+
4
+ module Vx
5
+ class Router
6
+ class Build
7
+
8
+ INITIALIED = 0
9
+ STARTED = 2
10
+ FINISHED = 3
11
+ FAILED = 5
12
+
13
+ include Router::Helper::Logger
14
+ include Router::Helper::Config
15
+
16
+ attr_reader :message, :output, :output_counter
17
+ attr_accessor :jobs_count
18
+
19
+ def initialize(perform_build_message)
20
+ @output_counter = 0
21
+ @output = Common::OutputBuffer.new(&method(:publish_build_log_message))
22
+ @message = perform_build_message
23
+ end
24
+
25
+ def release
26
+ output.close
27
+ end
28
+
29
+ def to_perform_job_message(configuration, job_id)
30
+ script_builder = ScriptBuilder.new self, configuration
31
+ job_message = Message::PerformJob.new(
32
+ id: message.id,
33
+ name: message.name,
34
+ job_id: job_id,
35
+ before_script: script_builder.to_before_script,
36
+ script: script_builder.to_script,
37
+ after_script: script_builder.to_after_script,
38
+ matrix_keys: configuration.matrix_keys,
39
+ )
40
+ job_message
41
+ end
42
+
43
+ def to_build_status_message(status)
44
+ tm = Time.now
45
+ attributes = {
46
+ build_id: message.id,
47
+ status: status,
48
+ tm: tm.to_i,
49
+ jobs_count: jobs_count || 0,
50
+ }
51
+ Message::BuildStatus.new attributes
52
+ end
53
+
54
+ def add_to_output(str)
55
+ output << str
56
+ logger.debug str.strip if logger.level == 0
57
+ end
58
+
59
+ def add_command_to_output(cmd)
60
+ add_to_output "$ #{cmd}\n"
61
+ end
62
+
63
+ def publish_build_log_message(str)
64
+ @output_counter += 1
65
+ log = Message::BuildLog.new(
66
+ build_id: message.id,
67
+ tm: output_counter,
68
+ log: str
69
+ )
70
+ BuildLogsConsumer.publish log
71
+ log
72
+ end
73
+
74
+ def scm_class
75
+ Vx::SCM::Git
76
+ end
77
+
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,53 @@
1
+ require 'optparse'
2
+ require 'vx/common/amqp_setup'
3
+ require 'vx/common/env_file'
4
+
5
+ module Vx
6
+ class Router
7
+ class CLI
8
+
9
+ include Helper::Config
10
+ include Helper::Logger
11
+ include Common::EnvFile
12
+
13
+ def initialize
14
+ @options = {}
15
+ parse!
16
+ Router.initialize!
17
+ end
18
+
19
+ def run
20
+ trap('INT') {
21
+ Thread.new do
22
+ Vx::Common::AMQP.shutdown
23
+ end.join
24
+ }
25
+
26
+ Vx::Common::AMQP::Supervisor::Threaded.build(
27
+ Vx::Router::BuildsConsumer => config.workers,
28
+ ).run
29
+ end
30
+
31
+ private
32
+
33
+ def parse!
34
+ OptionParser.new do |opts|
35
+ opts.banner = "Usage: vx-router [options]"
36
+ opts.on("-w", "--workers NUM", "Number of workers, default 1") do |v|
37
+ @options[:workers] = v.to_i
38
+ end
39
+ opts.on("-c", "--config FILE", "Path to configuration file, default /etc/vexor/ci") do |v|
40
+ @options[:config] = v
41
+ end
42
+ end.parse!
43
+
44
+ read_env_file @options.delete(:config)
45
+
46
+ @options.each_pair do |k,v|
47
+ config.public_send("#{k}=", v)
48
+ end
49
+ end
50
+
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,27 @@
1
+ require 'hashr'
2
+ require 'logger'
3
+ require 'vx/common/tagged_logging'
4
+
5
+ module Vx
6
+ class Router
7
+ class Configuration < ::Hashr
8
+
9
+ extend Hashr::EnvDefaults
10
+
11
+ self.env_namespace = 'ci_router'
12
+ self.raise_missing_keys = true
13
+
14
+ define amqp_url: nil,
15
+ timeout: 30 * 60,
16
+ logger: Common::TaggedLogging.new(Logger.new STDOUT),
17
+ workers: 1,
18
+ webdav_cache_url: nil
19
+
20
+
21
+ def timeout
22
+ self[:timeout].to_i
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ require 'vx/common/amqp'
2
+
3
+ module Vx
4
+ class Router
5
+ class BuildLogsConsumer
6
+
7
+ include Vx::Common::AMQP::Consumer
8
+
9
+ exchange 'vx.builds.log'
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'vx/common/amqp'
2
+
3
+ module Vx
4
+ class Router
5
+ class BuildStatusConsumer
6
+
7
+ include Vx::Common::AMQP::Consumer
8
+
9
+ exchange 'vx.builds.status'
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ require 'vx/common/amqp'
2
+ require 'vx/message'
3
+
4
+ module Vx
5
+ class Router
6
+ class BuildsConsumer
7
+
8
+ include Vx::Common::AMQP::Consumer
9
+ include Helper::Logger
10
+
11
+ exchange 'vx.builds'
12
+ queue 'vx.worker.builds'
13
+ ack true
14
+
15
+ model Message::PerformBuild
16
+
17
+ def perform(message)
18
+ logger.tagged self.class.consumer_id do
19
+ build = Build.new message
20
+ Router.new(build).perform
21
+ end
22
+ ack!
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,13 @@
1
+ require 'vx/common/amqp'
2
+
3
+ module Vx
4
+ class Router
5
+ class JobStatusConsumer
6
+
7
+ include Vx::Common::AMQP::Consumer
8
+
9
+ exchange 'vx.jobs.status'
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ require 'vx/common/amqp'
2
+
3
+ module Vx
4
+ class Router
5
+ class JobsConsumer
6
+
7
+ include Vx::Common::AMQP::Consumer
8
+
9
+ exchange 'vx.jobs'
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def extract_options!
3
+ self.last.is_a?(Hash) ? self.pop : {}
4
+ end
5
+ end
@@ -0,0 +1,9 @@
1
+ class String
2
+ def compact
3
+ gsub(/\n/, ' ').gsub(/ +/, ' ').strip
4
+ end
5
+
6
+ def camelize
7
+ split("_").each {|s| s.capitalize! }.join("")
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Vx
2
+ class Router
3
+ module Helper::Config
4
+
5
+ def config
6
+ Router.config
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ module Vx
2
+ class Router
3
+ module Helper::Logger
4
+
5
+ def logger
6
+ Router.logger
7
+ end
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module Vx
2
+ class Router
3
+
4
+ module Helper::TraceShCommand
5
+ def trace_sh_command(cmd, options = {})
6
+ trace = options[:trace] || cmd
7
+ "echo #{Shellwords.escape "$ #{trace}"}\n#{cmd}"
8
+ end
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ require 'vx/common/amqp'
2
+
3
+ Vx::Common::AMQP.setup(Vx::Router.logger, url: Vx::Router.config.amqp_url)
@@ -0,0 +1,98 @@
1
+ require 'shellwords'
2
+ require 'vx/common'
3
+
4
+ module Vx
5
+ class Router
6
+ class ScriptBuilder
7
+
8
+ autoload :Env, File.expand_path("../script_builder/env", __FILE__)
9
+ autoload :Ruby, File.expand_path("../script_builder/ruby", __FILE__)
10
+ autoload :Script, File.expand_path("../script_builder/script", __FILE__)
11
+ autoload :Prepare, File.expand_path("../script_builder/prepare", __FILE__)
12
+ autoload :Databases, File.expand_path("../script_builder/databases", __FILE__)
13
+ autoload :WebdavCache, File.expand_path("../script_builder/webdav_cache", __FILE__)
14
+
15
+ include Common::Helper::Middlewares
16
+
17
+ middlewares do
18
+ use ScriptBuilder::WebdavCache
19
+ use ScriptBuilder::Env
20
+ use ScriptBuilder::Prepare
21
+ use ScriptBuilder::Databases
22
+ use ScriptBuilder::Ruby
23
+ use ScriptBuilder::Script
24
+ end
25
+
26
+ attr_reader :configuration, :build
27
+
28
+ def initialize(build, configuration)
29
+ @configuration = configuration
30
+ @build = build
31
+ end
32
+
33
+ def to_before_script
34
+ a = []
35
+ a << "\n# init"
36
+ a += env.init
37
+
38
+ a << "\n# before install"
39
+ a += env.before_install
40
+
41
+ a << "\n# announce"
42
+ a += env.announce
43
+
44
+ a << "\n# install"
45
+ a += env.install
46
+
47
+ a << "\n# before script"
48
+ a += env.before_script
49
+ a.join("\n")
50
+ end
51
+
52
+ def to_after_script
53
+ a = []
54
+ a << "\n# after script"
55
+ a += env.after_script
56
+ a.join("\n")
57
+ end
58
+
59
+ def to_script
60
+ a = []
61
+ a << "\n# script"
62
+ a += env.script
63
+ a.join("\n")
64
+ end
65
+
66
+ private
67
+
68
+ def env
69
+ @env ||= run_middlewares(default_env) {|_| _ }
70
+ end
71
+
72
+ def default_env
73
+ OpenStruct.new(
74
+ # initialization, repo does not exists
75
+ init: [],
76
+
77
+ # before instalation, using for system setup
78
+ before_install: [],
79
+
80
+ # instalation, using for application setup
81
+ install: [],
82
+
83
+ # announce software and services version
84
+ announce: [],
85
+
86
+ before_script: [],
87
+ script: [],
88
+ after_script: [],
89
+
90
+ configuration: configuration,
91
+ build: build,
92
+ cache_key: []
93
+ )
94
+ end
95
+
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,28 @@
1
+ module Vx
2
+ class Router
3
+ class ScriptBuilder
4
+
5
+ Databases = Struct.new(:app) do
6
+
7
+ def call(env)
8
+ psql = []
9
+ psql << %{sudo -u postgres psql -q -h localhost -c "create role ci with login superuser password 'ci'" || true}
10
+ psql << %{sudo -u postgres psql -q -h localhost -c "create database ci" || true}
11
+
12
+ mysql = []
13
+
14
+ mysql << %{echo "CREATE USER 'ci'@'localhost';" | mysql -u root }
15
+ mysql << %{echo "GRANT ALL PRIVILEGES ON *.* TO 'ci'@'localhost';" | mysql -u root }
16
+
17
+ env.init.tap do |i|
18
+ psql.each {|c| i << c }
19
+ mysql.each {|c| i << c }
20
+ end
21
+
22
+ app.call(env)
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,21 @@
1
+ module Vx
2
+ class Router
3
+ class ScriptBuilder
4
+
5
+ Env = Struct.new(:app) do
6
+
7
+ include Helper::TraceShCommand
8
+
9
+ def call(env)
10
+ env.init << 'export LC_ALL=en_US.UTF8'
11
+ env.configuration.global_env.each do |e|
12
+ env.init << trace_sh_command("export #{e}")
13
+ end
14
+ env.announce << trace_sh_command("env")
15
+ app.call(env)
16
+ end
17
+
18
+ end
19
+ end
20
+ end
21
+ end