pipely 0.0.1 → 0.1.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.
- data/README.md +37 -0
- data/bin/pipely +28 -8
- data/lib/pipely.rb +6 -2
- data/lib/pipely/component.rb +2 -2
- data/lib/pipely/fog_client.rb +66 -0
- data/lib/pipely/graph_builder.rb +24 -12
- data/lib/pipely/live_pipeline.rb +42 -0
- data/lib/pipely/reference_list.rb +1 -1
- data/lib/pipely/runs_report.rb +20 -0
- data/lib/pipely/version.rb +1 -1
- data/spec/lib/pipely_spec.rb +6 -5
- metadata +36 -16
data/README.md
CHANGED
@@ -1,4 +1,41 @@
|
|
1
1
|
pipely
|
2
2
|
======
|
3
|
+
[](http://badge.fury.io/rb/pipely) [](https://travis-ci.org/swipely/pipely) [](https://codeclimate.com/repos/524b941156b1025b6c08a96a/feed)
|
3
4
|
|
4
5
|
Visualize pipeline definitions for AWS Data Pipeline
|
6
|
+
|
7
|
+
"AWS Data Pipeline is a web service that you can use to automate the movement and transformation of data. With AWS Data Pipeline, you can define data-driven workflows, so that tasks can be dependent on the successful completion of previous tasks."
|
8
|
+
|
9
|
+
http://docs.aws.amazon.com/datapipeline/latest/DeveloperGuide/what-is-datapipeline.html
|
10
|
+
|
11
|
+
|
12
|
+
## Install
|
13
|
+
|
14
|
+
(First install [GraphViz](http://www.graphviz.org) if it is not already installed.)
|
15
|
+
|
16
|
+
Into Gemfile from rubygems.org:
|
17
|
+
|
18
|
+
gem 'pipely'
|
19
|
+
|
20
|
+
Into environment gems from rubygems.org:
|
21
|
+
|
22
|
+
gem install pipely
|
23
|
+
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
(If you used the Gemfile install, prefix the below commands with `bundle exec`.)
|
28
|
+
|
29
|
+
### Command-line Interface
|
30
|
+
|
31
|
+
To render a JSON pipeline definition as a PNG graph visualization:
|
32
|
+
|
33
|
+
pipely definition.json
|
34
|
+
|
35
|
+
To specify the output path for PNG files:
|
36
|
+
|
37
|
+
pipely -o path/to/graph/pngs definition.json
|
38
|
+
|
39
|
+
### Rake Tasks
|
40
|
+
|
41
|
+
Coming soon.
|
data/bin/pipely
CHANGED
@@ -3,10 +3,21 @@
|
|
3
3
|
require 'pipely'
|
4
4
|
require 'optparse'
|
5
5
|
|
6
|
-
output_path = nil
|
6
|
+
pipeline_id = input_path = output_path = nil
|
7
|
+
verbose = automatic_open = false
|
7
8
|
|
8
9
|
OptionParser.new do |opts|
|
9
|
-
opts.banner = "Usage: pipely [options]
|
10
|
+
opts.banner = "Usage: pipely [options]"
|
11
|
+
|
12
|
+
opts.on("-p", "--pipeline-id PIPELINE_ID",
|
13
|
+
"ID of a live pipeline to visualize with live execution statuses") do |id|
|
14
|
+
pipeline_id = id
|
15
|
+
end
|
16
|
+
|
17
|
+
opts.on("-i", "--input PATH",
|
18
|
+
"Path to a JSON pipeline definition file to visualize") do |input|
|
19
|
+
input_path = input
|
20
|
+
end
|
10
21
|
|
11
22
|
opts.on("-o", "--output PATH",
|
12
23
|
"Path to write PNG file(s) of graph visualization(s)") do |output|
|
@@ -14,11 +25,20 @@ OptionParser.new do |opts|
|
|
14
25
|
end
|
15
26
|
end.parse!
|
16
27
|
|
17
|
-
|
18
|
-
|
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
|
+
puts "open: #{automatic_open}"
|
34
|
+
puts "verbose: #{verbose}"
|
19
35
|
|
20
|
-
|
21
|
-
|
36
|
+
else
|
37
|
+
input_path ||= ARGV.last
|
38
|
+
definition_json = File.open(input_path).read
|
39
|
+
output_base = File.basename(definition_file,".*") + '.png'
|
40
|
+
output_file = output_path ? File.join(output_path, output_base) : output_base
|
22
41
|
|
23
|
-
puts "Generating #{output_file}"
|
24
|
-
Pipely.draw(definition_json, output_file)
|
42
|
+
puts "Generating #{output_file}"
|
43
|
+
Pipely.draw(definition_json, output_file)
|
44
|
+
end
|
data/lib/pipely.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'pipely/definition'
|
2
2
|
require 'pipely/graph_builder'
|
3
|
+
require 'pipely/live_pipeline'
|
3
4
|
|
4
5
|
# The top-level module for this gem. It provides the recommended public
|
5
6
|
# interface for using Pipely to visualize and manipulate your Data Pipeline
|
@@ -7,9 +8,12 @@ require 'pipely/graph_builder'
|
|
7
8
|
#
|
8
9
|
module Pipely
|
9
10
|
|
10
|
-
def self.draw(definition_json, filename,
|
11
|
+
def self.draw(definition_json, filename, component_attributes=nil)
|
11
12
|
definition = Definition.parse(definition_json)
|
12
|
-
|
13
|
+
|
14
|
+
if component_attributes
|
15
|
+
definition.apply_component_attributes(component_attributes)
|
16
|
+
end
|
13
17
|
|
14
18
|
graph_builder = GraphBuilder.new
|
15
19
|
|
data/lib/pipely/component.rb
CHANGED
@@ -27,7 +27,7 @@ module Pipely
|
|
27
27
|
'FAILED' => 'orangered',
|
28
28
|
}
|
29
29
|
|
30
|
-
include Virtus
|
30
|
+
include Virtus.model
|
31
31
|
|
32
32
|
attribute :id, String
|
33
33
|
attribute :type, String
|
@@ -84,7 +84,7 @@ module Pipely
|
|
84
84
|
deps
|
85
85
|
end
|
86
86
|
|
87
|
-
def to_json(options={})
|
87
|
+
def to_json(options={}, depth=0)
|
88
88
|
h = @original_args
|
89
89
|
|
90
90
|
REFERENCE_KEYS.each do |key|
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'fog'
|
2
|
+
|
3
|
+
module Pipely
|
4
|
+
|
5
|
+
# Uses Fog to communicate with the AWS Data Pipeline service
|
6
|
+
class FogClient < Struct.new(:pipeline_id)
|
7
|
+
|
8
|
+
def definition
|
9
|
+
objects = Fog::AWS[:data_pipeline].get_pipeline_definition(pipeline_id)
|
10
|
+
|
11
|
+
flattened_objects = []
|
12
|
+
|
13
|
+
objects['pipelineObjects'].each do |object|
|
14
|
+
h = {
|
15
|
+
id: object['id'],
|
16
|
+
name: object['name'],
|
17
|
+
}
|
18
|
+
|
19
|
+
object['fields'].each do |field|
|
20
|
+
k = field['key']
|
21
|
+
if field['refValue']
|
22
|
+
h[k] ||= []
|
23
|
+
h[k] << { ref: field['refValue'] }
|
24
|
+
else
|
25
|
+
h[k] = field['stringValue']
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
flattened_objects << h
|
30
|
+
end
|
31
|
+
|
32
|
+
{ objects: flattened_objects }.to_json
|
33
|
+
end
|
34
|
+
|
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
|
+
task_states_by_scheduled_start = {}
|
41
|
+
|
42
|
+
instance_details['pipelineObjects'].each do |pipeline_object|
|
43
|
+
component_id = status = scheduled_start = nil
|
44
|
+
|
45
|
+
pipeline_object['fields'].each do |field|
|
46
|
+
case field['key']
|
47
|
+
when '@componentParent'
|
48
|
+
component_id = field['refValue']
|
49
|
+
when '@status'
|
50
|
+
status = field['stringValue']
|
51
|
+
when '@scheduledStartTime'
|
52
|
+
scheduled_start = field['stringValue']
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
task_states_by_scheduled_start[scheduled_start] ||= {}
|
57
|
+
task_states_by_scheduled_start[scheduled_start][component_id] = {
|
58
|
+
execution_state: status
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
task_states_by_scheduled_start
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
data/lib/pipely/graph_builder.rb
CHANGED
@@ -29,22 +29,34 @@ module Pipely
|
|
29
29
|
def add_edges(components)
|
30
30
|
components.each do |component|
|
31
31
|
component.dependencies.each do |dependency|
|
32
|
-
|
33
|
-
:label => dependency.label,
|
34
|
-
:color => dependency.color,
|
35
|
-
}
|
36
|
-
|
37
|
-
options[:dir] = 'back' if ('input' == dependency.label)
|
38
|
-
|
39
|
-
@graph.add_edges(
|
40
|
-
component.id,
|
41
|
-
dependency.target_id,
|
42
|
-
options
|
43
|
-
)
|
32
|
+
add_edge(component, dependency)
|
44
33
|
end
|
45
34
|
end
|
46
35
|
end
|
47
36
|
|
37
|
+
def add_edge(component, dependency)
|
38
|
+
options = {
|
39
|
+
:label => dependency.label,
|
40
|
+
:color => dependency.color,
|
41
|
+
}
|
42
|
+
|
43
|
+
options[:dir] = 'back' if ('input' == dependency.label)
|
44
|
+
|
45
|
+
if 'output' == dependency.label
|
46
|
+
@graph.add_edges(
|
47
|
+
dependency.target_id,
|
48
|
+
component.id,
|
49
|
+
options
|
50
|
+
)
|
51
|
+
else
|
52
|
+
@graph.add_edges(
|
53
|
+
component.id,
|
54
|
+
dependency.target_id,
|
55
|
+
options
|
56
|
+
)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
48
60
|
end
|
49
61
|
|
50
62
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'pipely/fog_client'
|
2
|
+
require 'pipely/runs_report'
|
3
|
+
|
4
|
+
module Pipely
|
5
|
+
|
6
|
+
# Represent a pipeline that has been deployed to AWS DataPipeline
|
7
|
+
class LivePipeline
|
8
|
+
|
9
|
+
def initialize(pipeline_id)
|
10
|
+
@pipeline_id = pipeline_id
|
11
|
+
|
12
|
+
client = FogClient.new(pipeline_id)
|
13
|
+
@definition_json = client.definition
|
14
|
+
@task_states_by_scheduled_start = client.task_states_by_scheduled_start
|
15
|
+
|
16
|
+
unless @definition_json
|
17
|
+
raise "No definition found for #{client.pipeline_id}"
|
18
|
+
end
|
19
|
+
|
20
|
+
if @task_states_by_scheduled_start.empty?
|
21
|
+
raise "No runs found for #{client.pipeline_id}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def print_runs_report
|
26
|
+
RunsReport.new(@task_states_by_scheduled_start).print
|
27
|
+
end
|
28
|
+
|
29
|
+
def render_graphs
|
30
|
+
@task_states_by_scheduled_start.each do |start, task_states|
|
31
|
+
utc_time = Time.now.to_i
|
32
|
+
formatted_start = start.gsub(/[:-]/, '').sub('T', '-')
|
33
|
+
|
34
|
+
filename = "graphs/#{@pipeline_id}-#{formatted_start}-#{utc_time}.png"
|
35
|
+
puts "Generating #{filename}"
|
36
|
+
Pipely.draw(@definition_json, filename, task_states)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Pipely
|
2
|
+
|
3
|
+
# Prints a CLI report of the execution status of a live pipeline
|
4
|
+
class RunsReport < Struct.new(:task_states_by_scheduled_start)
|
5
|
+
|
6
|
+
def print
|
7
|
+
task_states_by_scheduled_start.each do |scheduled_start, task_states|
|
8
|
+
task_states.to_a.sort_by(&:first).each do |task_name, attributes|
|
9
|
+
current_state = attributes[:execution_state]
|
10
|
+
|
11
|
+
puts task_name.ljust(55) +
|
12
|
+
"scheduled_start: #{scheduled_start}\t\t" +
|
13
|
+
"current_state: #{current_state}"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
data/lib/pipely/version.rb
CHANGED
data/spec/lib/pipely_spec.rb
CHANGED
@@ -24,13 +24,14 @@ describe Pipely do
|
|
24
24
|
described_class.draw(definition_json, filename)
|
25
25
|
end
|
26
26
|
|
27
|
-
context 'with
|
28
|
-
let(:
|
27
|
+
context 'with component_attributes' do
|
28
|
+
let(:component_attributes) { stub }
|
29
29
|
|
30
|
-
it 'applies the
|
31
|
-
definition.should_receive(:
|
30
|
+
it 'applies the component_attributes to the definition' do
|
31
|
+
definition.should_receive(:apply_component_attributes).
|
32
|
+
with(component_attributes)
|
32
33
|
|
33
|
-
described_class.draw(definition_json, filename,
|
34
|
+
described_class.draw(definition_json, filename, component_attributes)
|
34
35
|
end
|
35
36
|
end
|
36
37
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pipely
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-11-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ruby-graphviz
|
16
|
-
requirement: &
|
16
|
+
requirement: &70261464770320 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70261464770320
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rake
|
27
|
-
requirement: &
|
27
|
+
requirement: &70261464769340 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,21 +32,32 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70261464769340
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: virtus
|
38
|
-
requirement: &
|
38
|
+
requirement: &70261464784920 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
|
-
- -
|
41
|
+
- - ~>
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version:
|
43
|
+
version: 1.0.0
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70261464784920
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: fog
|
49
|
+
requirement: &70261464783540 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.18.0
|
44
55
|
type: :runtime
|
45
56
|
prerelease: false
|
46
|
-
version_requirements: *
|
57
|
+
version_requirements: *70261464783540
|
47
58
|
- !ruby/object:Gem::Dependency
|
48
59
|
name: rspec
|
49
|
-
requirement: &
|
60
|
+
requirement: &70261464782460 !ruby/object:Gem::Requirement
|
50
61
|
none: false
|
51
62
|
requirements:
|
52
63
|
- - ! '>='
|
@@ -54,10 +65,10 @@ dependencies:
|
|
54
65
|
version: '0'
|
55
66
|
type: :development
|
56
67
|
prerelease: false
|
57
|
-
version_requirements: *
|
68
|
+
version_requirements: *70261464782460
|
58
69
|
- !ruby/object:Gem::Dependency
|
59
70
|
name: cane
|
60
|
-
requirement: &
|
71
|
+
requirement: &70261464781200 !ruby/object:Gem::Requirement
|
61
72
|
none: false
|
62
73
|
requirements:
|
63
74
|
- - ! '>='
|
@@ -65,7 +76,7 @@ dependencies:
|
|
65
76
|
version: '0'
|
66
77
|
type: :development
|
67
78
|
prerelease: false
|
68
|
-
version_requirements: *
|
79
|
+
version_requirements: *70261464781200
|
69
80
|
description:
|
70
81
|
email:
|
71
82
|
- matt@swipely.com
|
@@ -77,8 +88,11 @@ files:
|
|
77
88
|
- lib/pipely/component.rb
|
78
89
|
- lib/pipely/definition.rb
|
79
90
|
- lib/pipely/dependency.rb
|
91
|
+
- lib/pipely/fog_client.rb
|
80
92
|
- lib/pipely/graph_builder.rb
|
93
|
+
- lib/pipely/live_pipeline.rb
|
81
94
|
- lib/pipely/reference_list.rb
|
95
|
+
- lib/pipely/runs_report.rb
|
82
96
|
- lib/pipely/version.rb
|
83
97
|
- lib/pipely.rb
|
84
98
|
- Rakefile
|
@@ -92,7 +106,8 @@ files:
|
|
92
106
|
- !binary |-
|
93
107
|
YmluL3BpcGVseQ==
|
94
108
|
homepage: http://github.com/swipely/pipely
|
95
|
-
licenses:
|
109
|
+
licenses:
|
110
|
+
- MIT
|
96
111
|
post_install_message:
|
97
112
|
rdoc_options: []
|
98
113
|
require_paths:
|
@@ -103,12 +118,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
103
118
|
- - ! '>='
|
104
119
|
- !ruby/object:Gem::Version
|
105
120
|
version: '0'
|
121
|
+
segments:
|
122
|
+
- 0
|
123
|
+
hash: -4454073296924687218
|
106
124
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
125
|
none: false
|
108
126
|
requirements:
|
109
127
|
- - ! '>='
|
110
128
|
- !ruby/object:Gem::Version
|
111
129
|
version: '0'
|
130
|
+
segments:
|
131
|
+
- 0
|
132
|
+
hash: -4454073296924687218
|
112
133
|
requirements: []
|
113
134
|
rubyforge_project:
|
114
135
|
rubygems_version: 1.8.11
|
@@ -122,4 +143,3 @@ test_files:
|
|
122
143
|
- spec/lib/pipely/graph_builder_spec.rb
|
123
144
|
- spec/lib/pipely/reference_list_spec.rb
|
124
145
|
- spec/lib/pipely_spec.rb
|
125
|
-
has_rdoc:
|