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.
@@ -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
@@ -0,0 +1,4 @@
1
+ module Clustr
2
+ # Version number of the Clustr gem
3
+ VERSION = "0.0.1"
4
+ 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
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Clustr do
4
+
5
+ it "should have a valid version number" do
6
+ expect(Gem::Version.correct?(Clustr::VERSION)).to eq(0)
7
+ end
8
+
9
+ end
@@ -0,0 +1,14 @@
1
+ require 'faker'
2
+
3
+ require 'clustr/node'
4
+
5
+ FactoryGirl.define do
6
+ factory :node, :class => Clustr::Node do
7
+ ignore do
8
+ id Faker::Number.number(10)
9
+ properties :foo => "bar"
10
+ end
11
+
12
+ initialize_with { new id, properties }
13
+ end
14
+ end
@@ -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