pipely 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/pipely.png)](http://badge.fury.io/rb/pipely) [![Build Status](https://travis-ci.org/swipely/pipely.png?branch=master)](https://travis-ci.org/swipely/pipely) [![Code Climate](https://codeclimate.com/repos/524b941156b1025b6c08a96a/badges/c0ad2bbec610f1d0f0f7/gpa.png)](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:
|