clustr 0.0.1
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 +15 -0
- data/.gitignore +17 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +29 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +5 -0
- data/bin/clustr +3 -0
- data/clustr.gemspec +38 -0
- data/lib/clustr/adapter/base.rb +18 -0
- data/lib/clustr/adapter/simpledb.rb +53 -0
- data/lib/clustr/adapter.rb +53 -0
- data/lib/clustr/cli.rb +94 -0
- data/lib/clustr/cluster.rb +57 -0
- data/lib/clustr/cluster_collection.rb +29 -0
- data/lib/clustr/config/clusters.rb +56 -0
- data/lib/clustr/config.rb +71 -0
- data/lib/clustr/log.rb +43 -0
- data/lib/clustr/node.rb +33 -0
- data/lib/clustr/node_collection.rb +8 -0
- data/lib/clustr/version.rb +4 -0
- data/lib/clustr.rb +69 -0
- data/spec/clustr/cluster_collection_spec.rb +41 -0
- data/spec/clustr/cluster_spec.rb +25 -0
- data/spec/clustr/config/clusters_spec.rb +109 -0
- data/spec/clustr/config_spec.rb +40 -0
- data/spec/clustr/log_spec.rb +27 -0
- data/spec/clustr/node_collection_spec.rb +32 -0
- data/spec/clustr/node_spec.rb +42 -0
- data/spec/clustr/version_spec.rb +9 -0
- data/spec/factories/node.rb +14 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/message_shared.rb +54 -0
- metadata +318 -0
data/lib/clustr/node.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
module Clustr
|
2
|
+
# Represents a single node within a cluster. Note that
|
3
|
+
# the physical entity represented by this node may occur
|
4
|
+
# several times in other {Cluster}.
|
5
|
+
class Node
|
6
|
+
|
7
|
+
attr_reader :id, :properties, :created
|
8
|
+
|
9
|
+
# Creates a new {Node}, taking a unique identifier
|
10
|
+
# for the node within the {Cluster} and a set of optional,
|
11
|
+
# properties to further describe or provide additional
|
12
|
+
# information about the node.
|
13
|
+
#
|
14
|
+
# @param [mixed] id of the node within the cluster.
|
15
|
+
# @param [hash] properties to set for this node.
|
16
|
+
def initialize(id, properties = {})
|
17
|
+
@id = id
|
18
|
+
@created = Time.now.to_i
|
19
|
+
@properties = properties
|
20
|
+
end
|
21
|
+
|
22
|
+
# Retrieves a specific key from the property set that
|
23
|
+
# accompanies this node.
|
24
|
+
#
|
25
|
+
# @param [string] key of the property value to retrieve.
|
26
|
+
#
|
27
|
+
# @return [mixed] the corresponding value for the property key.
|
28
|
+
def [](key)
|
29
|
+
properties[key.to_s]
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module Clustr
|
2
|
+
# Represents a collection of {Node} instances. This class
|
3
|
+
# extends Hash to provide methods to access the collection.
|
4
|
+
# Each key within the collection represents a Nodes unique
|
5
|
+
# identifier within the cluster it is among.
|
6
|
+
class NodeCollection < Hash
|
7
|
+
end
|
8
|
+
end
|
data/lib/clustr.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require "aws-sdk"
|
2
|
+
|
3
|
+
require "clustr/version"
|
4
|
+
require "clustr/config"
|
5
|
+
|
6
|
+
require "clustr/cluster_collection"
|
7
|
+
require "clustr/cluster"
|
8
|
+
|
9
|
+
require "clustr/node_collection"
|
10
|
+
require "clustr/node"
|
11
|
+
|
12
|
+
require "clustr/adapter"
|
13
|
+
|
14
|
+
# This gem uses Amazon SimpleDB and other adapters, to track
|
15
|
+
# namespaced attributes for instances that need to cluster together.
|
16
|
+
# It provides a simple CLI which can be used in conjunction with other
|
17
|
+
# shell commands. See examples/ for example Cloudformation Templates.
|
18
|
+
module Clustr
|
19
|
+
|
20
|
+
# Provides classes with the ability to store messages for consumption
|
21
|
+
# elsewhere within the application. Typically via the CLI.
|
22
|
+
#
|
23
|
+
# @example Including and using the {Clustr::Message} module.
|
24
|
+
# class Foo
|
25
|
+
# include Clustr::Message
|
26
|
+
# def initialize
|
27
|
+
# message "The class has been initialized!", :type => :ok
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
module Message
|
31
|
+
|
32
|
+
attr :messages
|
33
|
+
attr_reader :message_proc
|
34
|
+
|
35
|
+
# Binds a message handler to proc a block when a message
|
36
|
+
# is added to the messages array.
|
37
|
+
#
|
38
|
+
# @param [Proc] message_proc the block to call when a message is added.
|
39
|
+
def message_handler(&message_proc)
|
40
|
+
@message_proc = message_proc
|
41
|
+
end
|
42
|
+
|
43
|
+
protected
|
44
|
+
|
45
|
+
# Places a new message into the messages array and optionally
|
46
|
+
# calls a block passed into the action handler.
|
47
|
+
#
|
48
|
+
# @param [String] msg to add to the messages array.
|
49
|
+
def message(msg, options={})
|
50
|
+
(@messages ||= []) << options.merge({:message => msg})
|
51
|
+
proc_with_message(messages.last, &@message_proc) if @message_proc
|
52
|
+
end
|
53
|
+
|
54
|
+
# Retrieve the underlying messages array or pass them
|
55
|
+
# through a block.
|
56
|
+
def messages(&block)
|
57
|
+
@messages.each { |m| proc_with_message(m, &block) } if block
|
58
|
+
return @messages
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def proc_with_message(msg, &block)
|
64
|
+
block.call(msg[:message], msg)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'clustr/cluster'
|
4
|
+
require 'clustr/cluster_collection'
|
5
|
+
|
6
|
+
describe Clustr::ClusterCollection do
|
7
|
+
|
8
|
+
before { @cluster_collection = Clustr::ClusterCollection.new }
|
9
|
+
|
10
|
+
subject { @cluster_collection }
|
11
|
+
|
12
|
+
it { should respond_to(:[]) }
|
13
|
+
it { should be_a_kind_of(Hash) }
|
14
|
+
it { should be_empty }
|
15
|
+
|
16
|
+
describe "when cluster configurations are present" do
|
17
|
+
let(:configuration) {
|
18
|
+
{
|
19
|
+
"rabbitmq" => {
|
20
|
+
"key" => "b" * 10
|
21
|
+
}
|
22
|
+
}
|
23
|
+
}
|
24
|
+
|
25
|
+
before {
|
26
|
+
Clustr::Adapter.stub(:from_options) { nil }
|
27
|
+
@cluster_collection = Clustr::ClusterCollection.new configuration
|
28
|
+
}
|
29
|
+
|
30
|
+
subject { @cluster_collection }
|
31
|
+
|
32
|
+
it { should_not be_empty }
|
33
|
+
it { should include("rabbitmq") }
|
34
|
+
|
35
|
+
it "should return a cluster" do
|
36
|
+
expect(@cluster_collection["rabbitmq"]).to be_a(Clustr::Cluster)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'clustr/cluster'
|
4
|
+
|
5
|
+
describe Clustr::Cluster do
|
6
|
+
|
7
|
+
let(:cluster_name) { "foobar" }
|
8
|
+
|
9
|
+
before do
|
10
|
+
Clustr::Adapter.stub(:from_options) { nil }
|
11
|
+
@cluster = Clustr::Cluster.new(cluster_name, {})
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { @cluster }
|
15
|
+
|
16
|
+
it { should respond_to(:name) }
|
17
|
+
it { should respond_to(:members) }
|
18
|
+
it { should respond_to(:add) }
|
19
|
+
it { should respond_to(:nodes) }
|
20
|
+
|
21
|
+
it "should return the name of the cluster" do
|
22
|
+
expect(@cluster.name).to eq(cluster_name)
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require "clustr/config"
|
4
|
+
require "clustr/config/clusters"
|
5
|
+
|
6
|
+
describe Clustr::Config::Clusters do
|
7
|
+
|
8
|
+
include FakeFS::SpecHelpers
|
9
|
+
|
10
|
+
before { @clusters = Clustr::Config::Clusters }
|
11
|
+
|
12
|
+
subject { @clusters }
|
13
|
+
|
14
|
+
it { should respond_to(:configured?) }
|
15
|
+
it { should respond_to(:configuration) }
|
16
|
+
|
17
|
+
describe "return value of configuration" do
|
18
|
+
|
19
|
+
describe "when no cluster configuration files are set" do
|
20
|
+
|
21
|
+
before {
|
22
|
+
Clustr::Config::Clusters.stub(:config_files) { [] }
|
23
|
+
@configuration = Clustr::Config::Clusters.configuration
|
24
|
+
}
|
25
|
+
|
26
|
+
subject { @configuration }
|
27
|
+
|
28
|
+
it { should be_a_kind_of(Hash) }
|
29
|
+
|
30
|
+
it { should be_empty }
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "when cluster configuration is set" do
|
35
|
+
|
36
|
+
let(:service_name) { "a_service" }
|
37
|
+
|
38
|
+
let(:file_path) { "#{service_name}" }
|
39
|
+
|
40
|
+
let(:config_contents) {
|
41
|
+
{
|
42
|
+
"a_key" => "a_value",
|
43
|
+
"another_key" => "another_value"
|
44
|
+
}
|
45
|
+
}
|
46
|
+
|
47
|
+
before do
|
48
|
+
File.open(file_path, "wb") do |f|
|
49
|
+
config_contents.each do |k, v|
|
50
|
+
f.write "#{k}=#{v}\n"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
Clustr::Config::Clusters.stub(:config_files) { [file_path] }
|
55
|
+
@configuration = Clustr::Config::Clusters.configuration
|
56
|
+
end
|
57
|
+
|
58
|
+
subject { @configuration }
|
59
|
+
|
60
|
+
it { should be_a_kind_of(Hash) }
|
61
|
+
|
62
|
+
it { should include(service_name) }
|
63
|
+
|
64
|
+
it { should_not be_empty }
|
65
|
+
|
66
|
+
it "should equal the file contents" do
|
67
|
+
expect(@configuration[service_name]).to eq(config_contents)
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "when two cluster configuration files are set" do
|
71
|
+
|
72
|
+
let(:second_service_name) { "a_second_service" }
|
73
|
+
|
74
|
+
let(:second_file_path) { "#{second_service_name}" }
|
75
|
+
|
76
|
+
let(:second_config_contents) {
|
77
|
+
{
|
78
|
+
"foo" => "bar"
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
before do
|
83
|
+
File.open(second_file_path, "wb") do |f|
|
84
|
+
second_config_contents.each do |k, v|
|
85
|
+
f.write "#{k}=#{v}\n"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
Clustr::Config::Clusters.stub(:config_files) { [file_path, second_file_path] }
|
90
|
+
@configuration = Clustr::Config::Clusters.configuration
|
91
|
+
end
|
92
|
+
|
93
|
+
subject { @configuration }
|
94
|
+
|
95
|
+
it { should be_a_kind_of(Hash) }
|
96
|
+
|
97
|
+
it { should include(service_name, second_service_name) }
|
98
|
+
|
99
|
+
it "should equal the second file contents" do
|
100
|
+
expect(@configuration[second_service_name]).to eq(second_config_contents)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Clustr::Config do
|
4
|
+
|
5
|
+
let(:test_data) { { :a => "a", :b => "b"} }
|
6
|
+
|
7
|
+
before(:each) { Clustr::Config(test_data) }
|
8
|
+
|
9
|
+
it "should be present" do
|
10
|
+
expect(subject).to_not be_nil
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should accept new values" do
|
14
|
+
subject[:new_value] = true
|
15
|
+
expect(subject[:new_value]).to equal(true)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should modify existing values" do
|
19
|
+
subject[:a] = "a new value"
|
20
|
+
expect(subject[:a]).to_not equal(test_data[:a])
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should retrieve a single value" do
|
24
|
+
expect(subject[:a]).to equal(test_data[:a])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should retrieve sets of values" do
|
28
|
+
subject.select(*test_data.keys).should == test_data
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return nothing for a non-existing value" do
|
32
|
+
expect(subject.select(:non_existing_key)).to be_empty
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should delete a key" do
|
36
|
+
Clustr::Config.delete!(:a)
|
37
|
+
expect(subject.select(:a, :b)).to eq({:b => "b"})
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'clustr/log'
|
4
|
+
|
5
|
+
describe Clustr::Log do
|
6
|
+
|
7
|
+
it { Clustr::Log.should respond_to(:debug) }
|
8
|
+
it { Clustr::Log.should respond_to(:error) }
|
9
|
+
it { Clustr::Log.should respond_to(:info) }
|
10
|
+
it { Clustr::Log.should respond_to(:warn) }
|
11
|
+
it { Clustr::Log.should respond_to(:fatal) }
|
12
|
+
it { Clustr::Log.should respond_to(:ok) }
|
13
|
+
|
14
|
+
describe "return value of #format_msg" do
|
15
|
+
|
16
|
+
let(:severity) { "ERROR" }
|
17
|
+
let(:msg) { "This is a test message" }
|
18
|
+
let(:time) { Time.now }
|
19
|
+
|
20
|
+
it "should return a correctly formatted message" do
|
21
|
+
message = Clustr::Log.format_msg(severity, msg, time)
|
22
|
+
expected_message = "[#{time}] [#{severity}]: #{msg}\n"
|
23
|
+
expect(message).to eq(expected_message)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'clustr/node_collection'
|
4
|
+
|
5
|
+
describe Clustr::NodeCollection do
|
6
|
+
|
7
|
+
before { @node_collection = Clustr::NodeCollection.new }
|
8
|
+
|
9
|
+
subject { @node_collection }
|
10
|
+
|
11
|
+
it { should respond_to(:[]) }
|
12
|
+
it { should be_a_kind_of(Hash) }
|
13
|
+
it { should be_empty }
|
14
|
+
|
15
|
+
describe "when a node is present" do
|
16
|
+
|
17
|
+
let(:node) { build(:node) }
|
18
|
+
|
19
|
+
let(:node_id) { 123 }
|
20
|
+
|
21
|
+
before { @node_collection[node_id] = node }
|
22
|
+
|
23
|
+
it { should_not be_empty }
|
24
|
+
it { should include(node_id) }
|
25
|
+
|
26
|
+
it "should return the node" do
|
27
|
+
expect(@node_collection[node_id]).to eq node
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'clustr/node'
|
4
|
+
|
5
|
+
describe Clustr::Node do
|
6
|
+
|
7
|
+
let(:node_id) { 123 }
|
8
|
+
|
9
|
+
let(:node_properties) { { "foo" => "bar" } }
|
10
|
+
|
11
|
+
let(:node_created) { Time.now.to_i }
|
12
|
+
|
13
|
+
before { @node = Clustr::Node.new(node_id, node_properties) }
|
14
|
+
|
15
|
+
subject { @node }
|
16
|
+
|
17
|
+
it { should respond_to(:id) }
|
18
|
+
it { should respond_to(:properties) }
|
19
|
+
it { should respond_to(:created) }
|
20
|
+
it { should respond_to(:[]) }
|
21
|
+
|
22
|
+
it "should have the correct id" do
|
23
|
+
expect(@node.id).to eq(node_id)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should have the correct properties" do
|
27
|
+
expect(@node.properties).to eq(node_properties)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should have the correct create time" do
|
31
|
+
expect(@node.created).to eq(node_created)
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "return value of #[](key)" do
|
35
|
+
|
36
|
+
it "should return the correct property" do
|
37
|
+
expect(@node[node_properties.keys.first]).to eq(node_properties.values.first)
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
4
|
+
require 'coveralls'
|
5
|
+
Coveralls.wear!
|
6
|
+
|
7
|
+
require 'pp'
|
8
|
+
require 'aws-sdk'
|
9
|
+
require 'fakefs/spec_helpers'
|
10
|
+
require 'factory_girl'
|
11
|
+
require 'clustr'
|
12
|
+
|
13
|
+
AWS.stub!
|
14
|
+
|
15
|
+
Dir["./spec/support/**/*.rb"].sort.each {|f| require f}
|
16
|
+
|
17
|
+
FactoryGirl.find_definitions
|
18
|
+
|
19
|
+
RSpec.configure do |config|
|
20
|
+
config.include FactoryGirl::Syntax::Methods
|
21
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
shared_examples "a class that stores messages" do
|
2
|
+
|
3
|
+
let(:test_message) { "This is a test message" }
|
4
|
+
let(:test_options) { { :type => :ok } }
|
5
|
+
|
6
|
+
before do
|
7
|
+
subject.message(test_message, test_options)
|
8
|
+
end
|
9
|
+
|
10
|
+
it { should respond_to(:messages) }
|
11
|
+
it { should respond_to(:message) }
|
12
|
+
|
13
|
+
describe "return value of #messages" do
|
14
|
+
it "should return an array" do
|
15
|
+
expect(subject.messages).to be_a(Array)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should have a positive length" do
|
19
|
+
expect(subject.messages.length).to be > 0
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should be an array of hashes" do
|
23
|
+
expect(subject.messages.first).to be_a(Hash)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should contain an array of hashed messages" do
|
27
|
+
expect(subject.messages.first[:message]).to eq(test_message)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "when #messages is called with a block" do
|
32
|
+
|
33
|
+
it "should call the block" do
|
34
|
+
expect { |b| subject.messages(&b) }.to yield_control.once
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should yield the message elements" do
|
38
|
+
expected_options = test_options.merge(:message => test_message)
|
39
|
+
expect { |b| subject.messages(&b) }.to yield_with_args(test_message, expected_options)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "when a class has a message handler bound" do
|
45
|
+
it "should call the block when a message is added" do
|
46
|
+
expect { |b|
|
47
|
+
subject.message_handler &b
|
48
|
+
subject.message(test_message, test_options)
|
49
|
+
subject.message(test_message, test_options)
|
50
|
+
}.to yield_control.twice
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|