grandprix 0.0.4 → 0.0.5
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/.rvmrc +1 -1
- data/README.md +104 -12
- data/grandprix.gemspec +2 -0
- data/lib/grandprix.rb +32 -0
- data/lib/grandprix/graph.rb +3 -1
- data/spec/lib/grandprix/elements_spec.rb +8 -4
- data/spec/lib/grandprix/planner_spec.rb +3 -3
- data/spec/lib/grandprix_extensions_spec.rb +28 -0
- data/spec/spec_helper.rb +15 -0
- data/version +1 -1
- metadata +70 -23
data/.rvmrc
CHANGED
@@ -1 +1 @@
|
|
1
|
-
rvm use 1.
|
1
|
+
rvm use 1.8.7@grandprix --create
|
data/README.md
CHANGED
@@ -63,7 +63,7 @@ and run:
|
|
63
63
|
### Usage
|
64
64
|
An executable called `grandprix` will be installed, and can be called like this:
|
65
65
|
|
66
|
-
$
|
66
|
+
$ grandprix -t topology_file elements_file
|
67
67
|
|
68
68
|
The elements file argument can be omitted and grandprix will read them from
|
69
69
|
the standard in.
|
@@ -92,21 +92,113 @@ The `doc/sample/as_a_library` directory has an example ruby script calling
|
|
92
92
|
grandprix programatically.
|
93
93
|
|
94
94
|
|
95
|
-
##
|
95
|
+
## How it works
|
96
|
+
|
97
|
+
### The Basics
|
98
|
+
|
99
|
+
The basic function of grandprix is to to reorder a list of elements according to
|
100
|
+
a separately specified happens-before relation, a _topology_. The rule is
|
101
|
+
simple: an element **A** will be before another element **B** in the output
|
102
|
+
whenever there is a requirement on the topology that **B** must come after
|
103
|
+
**A**. This requirement may be direct — **B** is in the `after` list for the
|
104
|
+
**A** node in the input topology file — or it may be indirect — **A**'s `after`
|
105
|
+
list contains an element that itself has **B** in its list, and so on. If you
|
106
|
+
know some graph theory you may have recognized the rule as a _topological sort_,
|
107
|
+
and indeed **grandprix** is nothing more than a little topsort engine.
|
108
|
+
|
109
|
+
The `doc/sample/simple` directory contains sample topology and input files, and
|
110
|
+
an example of grandprix's output.
|
96
111
|
|
97
112
|
Grandprix offers a few more conveniences, illustrated on the `doc/sample`
|
98
113
|
directories:
|
99
114
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
115
|
+
### Extra input data
|
116
|
+
Elements can contain extra data that will carry over to the output, such as
|
117
|
+
version or environment information. Just append an equals sign and the information
|
118
|
+
you want to each element. For instance if you want to append version numbers, the
|
119
|
+
elements input file could be something like this:
|
120
|
+
|
121
|
+
```
|
122
|
+
frontend=1.0.0
|
123
|
+
client=2.0.3
|
124
|
+
backend=4.0.0
|
125
|
+
```
|
126
|
+
|
127
|
+
The output would be a permutation of the input lines according to the rules
|
128
|
+
given on the topology. This may be useful to integrate grandprix into your
|
129
|
+
scripting workflow.
|
130
|
+
|
131
|
+
Check out the `elements_with_extra_info` directory for example input and
|
132
|
+
outputs.
|
133
|
+
|
134
|
+
### Topology annotations
|
135
|
+
Adding extra data to the input elements is great for information that changes
|
136
|
+
with each run, but it would be tedious to have to always append long-lasting
|
137
|
+
information about the elements for each input. To address this, grandprix
|
138
|
+
offers a way to annotate the elements on the topology file:
|
139
|
+
|
140
|
+
```
|
141
|
+
frontend:
|
142
|
+
after: [backend, images]
|
143
|
+
annotation: company-frontend-script
|
144
|
+
|
145
|
+
images:
|
146
|
+
annotation:
|
147
|
+
recipe: image-server
|
148
|
+
script: install-images
|
149
|
+
|
150
|
+
backend:
|
151
|
+
after: [db]
|
152
|
+
annotation:
|
153
|
+
- This
|
154
|
+
- And that
|
155
|
+
```
|
156
|
+
|
157
|
+
The above shows the types of annotations that can be added to each node: simple
|
158
|
+
strings, hashes, and arrays of items. These values will carry over to the
|
159
|
+
output for each `grandprix` run:
|
160
|
+
|
161
|
+
```
|
162
|
+
images=2.0.3={"recipe":"image-server","script":"install-images"}
|
163
|
+
backend=4.0.0=["This","And that"]
|
164
|
+
frontend=1.0.0=company-frontend-script
|
165
|
+
```
|
166
|
+
|
167
|
+
In the above outout it can be seen that annotations are output for each element
|
168
|
+
after a second equals sign. Strings are output straight through, arrays and
|
169
|
+
objects are converted to a JSON serialization.
|
170
|
+
|
171
|
+
Sample files are on the `doc/sample/annotated_topology` directory.
|
172
|
+
|
173
|
+
### Alongside Elements
|
174
|
+
Another common occurrence is the need to specify that some elements must always
|
175
|
+
be accompanied by others. For instance, still on the deploy use case, sometimes
|
176
|
+
we want to say that a certain service — say, a front-end server — must always be
|
177
|
+
deployed alongside anoter — it could a static assets server in this example.
|
178
|
+
|
179
|
+
The relationship is specified as an `alongside` entry on a node's topology file:
|
180
|
+
|
181
|
+
```
|
182
|
+
frontend:
|
183
|
+
after: [backend]
|
184
|
+
alongside: [assets, images] # Frontend is always accompanied
|
185
|
+
# by assets and images
|
186
|
+
|
187
|
+
backend:
|
188
|
+
alongside: [external_backend]
|
189
|
+
|
190
|
+
images:
|
191
|
+
after: [client] #images that is an alongside dep of frontend
|
192
|
+
# can declare itself as after client
|
193
|
+
```
|
194
|
+
|
195
|
+
When an element that declares alongside elements is part of the input, the
|
196
|
+
alongside elements are output as well. They inherit their declaring node's order
|
197
|
+
restrictions, but they can declare their own order restrictions which are also
|
198
|
+
obeyed.
|
199
|
+
|
200
|
+
See the `doc/sample/alongside_elements` and `doc/sample/alongside_elements_2`
|
201
|
+
directories.
|
110
202
|
|
111
203
|
|
112
204
|
## Contributing
|
data/grandprix.gemspec
CHANGED
data/lib/grandprix.rb
CHANGED
@@ -3,7 +3,39 @@ module Grandprix
|
|
3
3
|
end
|
4
4
|
|
5
5
|
require 'json'
|
6
|
+
require 'active_support'
|
6
7
|
require 'grandprix/elements'
|
7
8
|
require 'grandprix/graph'
|
8
9
|
require 'grandprix/planner'
|
9
10
|
require 'grandprix/runner'
|
11
|
+
|
12
|
+
if RUBY_VERSION =~ /^1\.8/
|
13
|
+
class Array
|
14
|
+
def flat_map
|
15
|
+
self.reduce([]) {|so_far, current| so_far + yield(current)}
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_ordered_hash
|
19
|
+
ActiveSupport::OrderedHash[self]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class Hash
|
24
|
+
def flat_map
|
25
|
+
self.reduce([]) do |so_far, current|
|
26
|
+
key = current[0]
|
27
|
+
value = current[1]
|
28
|
+
so_far + yield(key, value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
if RUBY_VERSION =~ /^((1\.9)|2)/
|
36
|
+
class Array
|
37
|
+
def to_ordered_hash
|
38
|
+
Hash[self]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/grandprix/graph.rb
CHANGED
@@ -31,13 +31,13 @@ describe Grandprix::Elements do
|
|
31
31
|
|
32
32
|
describe :alongside do
|
33
33
|
it "should store the extra names" do
|
34
|
-
(make(["a","c"]).alongside "a"
|
34
|
+
(make(["a","c"]).alongside [["a", ["aa"]], ["c", ["cc"]]]).should == make(["a", "c", "aa", "cc"])
|
35
35
|
end
|
36
36
|
|
37
37
|
it "should copy extra data from the stored names to the names defined alongside" do
|
38
38
|
elements = make ["first=0.1", "second=0.2", "third"]
|
39
|
-
res = elements.alongside "first"
|
40
|
-
res.strings.should
|
39
|
+
res = elements.alongside [["first", ["one"]], ["second", ["two"]], ["third", ["three"]]]
|
40
|
+
res.strings.should =~ ["first=0.1", "second=0.2", "third", "one=0.1", "two=0.2", "three"]
|
41
41
|
end
|
42
42
|
|
43
43
|
it "REGRESSION: it should not add unrelated names" do
|
@@ -59,7 +59,11 @@ describe Grandprix::Elements do
|
|
59
59
|
it "should add extra object information to the appropriate names" do
|
60
60
|
elements = make ["first=1"]
|
61
61
|
res = elements.annotate "first" => {:a => "some string", "b" => ["other"]}
|
62
|
-
res.strings.should ==
|
62
|
+
res.strings.size.should == 1
|
63
|
+
|
64
|
+
line = res.strings.first
|
65
|
+
line.should match %r|first=1={.*}|
|
66
|
+
JSON.parse(res.strings.first.split(/=/).last).should include({"a" => "some string", "b" => ["other"]})
|
63
67
|
end
|
64
68
|
|
65
69
|
it "should format properly when the element has no prior extra information but is annotated" do
|
@@ -19,7 +19,7 @@ describe Grandprix::Planner do
|
|
19
19
|
|
20
20
|
elements = ["frontend", "db", "client"]
|
21
21
|
|
22
|
-
graph.should_receive(:sort).with([
|
22
|
+
graph.should_receive(:sort).with(in_any_order [
|
23
23
|
["backend", "frontend"],
|
24
24
|
["db", "backend"],
|
25
25
|
["mq", "backend"],
|
@@ -117,7 +117,7 @@ describe Grandprix::Planner do
|
|
117
117
|
|
118
118
|
elements = ["frontend", "db", "client"]
|
119
119
|
|
120
|
-
graph.should_receive(:sort).with([
|
120
|
+
graph.should_receive(:sort).with(in_any_order [
|
121
121
|
["backend", "frontend"],
|
122
122
|
["db", "backend"],
|
123
123
|
["mq", "backend"],
|
@@ -144,7 +144,7 @@ describe Grandprix::Planner do
|
|
144
144
|
|
145
145
|
elements = ["frontend=1.0.0", "backend=2.0.0", "db"]
|
146
146
|
|
147
|
-
graph.should_receive(:sort).with([
|
147
|
+
graph.should_receive(:sort).with(in_any_order [
|
148
148
|
["backend", "frontend"],
|
149
149
|
["db", "backend"],
|
150
150
|
]).and_return(["db", "backend", "frontend"])
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe "Extension" do
|
5
|
+
describe "Array.flat_map" do
|
6
|
+
it { [1,2,3].flat_map{|x| [x,x]}.should == [1,1,2,2,3,3]}
|
7
|
+
it { [1,2,3,4].flat_map{|x| x % 2 == 0 ? [x] : [] }.should == [2,4]}
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "Hash.flat_map" do
|
11
|
+
it do
|
12
|
+
h = {:a => 10, :b => 20}
|
13
|
+
res = h.flat_map do |k, v|
|
14
|
+
[k, v, v]
|
15
|
+
end
|
16
|
+
res.should == [:a, 10, 10, :b, 20, 20]
|
17
|
+
end
|
18
|
+
|
19
|
+
it do
|
20
|
+
h = [[:a,1], [:b,2], [:c,3], [:d,4]].to_ordered_hash
|
21
|
+
|
22
|
+
res = h.flat_map do |k, v|
|
23
|
+
v % 2 == 1 ? [k] : []
|
24
|
+
end
|
25
|
+
res.should == [:a, :c]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -86,3 +86,18 @@ RSpec::Matchers.define :beOrderedHaving do |*initial_segment|
|
|
86
86
|
end
|
87
87
|
end
|
88
88
|
end
|
89
|
+
|
90
|
+
class InAnyOrderArgumentMatcher
|
91
|
+
def initialize(expected)
|
92
|
+
@expected = expected
|
93
|
+
end
|
94
|
+
|
95
|
+
def ==(value)
|
96
|
+
(value.is_a?(Array) && @expected.is_a?(Array) &&
|
97
|
+
(@expected - value) == [] && (value - @expected) == [] )
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def in_any_order(expected)
|
102
|
+
InAnyOrderArgumentMatcher.new(expected)
|
103
|
+
end
|
data/version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.5
|
metadata
CHANGED
@@ -1,24 +1,60 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: grandprix
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Rafael de F. Ferreira
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
17
|
+
|
18
|
+
date: 2013-02-12 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: json
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: activesupport
|
36
|
+
prerelease: false
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
none: false
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
14
48
|
description: Deploy assistant.
|
15
|
-
email:
|
49
|
+
email:
|
16
50
|
- public@rafaelferreira.net
|
17
|
-
executables:
|
51
|
+
executables:
|
18
52
|
- grandprix
|
19
53
|
extensions: []
|
54
|
+
|
20
55
|
extra_rdoc_files: []
|
21
|
-
|
56
|
+
|
57
|
+
files:
|
22
58
|
- .gitignore
|
23
59
|
- .rspec
|
24
60
|
- .rvmrc
|
@@ -56,37 +92,48 @@ files:
|
|
56
92
|
- spec/lib/grandprix/graph_spec.rb
|
57
93
|
- spec/lib/grandprix/planner_spec.rb
|
58
94
|
- spec/lib/grandprix/runner_spec.rb
|
95
|
+
- spec/lib/grandprix_extensions_spec.rb
|
59
96
|
- spec/matchers_spec.rb
|
60
97
|
- spec/spec_helper.rb
|
61
98
|
- version
|
62
|
-
homepage:
|
99
|
+
homepage: ""
|
63
100
|
licenses: []
|
101
|
+
|
64
102
|
post_install_message:
|
65
103
|
rdoc_options: []
|
66
|
-
|
104
|
+
|
105
|
+
require_paths:
|
67
106
|
- lib
|
68
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
108
|
none: false
|
70
|
-
requirements:
|
71
|
-
- -
|
72
|
-
- !ruby/object:Gem::Version
|
73
|
-
|
74
|
-
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
hash: 3
|
113
|
+
segments:
|
114
|
+
- 0
|
115
|
+
version: "0"
|
116
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
117
|
none: false
|
76
|
-
requirements:
|
77
|
-
- -
|
78
|
-
- !ruby/object:Gem::Version
|
79
|
-
|
118
|
+
requirements:
|
119
|
+
- - ">="
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
hash: 3
|
122
|
+
segments:
|
123
|
+
- 0
|
124
|
+
version: "0"
|
80
125
|
requirements: []
|
126
|
+
|
81
127
|
rubyforge_project:
|
82
|
-
rubygems_version: 1.8.
|
128
|
+
rubygems_version: 1.8.25
|
83
129
|
signing_key:
|
84
130
|
specification_version: 3
|
85
131
|
summary: Deploy assistant
|
86
|
-
test_files:
|
132
|
+
test_files:
|
87
133
|
- spec/lib/grandprix/elements_spec.rb
|
88
134
|
- spec/lib/grandprix/graph_spec.rb
|
89
135
|
- spec/lib/grandprix/planner_spec.rb
|
90
136
|
- spec/lib/grandprix/runner_spec.rb
|
137
|
+
- spec/lib/grandprix_extensions_spec.rb
|
91
138
|
- spec/matchers_spec.rb
|
92
139
|
- spec/spec_helper.rb
|