masamune 0.11.0
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/LICENSE.txt +21 -0
- data/README.md +54 -0
- data/Rakefile +15 -0
- data/bin/masamune-elastic-mapreduce +4 -0
- data/bin/masamune-hive +4 -0
- data/bin/masamune-psql +4 -0
- data/bin/masamune-shell +4 -0
- data/lib/masamune.rb +56 -0
- data/lib/masamune/accumulate.rb +60 -0
- data/lib/masamune/actions.rb +38 -0
- data/lib/masamune/actions/data_flow.rb +131 -0
- data/lib/masamune/actions/date_parse.rb +75 -0
- data/lib/masamune/actions/elastic_mapreduce.rb +68 -0
- data/lib/masamune/actions/execute.rb +52 -0
- data/lib/masamune/actions/filesystem.rb +37 -0
- data/lib/masamune/actions/hadoop_filesystem.rb +40 -0
- data/lib/masamune/actions/hadoop_streaming.rb +41 -0
- data/lib/masamune/actions/hive.rb +74 -0
- data/lib/masamune/actions/postgres.rb +76 -0
- data/lib/masamune/actions/postgres_admin.rb +34 -0
- data/lib/masamune/actions/s3cmd.rb +44 -0
- data/lib/masamune/actions/transform.rb +89 -0
- data/lib/masamune/after_initialize_callbacks.rb +55 -0
- data/lib/masamune/cached_filesystem.rb +110 -0
- data/lib/masamune/commands.rb +37 -0
- data/lib/masamune/commands/elastic_mapreduce.rb +119 -0
- data/lib/masamune/commands/hadoop_filesystem.rb +57 -0
- data/lib/masamune/commands/hadoop_streaming.rb +116 -0
- data/lib/masamune/commands/hive.rb +178 -0
- data/lib/masamune/commands/interactive.rb +37 -0
- data/lib/masamune/commands/postgres.rb +128 -0
- data/lib/masamune/commands/postgres_admin.rb +72 -0
- data/lib/masamune/commands/postgres_common.rb +33 -0
- data/lib/masamune/commands/retry_with_backoff.rb +60 -0
- data/lib/masamune/commands/s3cmd.rb +70 -0
- data/lib/masamune/commands/shell.rb +202 -0
- data/lib/masamune/configuration.rb +195 -0
- data/lib/masamune/data_plan.rb +31 -0
- data/lib/masamune/data_plan/builder.rb +66 -0
- data/lib/masamune/data_plan/elem.rb +190 -0
- data/lib/masamune/data_plan/engine.rb +162 -0
- data/lib/masamune/data_plan/rule.rb +292 -0
- data/lib/masamune/data_plan/set.rb +176 -0
- data/lib/masamune/environment.rb +164 -0
- data/lib/masamune/filesystem.rb +567 -0
- data/lib/masamune/has_environment.rb +40 -0
- data/lib/masamune/helpers.rb +27 -0
- data/lib/masamune/helpers/postgres.rb +84 -0
- data/lib/masamune/io.rb +33 -0
- data/lib/masamune/last_element.rb +53 -0
- data/lib/masamune/method_logger.rb +41 -0
- data/lib/masamune/multi_io.rb +39 -0
- data/lib/masamune/schema.rb +36 -0
- data/lib/masamune/schema/catalog.rb +233 -0
- data/lib/masamune/schema/column.rb +527 -0
- data/lib/masamune/schema/dimension.rb +133 -0
- data/lib/masamune/schema/event.rb +121 -0
- data/lib/masamune/schema/fact.rb +133 -0
- data/lib/masamune/schema/map.rb +265 -0
- data/lib/masamune/schema/row.rb +133 -0
- data/lib/masamune/schema/store.rb +115 -0
- data/lib/masamune/schema/table.rb +308 -0
- data/lib/masamune/schema/table_reference.rb +76 -0
- data/lib/masamune/spec_helper.rb +23 -0
- data/lib/masamune/string_format.rb +34 -0
- data/lib/masamune/tasks/elastic_mapreduce_thor.rb +60 -0
- data/lib/masamune/tasks/hive_thor.rb +55 -0
- data/lib/masamune/tasks/postgres_thor.rb +47 -0
- data/lib/masamune/tasks/shell_thor.rb +63 -0
- data/lib/masamune/template.rb +77 -0
- data/lib/masamune/thor.rb +186 -0
- data/lib/masamune/thor_loader.rb +38 -0
- data/lib/masamune/topological_hash.rb +34 -0
- data/lib/masamune/transform.rb +47 -0
- data/lib/masamune/transform/bulk_upsert.psql.erb +64 -0
- data/lib/masamune/transform/bulk_upsert.rb +52 -0
- data/lib/masamune/transform/consolidate_dimension.rb +54 -0
- data/lib/masamune/transform/deduplicate_dimension.psql.erb +52 -0
- data/lib/masamune/transform/deduplicate_dimension.rb +53 -0
- data/lib/masamune/transform/define_event_view.hql.erb +51 -0
- data/lib/masamune/transform/define_event_view.rb +60 -0
- data/lib/masamune/transform/define_index.psql.erb +34 -0
- data/lib/masamune/transform/define_schema.hql.erb +23 -0
- data/lib/masamune/transform/define_schema.psql.erb +79 -0
- data/lib/masamune/transform/define_schema.rb +56 -0
- data/lib/masamune/transform/define_table.hql.erb +34 -0
- data/lib/masamune/transform/define_table.psql.erb +95 -0
- data/lib/masamune/transform/define_table.rb +40 -0
- data/lib/masamune/transform/define_unique.psql.erb +30 -0
- data/lib/masamune/transform/insert_reference_values.psql.erb +43 -0
- data/lib/masamune/transform/insert_reference_values.rb +64 -0
- data/lib/masamune/transform/load_dimension.rb +47 -0
- data/lib/masamune/transform/load_fact.rb +45 -0
- data/lib/masamune/transform/operator.rb +96 -0
- data/lib/masamune/transform/relabel_dimension.psql.erb +76 -0
- data/lib/masamune/transform/relabel_dimension.rb +39 -0
- data/lib/masamune/transform/rollup_fact.psql.erb +79 -0
- data/lib/masamune/transform/rollup_fact.rb +149 -0
- data/lib/masamune/transform/snapshot_dimension.psql.erb +75 -0
- data/lib/masamune/transform/snapshot_dimension.rb +74 -0
- data/lib/masamune/transform/stage_dimension.psql.erb +39 -0
- data/lib/masamune/transform/stage_dimension.rb +83 -0
- data/lib/masamune/transform/stage_fact.psql.erb +80 -0
- data/lib/masamune/transform/stage_fact.rb +111 -0
- data/lib/masamune/version.rb +25 -0
- data/spec/fixtures/aggregate.sql.erb +25 -0
- data/spec/fixtures/comment.sql.erb +27 -0
- data/spec/fixtures/invalid.sql.erb +23 -0
- data/spec/fixtures/relative.sql.erb +23 -0
- data/spec/fixtures/simple.sql.erb +28 -0
- data/spec/fixtures/whitespace.sql.erb +30 -0
- data/spec/masamune/actions/elastic_mapreduce_spec.rb +108 -0
- data/spec/masamune/actions/execute_spec.rb +50 -0
- data/spec/masamune/actions/hadoop_filesystem_spec.rb +44 -0
- data/spec/masamune/actions/hadoop_streaming_spec.rb +74 -0
- data/spec/masamune/actions/hive_spec.rb +117 -0
- data/spec/masamune/actions/postgres_admin_spec.rb +58 -0
- data/spec/masamune/actions/postgres_spec.rb +134 -0
- data/spec/masamune/actions/s3cmd_spec.rb +44 -0
- data/spec/masamune/actions/transform_spec.rb +144 -0
- data/spec/masamune/after_initialization_callbacks_spec.rb +61 -0
- data/spec/masamune/cached_filesystem_spec.rb +167 -0
- data/spec/masamune/commands/hadoop_filesystem_spec.rb +50 -0
- data/spec/masamune/commands/hadoop_streaming_spec.rb +106 -0
- data/spec/masamune/commands/hive_spec.rb +117 -0
- data/spec/masamune/commands/postgres_admin_spec.rb +69 -0
- data/spec/masamune/commands/postgres_spec.rb +100 -0
- data/spec/masamune/commands/retry_with_backoff_spec.rb +116 -0
- data/spec/masamune/commands/s3cmd_spec.rb +50 -0
- data/spec/masamune/commands/shell_spec.rb +101 -0
- data/spec/masamune/configuration_spec.rb +102 -0
- data/spec/masamune/data_plan/builder_spec.rb +91 -0
- data/spec/masamune/data_plan/elem_spec.rb +102 -0
- data/spec/masamune/data_plan/engine_spec.rb +356 -0
- data/spec/masamune/data_plan/rule_spec.rb +407 -0
- data/spec/masamune/data_plan/set_spec.rb +517 -0
- data/spec/masamune/environment_spec.rb +65 -0
- data/spec/masamune/filesystem_spec.rb +1421 -0
- data/spec/masamune/helpers/postgres_spec.rb +95 -0
- data/spec/masamune/schema/catalog_spec.rb +613 -0
- data/spec/masamune/schema/column_spec.rb +696 -0
- data/spec/masamune/schema/dimension_spec.rb +137 -0
- data/spec/masamune/schema/event_spec.rb +75 -0
- data/spec/masamune/schema/fact_spec.rb +117 -0
- data/spec/masamune/schema/map_spec.rb +593 -0
- data/spec/masamune/schema/row_spec.rb +28 -0
- data/spec/masamune/schema/store_spec.rb +49 -0
- data/spec/masamune/schema/table_spec.rb +395 -0
- data/spec/masamune/string_format_spec.rb +60 -0
- data/spec/masamune/tasks/elastic_mapreduce_thor_spec.rb +57 -0
- data/spec/masamune/tasks/hive_thor_spec.rb +75 -0
- data/spec/masamune/tasks/postgres_thor_spec.rb +42 -0
- data/spec/masamune/tasks/shell_thor_spec.rb +51 -0
- data/spec/masamune/template_spec.rb +77 -0
- data/spec/masamune/thor_spec.rb +238 -0
- data/spec/masamune/transform/bulk_upsert.dimension_spec.rb +200 -0
- data/spec/masamune/transform/consolidate_dimension_spec.rb +62 -0
- data/spec/masamune/transform/deduplicate_dimension_spec.rb +84 -0
- data/spec/masamune/transform/define_event_view_spec.rb +84 -0
- data/spec/masamune/transform/define_schema_spec.rb +83 -0
- data/spec/masamune/transform/define_table.dimension_spec.rb +306 -0
- data/spec/masamune/transform/define_table.fact_spec.rb +291 -0
- data/spec/masamune/transform/define_table.table_spec.rb +525 -0
- data/spec/masamune/transform/insert_reference_values.dimension_spec.rb +111 -0
- data/spec/masamune/transform/insert_reference_values.fact_spec.rb +149 -0
- data/spec/masamune/transform/load_dimension_spec.rb +76 -0
- data/spec/masamune/transform/load_fact_spec.rb +89 -0
- data/spec/masamune/transform/relabel_dimension_spec.rb +102 -0
- data/spec/masamune/transform/rollup_fact_spec.rb +333 -0
- data/spec/masamune/transform/snapshot_dimension_spec.rb +103 -0
- data/spec/masamune/transform/stage_dimension_spec.rb +115 -0
- data/spec/masamune/transform/stage_fact_spec.rb +204 -0
- data/spec/masamune_spec.rb +32 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/support/masamune/example_group.rb +36 -0
- data/spec/support/masamune/mock_command.rb +99 -0
- data/spec/support/masamune/mock_delegate.rb +51 -0
- data/spec/support/masamune/mock_filesystem.rb +96 -0
- data/spec/support/masamune/thor_mute.rb +35 -0
- data/spec/support/rspec/example/action_example_group.rb +34 -0
- data/spec/support/rspec/example/task_example_group.rb +80 -0
- data/spec/support/rspec/example/transform_example_group.rb +36 -0
- data/spec/support/shared_examples/postgres_common_examples.rb +53 -0
- metadata +462 -0
@@ -0,0 +1,72 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2014-2015, VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'delegate'
|
24
|
+
|
25
|
+
module Masamune::Commands
|
26
|
+
class PostgresAdmin < SimpleDelegator
|
27
|
+
include Masamune::Commands::PostgresCommon
|
28
|
+
|
29
|
+
DEFAULT_ATTRIBUTES =
|
30
|
+
{
|
31
|
+
:create_db_path => 'createdb',
|
32
|
+
:drop_db_path => 'dropdb',
|
33
|
+
:options => [],
|
34
|
+
:hostname => 'localhost',
|
35
|
+
:username => 'postgres',
|
36
|
+
:pgpass_file => nil,
|
37
|
+
:action => nil,
|
38
|
+
:database => nil
|
39
|
+
}
|
40
|
+
|
41
|
+
def initialize(delegate, attrs = {})
|
42
|
+
super delegate
|
43
|
+
DEFAULT_ATTRIBUTES.merge(configuration.postgres).merge(configuration.postgres_admin).merge(attrs).each do |name, value|
|
44
|
+
instance_variable_set("@#{name}", value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def command_args
|
49
|
+
raise ArgumentError, ':database must be given' unless @database
|
50
|
+
args = []
|
51
|
+
args << command_path
|
52
|
+
args << '--host=%s' % @hostname if @hostname
|
53
|
+
args << '--username=%s' % @username if @username
|
54
|
+
args << '--no-password'
|
55
|
+
args << @database
|
56
|
+
args.flatten.compact
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def command_path
|
62
|
+
case @action
|
63
|
+
when :create
|
64
|
+
[@create_db_path]
|
65
|
+
when :drop
|
66
|
+
[@drop_db_path, '--if-exists']
|
67
|
+
else
|
68
|
+
raise ArgumentError, ':action must be :create or :drop'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2014-2015, VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
module Masamune::Commands
|
24
|
+
module PostgresCommon
|
25
|
+
def command_env
|
26
|
+
{ 'PGOPTIONS'=> '--client-min-messages=warning' }.tap do |env|
|
27
|
+
if @pgpass_file && File.readable?(@pgpass_file)
|
28
|
+
env['PGPASSFILE'] = @pgpass_file
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2014-2015, VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'delegate'
|
24
|
+
|
25
|
+
module Masamune::Commands
|
26
|
+
class RetryWithBackoff < SimpleDelegator
|
27
|
+
MAX_RETRY_EXIT_STATUS = 8
|
28
|
+
|
29
|
+
def initialize(delegate, attrs = {})
|
30
|
+
super delegate
|
31
|
+
@delegate = delegate
|
32
|
+
@retries = attrs.fetch(:retries, configuration.retries)
|
33
|
+
@backoff = attrs.fetch(:backoff, configuration.backoff)
|
34
|
+
@retry_count = 0
|
35
|
+
end
|
36
|
+
|
37
|
+
def around_execute(&block)
|
38
|
+
begin
|
39
|
+
status = if @delegate.respond_to?(:around_execute)
|
40
|
+
@delegate.around_execute(&block)
|
41
|
+
else
|
42
|
+
block.call
|
43
|
+
end
|
44
|
+
raise "exited with code: #{status.exitstatus}" unless status.success?
|
45
|
+
status
|
46
|
+
rescue => e
|
47
|
+
logger.error(e.to_s)
|
48
|
+
sleep @backoff
|
49
|
+
@retry_count += 1
|
50
|
+
unless @retry_count > @retries
|
51
|
+
logger.debug("retrying (#{@retry_count}/#{@retries})")
|
52
|
+
retry
|
53
|
+
else
|
54
|
+
logger.debug("max retries (#{@retries}) attempted, bailing")
|
55
|
+
OpenStruct.new(:success? => false, :exitstatus => MAX_RETRY_EXIT_STATUS)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2014-2015, VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'delegate'
|
24
|
+
|
25
|
+
module Masamune::Commands
|
26
|
+
class S3Cmd < SimpleDelegator
|
27
|
+
DEFAULT_ATTRIBUTES =
|
28
|
+
{
|
29
|
+
:path => 's3cmd',
|
30
|
+
:options => [],
|
31
|
+
:extra => [],
|
32
|
+
:block => nil
|
33
|
+
}
|
34
|
+
|
35
|
+
def initialize(delegate, attrs = {})
|
36
|
+
super delegate
|
37
|
+
DEFAULT_ATTRIBUTES.merge(configuration.s3cmd).merge(attrs).each do |name, value|
|
38
|
+
instance_variable_set("@#{name}", value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def command_args
|
43
|
+
args = []
|
44
|
+
args << @path
|
45
|
+
args << @options.map(&:to_a)
|
46
|
+
args << @extra
|
47
|
+
args.flatten
|
48
|
+
end
|
49
|
+
|
50
|
+
def handle_stdout(line, line_no)
|
51
|
+
@block.call(line) if @block
|
52
|
+
end
|
53
|
+
|
54
|
+
module ClassMethods
|
55
|
+
def s3n(file, options = {})
|
56
|
+
file.dup.tap do |out|
|
57
|
+
out.sub!(%r{\As3://}, 's3n://')
|
58
|
+
out.sub!(%r{/?\z}, '/') if options[:dir]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def s3b(file, options = {})
|
63
|
+
file.dup.tap do |out|
|
64
|
+
out.sub!(%r{\As3n://}, 's3://')
|
65
|
+
out.sub!(%r{/?\z}, '/') if options[:dir]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,202 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2014-2015, VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'open3'
|
24
|
+
require 'readline'
|
25
|
+
require 'ostruct'
|
26
|
+
require 'delegate'
|
27
|
+
|
28
|
+
module Masamune::Commands
|
29
|
+
class Shell < SimpleDelegator
|
30
|
+
SIGINT_EXIT_STATUS = 130
|
31
|
+
PIPE_TIMEOUT = 10
|
32
|
+
|
33
|
+
attr_accessor :safe, :fail_fast
|
34
|
+
|
35
|
+
def initialize(delegate, opts = {})
|
36
|
+
super delegate
|
37
|
+
@delegate = delegate
|
38
|
+
self.safe = opts.fetch(:safe, false)
|
39
|
+
self.fail_fast = opts.fetch(:fail_fast, true)
|
40
|
+
@stdout_line_no = 0
|
41
|
+
@stderr_line_no = 0
|
42
|
+
end
|
43
|
+
|
44
|
+
def replace
|
45
|
+
logger.debug('replace: ' + command_args.join(' '))
|
46
|
+
before_execute
|
47
|
+
around_execute do
|
48
|
+
pid = Process.fork
|
49
|
+
if pid
|
50
|
+
STDIN.close; STDOUT.close; STDERR.close
|
51
|
+
Process.waitpid(pid)
|
52
|
+
exit
|
53
|
+
else
|
54
|
+
exec(command_env, *command_args)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def before_execute
|
60
|
+
logger.debug(command_args)
|
61
|
+
|
62
|
+
if configuration.verbose
|
63
|
+
trace(command_args)
|
64
|
+
end
|
65
|
+
|
66
|
+
if @delegate.respond_to?(:before_execute)
|
67
|
+
@delegate.before_execute
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def after_execute
|
72
|
+
if @delegate.respond_to?(:after_execute)
|
73
|
+
@delegate.after_execute
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def around_execute(&block)
|
78
|
+
if configuration.no_op && !safe
|
79
|
+
return OpenStruct.new(:success? => true)
|
80
|
+
end
|
81
|
+
|
82
|
+
if @delegate.respond_to?(:around_execute)
|
83
|
+
@delegate.around_execute(&block)
|
84
|
+
else
|
85
|
+
block.call
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def command_env
|
90
|
+
(@delegate.respond_to?(:command_env) ? @delegate.command_env : {}).merge('TZ' => 'UTC')
|
91
|
+
end
|
92
|
+
|
93
|
+
def command_args
|
94
|
+
if @delegate.respond_to?(:command_args)
|
95
|
+
@delegate.command_args
|
96
|
+
else
|
97
|
+
raise 'no command_args'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def command_bin
|
102
|
+
command_args.first
|
103
|
+
end
|
104
|
+
|
105
|
+
def execute
|
106
|
+
status = OpenStruct.new(:success? => false, :exitstatus => 1)
|
107
|
+
|
108
|
+
before_execute
|
109
|
+
status = around_execute do
|
110
|
+
execute_block
|
111
|
+
end
|
112
|
+
after_execute
|
113
|
+
|
114
|
+
unless status.success?
|
115
|
+
handle_failure(exit_code(status))
|
116
|
+
end
|
117
|
+
status
|
118
|
+
rescue Interrupt
|
119
|
+
handle_failure(SIGINT_EXIT_STATUS)
|
120
|
+
rescue SystemExit
|
121
|
+
handle_failure(exit_code(status))
|
122
|
+
end
|
123
|
+
|
124
|
+
def execute_block
|
125
|
+
Open3.popen3(command_env, *command_args) do |p_stdin, p_stdout, p_stderr, t_stdin|
|
126
|
+
p_stdin.wait_writable(PIPE_TIMEOUT) or raise "IO stdin not ready for write in #{PIPE_TIMEOUT}"
|
127
|
+
|
128
|
+
Thread.new {
|
129
|
+
if @delegate.respond_to?(:stdin)
|
130
|
+
@delegate.stdin.rewind
|
131
|
+
while line = @delegate.stdin.gets
|
132
|
+
trace(line.chomp)
|
133
|
+
p_stdin.puts line
|
134
|
+
p_stdin.flush
|
135
|
+
end
|
136
|
+
else
|
137
|
+
while !p_stdin.closed? do
|
138
|
+
input = Readline.readline('', true).strip
|
139
|
+
p_stdin.puts input
|
140
|
+
p_stdin.flush
|
141
|
+
end
|
142
|
+
end
|
143
|
+
p_stdin.close unless p_stdin.closed?
|
144
|
+
}
|
145
|
+
|
146
|
+
t_stderr = Thread.new {
|
147
|
+
while !p_stderr.eof? do
|
148
|
+
handle_stderr(p_stderr)
|
149
|
+
end
|
150
|
+
p_stderr.close unless p_stderr.closed?
|
151
|
+
}
|
152
|
+
|
153
|
+
t_stdout = Thread.new {
|
154
|
+
while !p_stdout.eof? do
|
155
|
+
handle_stdout(p_stdout)
|
156
|
+
end
|
157
|
+
p_stdout.close unless p_stdout.closed?
|
158
|
+
}
|
159
|
+
|
160
|
+
[t_stderr, t_stdout, t_stdin].compact.each { |t| t.join }
|
161
|
+
logger.debug(t_stdin.value)
|
162
|
+
t_stdin.value
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def handle_stdout(io)
|
167
|
+
line = io.gets.chomp
|
168
|
+
return unless line
|
169
|
+
if @delegate.respond_to?(:handle_stdout)
|
170
|
+
@delegate.handle_stdout(line, @stdout_line_no)
|
171
|
+
else
|
172
|
+
logger.debug(line)
|
173
|
+
end
|
174
|
+
@stdout_line_no += 1
|
175
|
+
end
|
176
|
+
|
177
|
+
def handle_stderr(io)
|
178
|
+
line = io.gets.chomp
|
179
|
+
return unless line
|
180
|
+
if @delegate.respond_to?(:handle_stderr)
|
181
|
+
@delegate.handle_stderr(line, @stderr_line_no)
|
182
|
+
else
|
183
|
+
logger.debug(line)
|
184
|
+
end
|
185
|
+
@stderr_line_no += 1
|
186
|
+
end
|
187
|
+
|
188
|
+
def handle_failure(status)
|
189
|
+
if @delegate.respond_to?(:handle_failure)
|
190
|
+
@delegate.handle_failure(status)
|
191
|
+
end
|
192
|
+
raise "fail_fast: #{command_args.join(' ')}" if fail_fast
|
193
|
+
end
|
194
|
+
|
195
|
+
private
|
196
|
+
|
197
|
+
def exit_code(status, code = 1)
|
198
|
+
return code unless status
|
199
|
+
status.exitstatus
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# The MIT License (MIT)
|
2
|
+
#
|
3
|
+
# Copyright (c) 2014-2015, VMware, Inc. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
10
|
+
# furnished to do so, subject to the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
13
|
+
# all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
# THE SOFTWARE.
|
22
|
+
|
23
|
+
require 'delegate'
|
24
|
+
require 'yaml'
|
25
|
+
require 'tilt/erb'
|
26
|
+
require 'pp'
|
27
|
+
|
28
|
+
require 'active_support/core_ext/hash'
|
29
|
+
|
30
|
+
class Masamune::Configuration
|
31
|
+
extend Forwardable
|
32
|
+
include Masamune::HasEnvironment
|
33
|
+
|
34
|
+
attr_accessor :quiet
|
35
|
+
attr_accessor :verbose
|
36
|
+
attr_accessor :debug
|
37
|
+
attr_accessor :no_op
|
38
|
+
attr_accessor :dry_run
|
39
|
+
attr_accessor :lock
|
40
|
+
attr_accessor :retries
|
41
|
+
attr_accessor :backoff
|
42
|
+
attr_accessor :params
|
43
|
+
|
44
|
+
COMMANDS = %w(hive hadoop_streaming hadoop_filesystem elastic_mapreduce s3cmd postgres postgres_admin)
|
45
|
+
COMMANDS.each do |command|
|
46
|
+
attr_accessor command
|
47
|
+
define_method(command) do
|
48
|
+
instance_variable_get("@#{command}").symbolize_keys!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(environment)
|
53
|
+
self.environment = environment
|
54
|
+
self.quiet = false
|
55
|
+
self.verbose = false
|
56
|
+
self.debug = false
|
57
|
+
self.no_op = false
|
58
|
+
self.dry_run = false
|
59
|
+
self.lock = nil
|
60
|
+
self.retries = 3
|
61
|
+
self.backoff = 5
|
62
|
+
self.params = HashWithIndifferentAccess.new
|
63
|
+
|
64
|
+
@templates = Hash.new { |h,k| h[k] = {} }
|
65
|
+
|
66
|
+
COMMANDS.each do |command|
|
67
|
+
instance_variable_set("@#{command}", {})
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def load(file)
|
72
|
+
@load_once ||= begin
|
73
|
+
load_yaml_erb_file(file).each_pair do |command, value|
|
74
|
+
if COMMANDS.include?(command)
|
75
|
+
send("#{command}=", value)
|
76
|
+
elsif command == 'paths'
|
77
|
+
load_paths(value)
|
78
|
+
elsif command == 'params'
|
79
|
+
raise ArgumentError, 'params section must only contain key value pairs' unless value.is_a?(Hash)
|
80
|
+
self.params.merge! value
|
81
|
+
end
|
82
|
+
end
|
83
|
+
logger.debug("Loaded configuration #{file}")
|
84
|
+
load_catalog(configuration.postgres.fetch(:schema_files, []) + configuration.hive.fetch(:schema_files, []))
|
85
|
+
true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def load_catalog(paths = [])
|
90
|
+
paths.each do |path|
|
91
|
+
filesystem.glob_sort(path, order: :basename).each do |file|
|
92
|
+
configuration.with_quiet do
|
93
|
+
catalog.load(file)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_s
|
100
|
+
io = StringIO.new
|
101
|
+
rep = {"path" => filesystem.paths}
|
102
|
+
COMMANDS.each do |command|
|
103
|
+
rep[command] = send(command)
|
104
|
+
end
|
105
|
+
PP.pp(rep, io)
|
106
|
+
io.string
|
107
|
+
end
|
108
|
+
|
109
|
+
def debug=(debug)
|
110
|
+
@debug = debug
|
111
|
+
environment.reload_logger!
|
112
|
+
end
|
113
|
+
|
114
|
+
def bind_template(section, template, input_args = {})
|
115
|
+
free_command = load_template(section, template)[:command].split(/\s+/)
|
116
|
+
[].tap do |bind_command|
|
117
|
+
free_command.each do |free_expr|
|
118
|
+
if free_expr =~ /(%.*)/
|
119
|
+
free_param = $1
|
120
|
+
bind_command << free_expr.gsub!(free_param, bind_param(section, template, free_param, input_args))
|
121
|
+
elsif param = free_expr
|
122
|
+
bind_command << param
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def as_options
|
129
|
+
opts = []
|
130
|
+
opts << '--quiet' if quiet
|
131
|
+
opts << '--verbose' if verbose
|
132
|
+
opts << '--debug' if debug
|
133
|
+
opts << '--no_op' if no_op
|
134
|
+
opts << '--dry_run' if dry_run
|
135
|
+
opts
|
136
|
+
end
|
137
|
+
|
138
|
+
def_delegators :filesystem, :add_path, :get_path
|
139
|
+
|
140
|
+
def with_quiet(&block)
|
141
|
+
prev_quiet, self.quiet = quiet, true
|
142
|
+
yield
|
143
|
+
ensure
|
144
|
+
self.quiet = prev_quiet
|
145
|
+
end
|
146
|
+
|
147
|
+
def load_yaml_erb_file(file)
|
148
|
+
t = ERB.new(File.read(file))
|
149
|
+
t.filename = file
|
150
|
+
YAML.load(t.result(binding))
|
151
|
+
end
|
152
|
+
|
153
|
+
class << self
|
154
|
+
def default_config_file=(config_file)
|
155
|
+
@default_config_file = config_file
|
156
|
+
end
|
157
|
+
|
158
|
+
def default_config_file
|
159
|
+
@default_config_file ||= File.join(File.expand_path('../../../', __FILE__), 'config', 'masamune.yml.erb')
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def default_config_file
|
164
|
+
self.class.default_config_file
|
165
|
+
end
|
166
|
+
|
167
|
+
private
|
168
|
+
|
169
|
+
def load_template(section, template_name)
|
170
|
+
@templates[section][template_name] ||= begin
|
171
|
+
raise ArgumentError, "no configuration section #{section}" unless COMMANDS.include?(section.to_s)
|
172
|
+
raise ArgumentError, 'no template_name' unless template_name
|
173
|
+
templates = send(section).fetch(:templates, {}).symbolize_keys!
|
174
|
+
template = templates[template_name.to_sym] or raise ArgumentError, "no template for #{template_name}"
|
175
|
+
template.symbolize_keys!
|
176
|
+
template.has_key?(:command) or raise ArgumentError, "no command for template #{template_name}"
|
177
|
+
template[:default] ||= {}
|
178
|
+
template[:default] = Hash[ template[:default].collect { |key,val| [key.to_sym, val.to_s] } ]
|
179
|
+
template
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def bind_param(section, template, free_param, input_args = {})
|
184
|
+
default = load_template(section, template).fetch(:default, {})
|
185
|
+
param = free_param[/(?<=%).*/].to_sym
|
186
|
+
default.merge(input_args.symbolize_keys || {})[param] or raise ArgumentError, "no param for #{free_param}"
|
187
|
+
end
|
188
|
+
|
189
|
+
def load_paths(paths)
|
190
|
+
paths.each do |value|
|
191
|
+
symbol, path, options = *value.to_a.flatten
|
192
|
+
add_path(symbol, path, options)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|