pipely 0.3.0 → 0.4.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 +4 -4
- data/bin/pipely +7 -31
- data/lib/pipely.rb +11 -1
- data/lib/pipely/actions.rb +8 -0
- data/lib/pipely/actions/graph_file_pipeline.rb +39 -0
- data/lib/pipely/actions/graph_live_pipeline.rb +34 -0
- data/lib/pipely/actions/list_live_pipelines.rb +38 -0
- data/lib/pipely/fog_client.rb +28 -5
- data/lib/pipely/live_pipeline.rb +18 -12
- data/lib/pipely/options.rb +47 -0
- data/lib/pipely/s3_writer.rb +33 -0
- data/lib/pipely/version.rb +1 -1
- data/spec/lib/pipely_spec.rb +1 -1
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b9b109c0262cd83b9db819b8464e1ed39a6bef6
|
4
|
+
data.tar.gz: 633b3e1c8bb00323199629919714dec0cce1523f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9576d0aeb1273ebf96021d60cd1f6fb98f9f800cdae3bcd14a1ea6ca5919864ba3896ff99668c340ef0f25b15fe1280997ca7f997ccabe78ddbb4709762c216a
|
7
|
+
data.tar.gz: ef09a07d9ecdb00ea13633455b88f262336756e3e8d51889dd989be384b0ce01192ccbc7c9d474b21d436a2a055c67c1d134bf9ed83786af47add4571b5a2a12
|
data/bin/pipely
CHANGED
@@ -1,40 +1,16 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'pipely'
|
4
|
-
require 'optparse'
|
5
4
|
|
6
|
-
|
7
|
-
verbose = automatic_open = false
|
5
|
+
options = Pipely::Options.parse
|
8
6
|
|
9
|
-
|
10
|
-
|
7
|
+
if options.pipeline_id
|
8
|
+
Pipely::Actions::GraphLivePipeline.new(options).execute
|
11
9
|
|
12
|
-
|
13
|
-
|
14
|
-
pipeline_id = id
|
15
|
-
end
|
10
|
+
elsif options.input_path
|
11
|
+
Pipely::Actions::GraphFilePipeline.new(options).execute
|
16
12
|
|
17
|
-
|
18
|
-
|
19
|
-
input_path = input
|
20
|
-
end
|
13
|
+
else
|
14
|
+
Pipely::Actions::ListLivePipelines.new(options).execute
|
21
15
|
|
22
|
-
opts.on("-o", "--output PATH",
|
23
|
-
"Path to write PNG file(s) of graph visualization(s)") do |output|
|
24
|
-
output_path = output
|
25
|
-
end
|
26
|
-
end.parse!
|
27
|
-
|
28
|
-
if pipeline_id
|
29
|
-
live_pipeline = Pipely::LivePipeline.new(pipeline_id)
|
30
|
-
live_pipeline.print_runs_report
|
31
|
-
live_pipeline.render_graphs
|
32
|
-
|
33
|
-
elsif input_path
|
34
|
-
definition_json = File.open(input_path).read
|
35
|
-
output_base = File.basename(input_path,".*") + '.png'
|
36
|
-
output_file = output_path ? File.join(output_path, output_base) : output_base
|
37
|
-
|
38
|
-
puts "Generating #{output_file}"
|
39
|
-
Pipely.draw(definition_json, output_file)
|
40
16
|
end
|
data/lib/pipely.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
|
+
require 'pipely/options'
|
2
|
+
require 'pipely/actions'
|
1
3
|
require 'pipely/definition'
|
2
4
|
require 'pipely/graph_builder'
|
3
5
|
require 'pipely/live_pipeline'
|
6
|
+
require 'pipely/s3_writer'
|
4
7
|
|
5
8
|
# The top-level module for this gem. It provides the recommended public
|
6
9
|
# interface for using Pipely to visualize and manipulate your Data Pipeline
|
@@ -18,7 +21,14 @@ module Pipely
|
|
18
21
|
graph_builder = GraphBuilder.new
|
19
22
|
|
20
23
|
graph = graph_builder.build(definition.components_for_graph)
|
21
|
-
|
24
|
+
|
25
|
+
if filename.start_with?('s3://')
|
26
|
+
content = graph.output( :png => String )
|
27
|
+
S3Writer.new(filename).write(content)
|
28
|
+
else
|
29
|
+
graph.output( :png => filename )
|
30
|
+
filename
|
31
|
+
end
|
22
32
|
end
|
23
33
|
|
24
34
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Pipely
|
2
|
+
module Actions
|
3
|
+
|
4
|
+
# Graph a pipeline definition from a file.
|
5
|
+
#
|
6
|
+
class GraphFilePipeline
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
puts "Generating #{output_file}"
|
14
|
+
Pipely.draw(definition_json, output_file)
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def definition_json
|
20
|
+
File.open(@options.input_path).read
|
21
|
+
end
|
22
|
+
|
23
|
+
def output_base
|
24
|
+
@output_base ||= File.basename(@options.input_path,".*") + '.png'
|
25
|
+
end
|
26
|
+
|
27
|
+
def output_file
|
28
|
+
@output_file ||= if @options.output_path
|
29
|
+
File.join(@options.output_path, output_base)
|
30
|
+
else
|
31
|
+
output_base
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Pipely
|
2
|
+
module Actions
|
3
|
+
|
4
|
+
# Graph a deployed pipeline with live execution statuses.
|
5
|
+
#
|
6
|
+
class GraphLivePipeline
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
live_pipeline = Pipely::LivePipeline.new(@options.pipeline_id)
|
14
|
+
live_pipeline.print_runs_report
|
15
|
+
|
16
|
+
outfile = if @options.latest_run
|
17
|
+
live_pipeline.render_latest_graph(@options.output_path)
|
18
|
+
else
|
19
|
+
live_pipeline.render_graphs(@options.output_path)
|
20
|
+
end
|
21
|
+
|
22
|
+
if @options.json_output
|
23
|
+
$stdout.puts({ :graph => outfile }.to_json)
|
24
|
+
elsif $stdout.tty?
|
25
|
+
$stdout.puts "Generated #{outfile}"
|
26
|
+
else
|
27
|
+
$stdout.puts outfile
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Pipely
|
2
|
+
module Actions
|
3
|
+
|
4
|
+
# List currently deployed pipelines
|
5
|
+
#
|
6
|
+
class ListLivePipelines
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
@options = options
|
10
|
+
end
|
11
|
+
|
12
|
+
def execute
|
13
|
+
if @options.json_output
|
14
|
+
$stdout.puts pipeline_ids.to_json
|
15
|
+
else
|
16
|
+
$stdout.puts pipeline_ids.map { |pipeline|
|
17
|
+
[ pipeline['name'], pipeline['id'] ].join("\t")
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def pipeline_ids
|
25
|
+
ids = []
|
26
|
+
|
27
|
+
begin
|
28
|
+
result = Fog::AWS[:data_pipeline].list_pipelines
|
29
|
+
ids += result['pipelineIdList']
|
30
|
+
end while (result['hasMoreResults'] && result['marker'])
|
31
|
+
|
32
|
+
ids
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/pipely/fog_client.rb
CHANGED
@@ -33,13 +33,9 @@ module Pipely
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def task_states_by_scheduled_start
|
36
|
-
c = Fog::AWS[:data_pipeline]
|
37
|
-
instances = c.query_objects(pipeline_id, 'INSTANCE')
|
38
|
-
instance_details = c.describe_objects(pipeline_id, instances['ids'])
|
39
|
-
|
40
36
|
task_states_by_scheduled_start = {}
|
41
37
|
|
42
|
-
|
38
|
+
all_instances.each do |pipeline_object|
|
43
39
|
component_id = status = scheduled_start = nil
|
44
40
|
|
45
41
|
pipeline_object['fields'].each do |field|
|
@@ -62,5 +58,32 @@ module Pipely
|
|
62
58
|
task_states_by_scheduled_start
|
63
59
|
end
|
64
60
|
|
61
|
+
private
|
62
|
+
|
63
|
+
def all_instances
|
64
|
+
c = Fog::AWS[:data_pipeline]
|
65
|
+
|
66
|
+
result = {}
|
67
|
+
pipeline_objects = []
|
68
|
+
|
69
|
+
begin
|
70
|
+
if result['marker']
|
71
|
+
marker = JSON.parse(result['marker'])['primary']
|
72
|
+
end
|
73
|
+
|
74
|
+
result = c.query_objects(
|
75
|
+
pipeline_id,
|
76
|
+
'INSTANCE',
|
77
|
+
marker: result['marker']
|
78
|
+
)
|
79
|
+
|
80
|
+
instance_details = c.describe_objects(pipeline_id, result['ids'])
|
81
|
+
pipeline_objects += instance_details['pipelineObjects']
|
82
|
+
|
83
|
+
end while (result['hasMoreResults'] && result['marker'])
|
84
|
+
|
85
|
+
pipeline_objects
|
86
|
+
end
|
87
|
+
|
65
88
|
end
|
66
89
|
end
|
data/lib/pipely/live_pipeline.rb
CHANGED
@@ -26,22 +26,28 @@ module Pipely
|
|
26
26
|
RunsReport.new(@task_states_by_scheduled_start).print
|
27
27
|
end
|
28
28
|
|
29
|
-
def
|
30
|
-
@task_states_by_scheduled_start.
|
31
|
-
|
32
|
-
|
29
|
+
def render_latest_graph(output_path=nil)
|
30
|
+
latest_start = @task_states_by_scheduled_start.keys.max
|
31
|
+
task_states = @task_states_by_scheduled_start[latest_start]
|
32
|
+
render_graph(latest_start, task_states, output_path)
|
33
|
+
end
|
33
34
|
|
34
|
-
|
35
|
+
def render_graphs(output_path=nil)
|
36
|
+
@task_states_by_scheduled_start.map do |start, task_states|
|
37
|
+
render_graph(start, task_states, output_path)
|
38
|
+
end
|
39
|
+
end
|
35
40
|
|
36
|
-
|
37
|
-
$stdout.puts "Generating #{filename}"
|
38
|
-
else
|
39
|
-
$stdout.puts filename
|
40
|
-
end
|
41
|
+
private
|
41
42
|
|
42
|
-
|
43
|
-
|
43
|
+
def render_graph(start, task_states, output_path)
|
44
|
+
utc_time = Time.now.to_i
|
45
|
+
formatted_start = start.gsub(/[:-]/, '').sub('T', '-')
|
46
|
+
|
47
|
+
output_base = "#{@pipeline_id}-#{formatted_start}-#{utc_time}.png"
|
48
|
+
filename = File.join((output_path || 'graphs'), output_base)
|
44
49
|
|
50
|
+
Pipely.draw(@definition_json, filename, task_states)
|
45
51
|
end
|
46
52
|
|
47
53
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Pipely
|
4
|
+
|
5
|
+
# Options for running the CLI
|
6
|
+
#
|
7
|
+
class Options
|
8
|
+
|
9
|
+
attr_accessor :pipeline_id, :input_path, :output_path,
|
10
|
+
:verbose, :automatic_open, :json_output, :latest_run
|
11
|
+
|
12
|
+
def self.parse
|
13
|
+
options = Pipely::Options.new
|
14
|
+
|
15
|
+
OptionParser.new do |opts|
|
16
|
+
opts.banner = "Usage: pipely [options]"
|
17
|
+
|
18
|
+
opts.on("-p", "--pipeline-id PIPELINE_ID",
|
19
|
+
"ID of a live pipeline to visualize with live statuses") do |id|
|
20
|
+
options.pipeline_id = id
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on("-l", "--latest", "Graph only the latest run") do |latest|
|
24
|
+
options.latest_run = latest
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-i", "--input PATH",
|
28
|
+
"Path to a JSON pipeline definition file to visualize") do |input|
|
29
|
+
options.input_path = input
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on("-o", "--output PATH",
|
33
|
+
"Local or S3 path to write Graphviz PNG file(s)") do |output|
|
34
|
+
options.output_path = output
|
35
|
+
end
|
36
|
+
|
37
|
+
opts.on("-j", "--json", "Write STDOUT formatted as JSON") do |json|
|
38
|
+
options.json_output = json
|
39
|
+
end
|
40
|
+
end.parse!
|
41
|
+
|
42
|
+
options
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'fog/storage'
|
2
|
+
|
3
|
+
module Pipely
|
4
|
+
|
5
|
+
# Writes content from a String to an S3 path
|
6
|
+
#
|
7
|
+
class S3Writer
|
8
|
+
|
9
|
+
def initialize(s3_path)
|
10
|
+
uri = URI.parse(s3_path)
|
11
|
+
@host, @path = uri.host, uri.path.gsub(/^\//,'')
|
12
|
+
end
|
13
|
+
|
14
|
+
def directory
|
15
|
+
storage = Fog::Storage.new({ provider: 'AWS' })
|
16
|
+
directory = storage.directories.detect{ |d| d.key == @host }
|
17
|
+
|
18
|
+
directory or raise("Couldn't find S3 bucket '#{@host}'")
|
19
|
+
end
|
20
|
+
|
21
|
+
def write(content)
|
22
|
+
remote_file = directory.files.create({
|
23
|
+
:key => @path,
|
24
|
+
:body => content,
|
25
|
+
:public => true,
|
26
|
+
})
|
27
|
+
|
28
|
+
remote_file.public_url
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/lib/pipely/version.rb
CHANGED
data/spec/lib/pipely_spec.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pipely
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Gillooly
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-03-
|
11
|
+
date: 2014-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-graphviz
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 1.
|
61
|
+
version: 1.21.0
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ~>
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.
|
68
|
+
version: 1.21.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: unf
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -158,6 +158,10 @@ executables:
|
|
158
158
|
extensions: []
|
159
159
|
extra_rdoc_files: []
|
160
160
|
files:
|
161
|
+
- lib/pipely/actions/graph_file_pipeline.rb
|
162
|
+
- lib/pipely/actions/graph_live_pipeline.rb
|
163
|
+
- lib/pipely/actions/list_live_pipelines.rb
|
164
|
+
- lib/pipely/actions.rb
|
161
165
|
- lib/pipely/build/daily_scheduler.rb
|
162
166
|
- lib/pipely/build/definition.rb
|
163
167
|
- lib/pipely/build/environment_config.rb
|
@@ -174,8 +178,10 @@ files:
|
|
174
178
|
- lib/pipely/fog_client.rb
|
175
179
|
- lib/pipely/graph_builder.rb
|
176
180
|
- lib/pipely/live_pipeline.rb
|
181
|
+
- lib/pipely/options.rb
|
177
182
|
- lib/pipely/reference_list.rb
|
178
183
|
- lib/pipely/runs_report.rb
|
184
|
+
- lib/pipely/s3_writer.rb
|
179
185
|
- lib/pipely/shared_examples.rb
|
180
186
|
- lib/pipely/tasks/definition.rb
|
181
187
|
- lib/pipely/tasks/deploy.rb
|