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 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] definition.json"
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
- definition_file = ARGV.last
18
- definition_json = File.open(definition_file).read
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
- output_base = File.basename(definition_file,".*") + '.png'
21
- output_file = output_path ? File.join(output_path, output_base) : output_base
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, node_attributes=nil)
11
+ def self.draw(definition_json, filename, component_attributes=nil)
11
12
  definition = Definition.parse(definition_json)
12
- definition.apply_node_attributes(node_attributes) if node_attributes
13
+
14
+ if component_attributes
15
+ definition.apply_component_attributes(component_attributes)
16
+ end
13
17
 
14
18
  graph_builder = GraphBuilder.new
15
19
 
@@ -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
@@ -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
- options = {
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
@@ -14,7 +14,7 @@ module Pipely
14
14
  @raw_references.map{|h| Dependency.new(label, h['ref'])}
15
15
  end
16
16
 
17
- def to_json(options={})
17
+ def to_json(options={}, depth=0)
18
18
  if 1 == @raw_references.count
19
19
  @raw_references.first.to_json(options)
20
20
  else
@@ -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
+
@@ -1,3 +1,3 @@
1
1
  module Pipely
2
- VERSION = "0.0.1" unless defined?(::DataPipelineGraphviz::VERSION)
2
+ VERSION = "0.1.0" unless defined?(::DataPipelineGraphviz::VERSION)
3
3
  end
@@ -24,13 +24,14 @@ describe Pipely do
24
24
  described_class.draw(definition_json, filename)
25
25
  end
26
26
 
27
- context 'with node_attributes' do
28
- let(:node_attributes) { stub }
27
+ context 'with component_attributes' do
28
+ let(:component_attributes) { stub }
29
29
 
30
- it 'applies the node_attributes to the definition' do
31
- definition.should_receive(:apply_node_attributes).with(node_attributes)
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, node_attributes)
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.1
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-10-02 00:00:00.000000000 Z
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: &70104078435520 !ruby/object:Gem::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: *70104078435520
24
+ version_requirements: *70261464770320
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70104078435100 !ruby/object:Gem::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: *70104078435100
35
+ version_requirements: *70261464769340
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: virtus
38
- requirement: &70104078434680 !ruby/object:Gem::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: '0'
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: *70104078434680
57
+ version_requirements: *70261464783540
47
58
  - !ruby/object:Gem::Dependency
48
59
  name: rspec
49
- requirement: &70104078434260 !ruby/object:Gem::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: *70104078434260
68
+ version_requirements: *70261464782460
58
69
  - !ruby/object:Gem::Dependency
59
70
  name: cane
60
- requirement: &70104078433840 !ruby/object:Gem::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: *70104078433840
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: