bricolage 5.8.7

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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +4 -0
  3. data/bin/bricolage +6 -0
  4. data/bin/bricolage-jobnet +6 -0
  5. data/jobclass/create.rb +21 -0
  6. data/jobclass/exec.rb +17 -0
  7. data/jobclass/insert-delta.rb +31 -0
  8. data/jobclass/insert.rb +33 -0
  9. data/jobclass/load.rb +39 -0
  10. data/jobclass/my-export.rb +40 -0
  11. data/jobclass/my-migrate.rb +103 -0
  12. data/jobclass/noop.rb +13 -0
  13. data/jobclass/rebuild-drop.rb +37 -0
  14. data/jobclass/rebuild-rename.rb +49 -0
  15. data/jobclass/s3-put.rb +19 -0
  16. data/jobclass/sql.rb +29 -0
  17. data/jobclass/td-delete.rb +20 -0
  18. data/jobclass/td-export.rb +30 -0
  19. data/jobclass/unload.rb +30 -0
  20. data/jobclass/wait-file.rb +48 -0
  21. data/lib/bricolage/application.rb +260 -0
  22. data/lib/bricolage/commandutils.rb +52 -0
  23. data/lib/bricolage/configloader.rb +126 -0
  24. data/lib/bricolage/context.rb +108 -0
  25. data/lib/bricolage/datasource.rb +144 -0
  26. data/lib/bricolage/eventhandlers.rb +47 -0
  27. data/lib/bricolage/exception.rb +47 -0
  28. data/lib/bricolage/filedatasource.rb +42 -0
  29. data/lib/bricolage/filesystem.rb +165 -0
  30. data/lib/bricolage/genericdatasource.rb +37 -0
  31. data/lib/bricolage/job.rb +212 -0
  32. data/lib/bricolage/jobclass.rb +98 -0
  33. data/lib/bricolage/jobfile.rb +100 -0
  34. data/lib/bricolage/jobflow.rb +389 -0
  35. data/lib/bricolage/jobnetrunner.rb +264 -0
  36. data/lib/bricolage/jobresult.rb +74 -0
  37. data/lib/bricolage/logger.rb +52 -0
  38. data/lib/bricolage/mysqldatasource.rb +223 -0
  39. data/lib/bricolage/parameters.rb +653 -0
  40. data/lib/bricolage/postgresconnection.rb +78 -0
  41. data/lib/bricolage/psqldatasource.rb +449 -0
  42. data/lib/bricolage/resource.rb +68 -0
  43. data/lib/bricolage/rubyjobclass.rb +42 -0
  44. data/lib/bricolage/s3datasource.rb +144 -0
  45. data/lib/bricolage/script.rb +120 -0
  46. data/lib/bricolage/sqlstatement.rb +351 -0
  47. data/lib/bricolage/taskqueue.rb +156 -0
  48. data/lib/bricolage/tddatasource.rb +116 -0
  49. data/lib/bricolage/variables.rb +208 -0
  50. data/lib/bricolage/version.rb +4 -0
  51. data/lib/bricolage.rb +8 -0
  52. data/libexec/sqldump +9 -0
  53. data/libexec/sqldump.Darwin +0 -0
  54. data/libexec/sqldump.Linux +0 -0
  55. data/test/all.rb +3 -0
  56. data/test/home/config/development/database.yml +57 -0
  57. data/test/home/config/development/password.yml +2 -0
  58. data/test/home/subsys/separated.job +1 -0
  59. data/test/home/subsys/separated.sql +1 -0
  60. data/test/home/subsys/unified.jobnet +1 -0
  61. data/test/home/subsys/unified.sql.job +5 -0
  62. data/test/test_filesystem.rb +19 -0
  63. data/test/test_parameters.rb +401 -0
  64. data/test/test_variables.rb +114 -0
  65. metadata +192 -0
@@ -0,0 +1,116 @@
1
+ require 'bricolage/datasource'
2
+ require 'bricolage/commandutils'
3
+ require 'stringio'
4
+ require 'date'
5
+
6
+ module Bricolage
7
+
8
+ class TDDataSource < DataSource
9
+ declare_type 'td'
10
+
11
+ include CommandUtils
12
+
13
+ def initialize(database: nil, username: nil, apikey: nil, td: 'td', priority: -2)
14
+ @database = database
15
+ @apikey = apikey
16
+ @td = td
17
+ @priority = priority
18
+ end
19
+
20
+ def new_task
21
+ TDTask.new(self)
22
+ end
23
+
24
+ def query_command_args(*args)
25
+ [@td, "--apikey=#{@apikey}", "query", "--database=#{@database}", "--wait"] + args
26
+ end
27
+
28
+ def delete_command_args(*args)
29
+ [@td, "--apikey=#{@apikey}", "table:partial_delete", @database, *args, "--wait"]
30
+ end
31
+
32
+ def exec(*args)
33
+ JobResult.for_process_status(command(*args))
34
+ end
35
+ end
36
+
37
+ class TDTask < DataSourceTask
38
+ def export(stmt, path: nil, **opts)
39
+ add TDTask::Export.new(stmt, path: path, **opts)
40
+ end
41
+
42
+ class Export < Action
43
+ include CommandUtils
44
+
45
+ def initialize(stmt, path: nil, format: nil, gzip: false, override: false)
46
+ @statement = stmt
47
+ @path = path
48
+ @format = format
49
+ @gzip = gzip
50
+ @override = override
51
+ end
52
+
53
+ def_delegator '@statement', :bind
54
+
55
+ def source
56
+ buf = StringIO.new
57
+ buf.puts command_args(new_tmpfile_path).join(' ') + " <<EndTDSQL"
58
+ buf.puts @statement.source
59
+ buf.puts 'EndTDSQL'
60
+ buf.string
61
+ end
62
+
63
+ GZIP_COMMAND = 'gzip' # FIXME: parameterize
64
+
65
+ def run
66
+ if File.exist?(@path) and not @override
67
+ raise JobFailure, "target file exists: #{@path.inspect}"
68
+ end
69
+ puts @statement.source
70
+ td_result = make_tmpfile(@statement.source) {|query_path|
71
+ ds.exec(*command_args(query_path))
72
+ }
73
+ if @gzip
74
+ gzip_result = ds.command(GZIP_COMMAND, export_path)
75
+ raise JobFailure, "gzip failed" unless gzip_result.success?
76
+ end
77
+ td_result
78
+ end
79
+
80
+ def command_args(query_path)
81
+ ds.query_command_args("--query=#{query_path}", "--output=#{export_path}", "--format=#{@format}")
82
+ end
83
+
84
+ def export_path
85
+ @gzip ? @path.sub(/\.gz\z/, '') : @path
86
+ end
87
+ end
88
+
89
+ def delete(table_name, **opts)
90
+ add TDTask::Delete.new(table_name, **opts)
91
+ end
92
+
93
+ class Delete < Action
94
+ def initialize(table_name, from:, to:)
95
+ @table = table_name
96
+ @from = from.to_time.to_i
97
+ @to = to.to_time.to_i
98
+ end
99
+
100
+ def run
101
+ td_result = ds.exec(source)
102
+ end
103
+
104
+ def command_args
105
+ ds.delete_command_args(@table, "--from #{@from}", "--to #{@to}")
106
+ end
107
+
108
+ def source
109
+ buf = StringIO.new
110
+ buf.puts command_args.join(' ')
111
+ buf.string
112
+ end
113
+ end
114
+ end
115
+
116
+ end
@@ -0,0 +1,208 @@
1
+ require 'bricolage/exception'
2
+
3
+ module Bricolage
4
+
5
+ class Variables
6
+ def Variables.union(*vars_list)
7
+ new.tap {|result|
8
+ vars_list.each do |vars|
9
+ result.update vars
10
+ end
11
+ }
12
+ end
13
+
14
+ def Variables.define
15
+ new.tap {|vars|
16
+ yield vars
17
+ }
18
+ end
19
+
20
+ def initialize
21
+ @vars = {}
22
+ end
23
+
24
+ def inspect
25
+ "\#<#{self.class} #{@vars.inspect}>"
26
+ end
27
+
28
+ def resolved?
29
+ false
30
+ end
31
+
32
+ def each_variable(&block)
33
+ @vars.each_value(&block)
34
+ end
35
+
36
+ def [](name)
37
+ var = @vars[name] or raise ParameterError, "no such variable: #{name}"
38
+ var.value
39
+ end
40
+
41
+ def []=(name, value)
42
+ add Variable.new(name, value)
43
+ end
44
+
45
+ def keys
46
+ @vars.keys
47
+ end
48
+
49
+ def add(var)
50
+ @vars[var.name] = var
51
+ end
52
+
53
+ def update(vars)
54
+ vars.each_variable do |var|
55
+ add var
56
+ end
57
+ end
58
+
59
+ # resolve self recursively
60
+ def resolve
61
+ ResolvedVariables.define {|resolved|
62
+ @vars.each_value do |var|
63
+ resolved.add do_expand_variable(var, resolved, {})
64
+ end
65
+ }
66
+ end
67
+
68
+ def do_expand_variable(var, resolved, seen)
69
+ if seen[var.name]
70
+ cycle = (seen.keys + [var.name]).join(', ')
71
+ raise ParameterError, "recursive variable reference: #{var.name} (#{cycle})"
72
+ end
73
+ seen[var.name] = true
74
+ if var.resolved?
75
+ var
76
+ else
77
+ value = Variable.expand_string(var.value.to_s) {|name|
78
+ if resolved.bound?(name)
79
+ resolved[name]
80
+ elsif var2 = @vars[name]
81
+ res_var2 = do_expand_variable(var2, resolved, seen)
82
+ resolved.add res_var2
83
+ res_var2.value
84
+ else
85
+ raise ParameterError, "undefined variable in parameter #{var.name}: $#{name}"
86
+ end
87
+ }
88
+ ResolvedVariable.new(var.name, value)
89
+ end
90
+ end
91
+ private :do_expand_variable
92
+
93
+ def resolve_with(resolved)
94
+ raise "[BUG] unresolved variables given" unless resolved.resolved?
95
+ ResolvedVariables.define {|result|
96
+ each_variable do |var|
97
+ if var.resolved?
98
+ result.add var
99
+ else
100
+ val = resolved.expand(var.value.to_s)
101
+ result.add ResolvedVariable.new(var.name, val)
102
+ end
103
+ end
104
+ }
105
+ end
106
+ end
107
+
108
+ class ResolvedVariables
109
+ def ResolvedVariables.define
110
+ new.tap {|vars|
111
+ yield vars
112
+ vars.freeze
113
+ }
114
+ end
115
+
116
+ def initialize
117
+ @vars = {}
118
+ end
119
+
120
+ def inspect
121
+ "\#<#{self.class} #{@vars.inspect}>"
122
+ end
123
+
124
+ def resolved?
125
+ true
126
+ end
127
+
128
+ def freeze
129
+ @vars.freeze
130
+ super
131
+ end
132
+
133
+ def add(var)
134
+ raise "[BUG] unresolved variable: #{var.name}" unless var.resolved?
135
+ @vars[var.name] = var
136
+ end
137
+
138
+ def resolve_update(unresolved)
139
+ raise "[BUG] already resolved variables given" if unresolved.resolved?
140
+ unresolved.resolve_with(self).each_variable do |var|
141
+ add var
142
+ end
143
+ end
144
+
145
+ def bound?(name)
146
+ @vars.key?(name.to_s)
147
+ end
148
+
149
+ def [](name)
150
+ var = @vars[name.to_s] or raise ParameterError, "undefined parameter: #{name}"
151
+ var.value
152
+ end
153
+
154
+ def keys
155
+ @vars.keys
156
+ end
157
+
158
+ def each_variable(&block)
159
+ @vars.each_value(&block)
160
+ end
161
+
162
+ def expand(str)
163
+ Variable.expand_string(str) {|name| self[name] }
164
+ end
165
+
166
+ def bind_declarations(decls)
167
+ decls.each do |decl|
168
+ unless bound?(decl.name)
169
+ raise ParameterError, "script parameter not given: #{decl.name}"
170
+ end
171
+ end
172
+ end
173
+ end
174
+
175
+ class Variable
176
+ # generic variable extractor
177
+ def Variable.expand_string(str)
178
+ str.gsub(/\$(\w+)|\$\{(\w+)\}/) { yield ($1 || $2) }
179
+ end
180
+
181
+ def Variable.list(str)
182
+ str.scan(/\$(\w+)|\$\{(\w+)\}/).flatten.compact.uniq
183
+ end
184
+
185
+ def initialize(name, value)
186
+ @name = name
187
+ @value = value
188
+ end
189
+
190
+ attr_reader :name
191
+ attr_reader :value
192
+
193
+ def resolved?
194
+ false
195
+ end
196
+
197
+ def inspect
198
+ "\#<#{self.class} #{@name}=#{@value.inspect}>"
199
+ end
200
+ end
201
+
202
+ class ResolvedVariable < Variable
203
+ def resolved?
204
+ true
205
+ end
206
+ end
207
+
208
+ end
@@ -0,0 +1,4 @@
1
+ module Bricolage
2
+ APPLICATION_NAME = 'Bricolage'
3
+ VERSION = '5.8.7'
4
+ end
data/lib/bricolage.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'bricolage/context'
2
+ require 'bricolage/filesystem'
3
+ require 'bricolage/job'
4
+ require 'bricolage/rubyjobclass'
5
+ require 'bricolage/datasource'
6
+ require 'bricolage/eventhandlers'
7
+ require 'bricolage/logger'
8
+ require 'bricolage/exception'
data/libexec/sqldump ADDED
@@ -0,0 +1,9 @@
1
+ #!/bin/sh
2
+
3
+ binary="$0.$(uname -s)"
4
+ if ! [[ -x $binary ]]
5
+ then
6
+ echo "$0: error: sqldump does not support $(uname -s)" 1>&2
7
+ exit 1
8
+ fi
9
+ exec "$binary" "$@"
Binary file
Binary file
data/test/all.rb ADDED
@@ -0,0 +1,3 @@
1
+ Dir.glob("#{__dir__}/test_*.rb").each do |path|
2
+ load path
3
+ end
@@ -0,0 +1,57 @@
1
+ sql: &redshift_parameters
2
+ type: psql
3
+ host: <%= ENV['RSPROXYHOST'] ? 'localhost' : (ENV['RSHOST'] || 'cookpad-dwh-201.cbzwjkf60uat.ap-northeast-1.redshift.amazonaws.com') %>
4
+ port: <%= ENV['RSPROXYPORT'] || ENV['RSPORT'] || 5439 %>
5
+ database: production
6
+ username: tabemirudev
7
+ pgpass: <%= user_home_relative_path '.pgpass' %>
8
+ encoding: utf8
9
+
10
+ sql_dev:
11
+ <<: *redshift_parameters
12
+
13
+ sql_prod:
14
+ <<: *redshift_parameters
15
+ username: tabemiru_batch
16
+
17
+ sql_app:
18
+ type: psql
19
+ host: localhost #cookpad-dwh-101.cbzwjkf60uat.ap-northeast-1.redshift.amazonaws.com
20
+ port: 5445
21
+ database: production
22
+ username: tabemiru_batch
23
+ pgpass: <%= user_home_relative_path '.pgpass' %>
24
+ encoding: utf8
25
+
26
+ sql_app2:
27
+ type: psql
28
+ host: localhost
29
+ port: 5446
30
+ database: production
31
+ username: tabemiru_batch
32
+ pgpass: <%= user_home_relative_path '.pgpass' %>
33
+ encoding: utf8
34
+
35
+ td:
36
+ type: td
37
+ database: logs
38
+ apikey: <%= password 'td_tabemiru' %>
39
+
40
+ td_search_log:
41
+ type: td
42
+ database: search_log
43
+ apikey: <%= password 'td_tabemiru' %>
44
+
45
+ s3:
46
+ type: s3
47
+ bucket: tabemiru-data.ap-northeast-1
48
+ prefix: "/dev"
49
+ s3cfg: <%= user_home_relative_path '.s3cfg' %>
50
+
51
+ mysql:
52
+ type: mysql
53
+ host: db-main-slave-free-001
54
+ database: main
55
+ username: work_readonly
56
+ password: <%= password 'mysql_local_work_readonly' %>
57
+ encoding: utf8
@@ -0,0 +1,2 @@
1
+ td_tabemiru: "121/c1376bb5e3972fc4a4efc942c1df100f7bc156e0"
2
+ mysql_local_work_readonly: "ckpdtest"
@@ -0,0 +1 @@
1
+ class: sql
@@ -0,0 +1 @@
1
+ select 1;
@@ -0,0 +1 @@
1
+ unified
@@ -0,0 +1,5 @@
1
+ /*
2
+ class: sql
3
+ */
4
+
5
+ select 1;
@@ -0,0 +1,19 @@
1
+ require 'test/unit'
2
+ require 'bricolage/filesystem'
3
+ require 'pathname'
4
+ require 'pp'
5
+
6
+ module Bricolage
7
+ class TestFileSystem < Test::Unit::TestCase
8
+ fs = FileSystem.new("#{__dir__}/home", "test")
9
+ subfs = fs.subsystem('subsys')
10
+
11
+ test "FileSystem.job_file" do
12
+ path = subfs.job_file('unified')
13
+ assert_instance_of Pathname, path
14
+ assert_equal subfs.relative('unified.sql.job'), path
15
+
16
+ assert_equal subfs.relative('separated.job'), subfs.job_file('separated')
17
+ end
18
+ end
19
+ end