clustr 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZWRlNjk2ZGY1YTM1OTA2YjI4MWJiMWM3YjI1Y2VlMzlhZmIxNjEzMw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
OWRhYzAwODI2ZGQxZjU1MjljZGEwYzEwMzlkNDI2YjUzNmY4YTE1Yg==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NzRiZDkzYjhhNzMwNWYxYzNlYjdiYWRiNTZlMGM5ZTFiMzhhYTU1M2FkNTdh
|
10
|
+
ZDQyMzc1NDA3ODFkOGMyZTc1YmM1MGIxZTVhZTZmYTI4YmM4YjY1OGQzN2Zj
|
11
|
+
Yzg4MTNlMTE4M2ZhZDYxNzMwNDVhN2ZmNmMxMDc1ZGMzZmZkZDE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MjJmOGEzYWY5ZDQ3OTAwYjIyZDBkYTRiMDY4NTRhNWQwNWI1NDc4MDE2MzE4
|
14
|
+
OGY3NDllNTRiMjU3NTBhZjc0MjAxMjUxMDdlZDM5NTE5Yzc0ZTcwNGJmMTE0
|
15
|
+
ZWQwYjhlZWY4NDk1NDMwMDY1YzI5ZjFjM2YzZDc4ZTYyZGVhZTA=
|
data/.gitignore
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
clustr
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.0.0-p353
|
data/.travis.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 1.9.3
|
4
|
+
- 2.1.0
|
5
|
+
- 2.0.0-p353
|
6
|
+
- ruby-head
|
7
|
+
notifications:
|
8
|
+
flowdock:
|
9
|
+
secure: TI1C6kvdJrMD7m4ImSvf9cU8+1UBrpzlcsfwu2Q+1MYyEz2eBP819eBVF6g/HQM1Z8KU/7ShCtDA2NJQZXn9HfSbT4pebaAcr7FnyEDuWB/38F6/kZdzG71G10SkaRi4kIp4CbaYovaYrO7simeVoYdtK5RWgxGWcnGML8ubL8Y=
|
10
|
+
before_deploy:
|
11
|
+
- yard doc
|
12
|
+
deploy:
|
13
|
+
- provider: s3
|
14
|
+
access_key_id: AKIAIQXC7KNC6CPV3RMA
|
15
|
+
bucket: clustr.koala.im
|
16
|
+
skip_cleanup: true
|
17
|
+
local-dir: doc
|
18
|
+
edge: true
|
19
|
+
region: eu-west-1
|
20
|
+
endpoint: s3-eu-west-1.amazonaws.com
|
21
|
+
on:
|
22
|
+
branch: master
|
23
|
+
secret_access_key:
|
24
|
+
secure: dNZcJd61mj56+nCkOHpmKt+hcwB3KLpvViRLYUKR4G5isgCfkPtoe8PeYx9y/BiNuP0dXGu/Ij7Wgl/BuPGdp6VeNecPZAjWhgsTSuHp7WFlax4WPBe3qdtYWkrws1Gy4vN5da4zN3sY8qWxnu+51t1Im5nibz/fxHnY9THV9q8=
|
25
|
+
- provider: rubygems
|
26
|
+
on:
|
27
|
+
branch: master
|
28
|
+
api_key:
|
29
|
+
secure: Kkwrzg+2dJrrShwVceco8EEP8Kc9BsKvDWIK6pFrdalZRY4sDG7BC7phPOmQPW40Qvl9f3qaZFjpohN0/QLDlF48Zg2AUBGuKfWpQ7V+dTL7hd6ZewFEs/5vyoofEf7BkSE9NFrsuOymEtHH+MJjqY+aIFg4YrNRcoEvCFuEZRk=
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 davidkelley
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Clustr
|
2
|
+
|
3
|
+
[![Coverage Status](https://coveralls.io/repos/davidkelley/clustr/badge.png)](https://coveralls.io/r/davidkelley/clustr) [![Build Status](https://travis-ci.org/davidkelley/clustr.svg?branch=master)](https://travis-ci.org/davidkelley/clustr) [![Code Climate](https://codeclimate.com/github/davidkelley/clustr.png)](https://codeclimate.com/github/davidkelley/clustr) [![Dependency Status](https://gemnasium.com/davidkelley/clustr.svg)](https://gemnasium.com/davidkelley/clustr)
|
4
|
+
|
5
|
+
TODO: Write a gem description
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'clustr'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install clustr
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( http://github.com/<my-github-username>/clustr/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/clustr
ADDED
data/clustr.gemspec
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'clustr/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "clustr"
|
8
|
+
spec.version = Clustr::VERSION
|
9
|
+
spec.authors = ["davidkelley"]
|
10
|
+
spec.email = ["david.james.kelley@gmail.com"]
|
11
|
+
spec.summary = %q{Ruby gem that enables EC2 instances to cluster together.}
|
12
|
+
spec.description = %q{This gem uses Amazon SimpleDB and other adapters, to track namespaced attributes for instances that need to cluster together. It provides a simple CLI which can be used in conjunction with other shell commands. See examples/ for example Cloudformation Templates.}
|
13
|
+
spec.homepage = "https://github.com/davidkelley/clustr"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "thor"
|
22
|
+
spec.add_dependency "aws-sdk"
|
23
|
+
spec.add_dependency "terminal-table"
|
24
|
+
spec.add_dependency "nokogiri", ">= 1.4.4"
|
25
|
+
|
26
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
27
|
+
spec.add_development_dependency "rspec", "~> 2.6"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
spec.add_development_dependency "yard"
|
30
|
+
spec.add_development_dependency "faker"
|
31
|
+
spec.add_development_dependency "redcarpet"
|
32
|
+
spec.add_development_dependency "cucumber"
|
33
|
+
spec.add_development_dependency "aruba"
|
34
|
+
spec.add_development_dependency "fakefs"
|
35
|
+
spec.add_development_dependency "factory_girl"
|
36
|
+
spec.add_development_dependency "codeclimate-test-reporter"
|
37
|
+
spec.add_development_dependency 'coveralls'
|
38
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Clustr
|
2
|
+
module Adapter
|
3
|
+
# Base class for all implemented Adapters.
|
4
|
+
class Base
|
5
|
+
|
6
|
+
attr_reader :key
|
7
|
+
|
8
|
+
# Initializes a new Adapter class and associates
|
9
|
+
# the unique key for this cluster to the @key
|
10
|
+
# variable. This key is required for all Adapters.
|
11
|
+
def initialize(options)
|
12
|
+
@key = options["key"]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Clustr
|
2
|
+
module Adapter
|
3
|
+
# Implements the SimpleDB adapter, which controls a cluster
|
4
|
+
# and its members through the usage of SimpleDB as a centralized,
|
5
|
+
# high-availability data store.
|
6
|
+
#
|
7
|
+
# SimpleDB is capable of storing unique node identifiers along
|
8
|
+
# with additional information that is to be associated with them.
|
9
|
+
class SimpleDB < Clustr::Adapter::Base
|
10
|
+
|
11
|
+
# Initializes a new SimpleDB Adapter class. If the domain
|
12
|
+
# that has been defined in the @key attribute does not exist,
|
13
|
+
# it will create it.
|
14
|
+
def initialize
|
15
|
+
super
|
16
|
+
sdb.domains.create(@key) unless domain.exists?
|
17
|
+
end
|
18
|
+
|
19
|
+
# Add a new node to the SimpleDB domain.
|
20
|
+
#
|
21
|
+
# @param [string] id Unique ID of the node to add.
|
22
|
+
# @param [integer] created timestamp of the node.
|
23
|
+
# @param [hash] properties additional properties to add for the node.
|
24
|
+
#
|
25
|
+
# @return [boolean] True if node was added successfully, false otherwise.
|
26
|
+
def add(id, created, properties = {})
|
27
|
+
item = domain.items[id]
|
28
|
+
properties.each { |k, v| item.attributes[k] = v }
|
29
|
+
item.attributes["joined"] = created
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return an array of the unique identifiers for each node
|
34
|
+
# in the cluster.
|
35
|
+
#
|
36
|
+
# @return [array] an array containing the unique ids for each node.
|
37
|
+
def members
|
38
|
+
domain.items.collect(&:name)
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def domain
|
44
|
+
sdb.domains[@key]
|
45
|
+
end
|
46
|
+
|
47
|
+
def sdb
|
48
|
+
@sdb ||= AWS::SimpleDB.new
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "clustr/adapter/base"
|
2
|
+
|
3
|
+
require "clustr/adapter/simpledb"
|
4
|
+
|
5
|
+
module Clustr
|
6
|
+
# Contains different adapters for controlling the
|
7
|
+
# underlying logic for determining current members
|
8
|
+
# and adding new members to a cluster through the
|
9
|
+
# usage of different technologies,
|
10
|
+
# such as {Clustr::Adapter::SimpleDB}.
|
11
|
+
module Adapter
|
12
|
+
|
13
|
+
# Instantiates and returns a new Adapter class
|
14
|
+
# which has been determined from the options hash
|
15
|
+
# that has been passed in.
|
16
|
+
#
|
17
|
+
# @example Initializing a new adapter
|
18
|
+
# sdb = Clustr::Adapter.from_options({
|
19
|
+
# "adapter" => "SimpleDB",
|
20
|
+
# "key" => "foobar"
|
21
|
+
# })
|
22
|
+
#
|
23
|
+
# @note The named adapter must equal the defined class name.
|
24
|
+
#
|
25
|
+
# @param [hash] options to use, must contain an "adapter" key.
|
26
|
+
#
|
27
|
+
# @return [object] An instantiated adapter object.
|
28
|
+
def self.from_options(options)
|
29
|
+
name = options["adapter"]
|
30
|
+
self[name].new(options)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Return the constantized adapter class.
|
34
|
+
#
|
35
|
+
# @param [string] adapter to return.
|
36
|
+
#
|
37
|
+
# @return [class] The uninitialized Adapter, nil if undefined.
|
38
|
+
def self.[](adapter)
|
39
|
+
self.const_get adapter
|
40
|
+
end
|
41
|
+
|
42
|
+
# Determines if the named adapter that has been passed in
|
43
|
+
# is a defined and implemented Adapter.
|
44
|
+
#
|
45
|
+
# @param [string] adapter name
|
46
|
+
#
|
47
|
+
# @return [boolean] True if the Adapter is implemented, false otherwise.
|
48
|
+
def self.exists?(adapter)
|
49
|
+
self.const_defined? adapter
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/lib/clustr/cli.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'clustr'
|
3
|
+
require 'terminal-table'
|
4
|
+
|
5
|
+
require 'clustr/log'
|
6
|
+
|
7
|
+
module Clustr
|
8
|
+
# Provides a Command Line Interface for the Launcher gem. See the method definitions
|
9
|
+
# for more information on its usage.
|
10
|
+
#
|
11
|
+
# @example Checking the version
|
12
|
+
# launcher version
|
13
|
+
class CLI < Thor
|
14
|
+
|
15
|
+
package_name "Clustr"
|
16
|
+
|
17
|
+
class_option :access_key_id, :type => :string
|
18
|
+
class_option :secret_access_key, :type => :string
|
19
|
+
class_option :region, :type => :string, :default => "eu-west-1"
|
20
|
+
|
21
|
+
def initialize(*args)
|
22
|
+
super
|
23
|
+
# Commit command line options to general config
|
24
|
+
Clustr::Config(options)
|
25
|
+
|
26
|
+
# Set AWS Configuration if it has been passed in
|
27
|
+
config = ["access_key_id", "secret_access_key", "region"]
|
28
|
+
::AWS.config(options.select { |k| config.include?(k) })
|
29
|
+
describe_aws_configuration
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "version", "Displays the current version number of Clustr."
|
33
|
+
# Displays the current version of the installed Clustr gem on the command line.
|
34
|
+
# For more help on this command, use `clustr help version` from the command line.
|
35
|
+
def version
|
36
|
+
Log.info Clustr::VERSION
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "join", "Insert this node into the cluster, along with any additional attributes."
|
40
|
+
method_option :clusters, :type => :array, :desc => "List of clusters this node should join."
|
41
|
+
method_option :id, :type => :string, :desc => "ID of the node joining the cluster.", :required => true
|
42
|
+
method_option :attributes, :type => :hash, :desc => "Additional attributes to store for this node", :default => {}
|
43
|
+
# Joins the node that this command is ran on, to a new, or previously existing cluster.
|
44
|
+
# For more help on this command, use `clustr help join` from the command line.
|
45
|
+
def join
|
46
|
+
node = Node.new Config[:id], Config[:attributes]
|
47
|
+
(Config[:clusters] || clusters.keys).each do |cluster|
|
48
|
+
if clusters.include?(cluster)
|
49
|
+
if clusters[cluster].add(node)
|
50
|
+
Log.ok "Joined cluster #{cluster}"
|
51
|
+
end
|
52
|
+
else
|
53
|
+
raise ArgumentError.new "Cluster #{cluster} not configured."
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "list", "Output only the unique identifiers for each node in a list."
|
59
|
+
method_option :cluster, :type => :string, :desc => "The cluster to output nodes from.", :required => true
|
60
|
+
method_option :delimiter, :type => :string, :desc => "The delimiter to separate each node identifier with, defaults to space", :default => " "
|
61
|
+
def list
|
62
|
+
puts clusters[Config[:cluster]].members.join(Config[:delimiter])
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
def clusters
|
68
|
+
@clusters ||= ClusterCollection.new(Config::Clusters.configuration)
|
69
|
+
end
|
70
|
+
|
71
|
+
def aws_configuration
|
72
|
+
AWS.config.credentials
|
73
|
+
end
|
74
|
+
|
75
|
+
def aws_configured?
|
76
|
+
begin
|
77
|
+
aws_configuration
|
78
|
+
rescue
|
79
|
+
false
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def describe_aws_configuration
|
84
|
+
if aws_configured?
|
85
|
+
config = aws_configuration
|
86
|
+
Log.info "AWS Region #{config[:region]}"
|
87
|
+
Log.info "AWS Access Key #{config[:access_key_id]}"
|
88
|
+
else
|
89
|
+
Log.warn "No AWS config detected."
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Clustr
|
2
|
+
# Represents a cluster that has been configured and initialized
|
3
|
+
# from the options passed into it. Each Cluster has an adapter,
|
4
|
+
# which is reponsible for determining the current members
|
5
|
+
# of a cluster, and adding new nodes.
|
6
|
+
class Cluster
|
7
|
+
|
8
|
+
attr_accessor :name
|
9
|
+
|
10
|
+
# Initializes a new Cluster from the options that
|
11
|
+
# are passed into it. Information about the adapter
|
12
|
+
# that this cluster should use is required.
|
13
|
+
#
|
14
|
+
# @example Instantiating a new Cluster
|
15
|
+
# cluster = Clustr::Cluster.new "my_cluster", {
|
16
|
+
# "adapter" => "SimpleDB",
|
17
|
+
# "key" => "simpledb-domain-edfvw2031"
|
18
|
+
# }
|
19
|
+
#
|
20
|
+
# @param [string] name of the cluster
|
21
|
+
# @param [hash] options to initialize the cluster with.
|
22
|
+
def initialize(name, options)
|
23
|
+
@name = name
|
24
|
+
@adapter = Clustr::Adapter.from_options(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return an array of the unique identifiers for each
|
28
|
+
# node within the cluster.
|
29
|
+
#
|
30
|
+
# @note This method only returns the ID's for each node,
|
31
|
+
# not any accompanying attributes for the node.
|
32
|
+
#
|
33
|
+
# @return [array] Cluster members, node ID's
|
34
|
+
def members
|
35
|
+
@adapter.members
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns an array of {Node} objects. These nodes
|
39
|
+
# have been identified as members of this Cluster.
|
40
|
+
#
|
41
|
+
# @return [array] Array of {Node} objects.
|
42
|
+
def nodes
|
43
|
+
raise NotImplementedError.new
|
44
|
+
end
|
45
|
+
|
46
|
+
# Add a node to this cluster. Note that the node
|
47
|
+
# must have a compatible ID.
|
48
|
+
#
|
49
|
+
# @param [Node] node to add to this cluster.
|
50
|
+
#
|
51
|
+
# @return [boolean] True if successful, false otherwise
|
52
|
+
def add(node)
|
53
|
+
@adapter.add node.id, node.created, node.properties
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Clustr
|
2
|
+
# Represents a collection of the current {Cluster}
|
3
|
+
# configurations. This class can handle initializing
|
4
|
+
# clusters that have been defined through
|
5
|
+
# {Clustr::Config::Clusters}.
|
6
|
+
#
|
7
|
+
# @note The key for each cluster is its name, ie. "rabbitmq"
|
8
|
+
class ClusterCollection < Hash
|
9
|
+
|
10
|
+
# Initializes a collection of {Cluster} instances,
|
11
|
+
# by looping through the configurations that have been
|
12
|
+
# passed in.
|
13
|
+
#
|
14
|
+
# @example Initializing a ClusterCollection
|
15
|
+
# config = {
|
16
|
+
# "rabbitmq" => {
|
17
|
+
# "adapter" => "SimpleDB",
|
18
|
+
# "key" => "my-simpledb-domain"
|
19
|
+
# }
|
20
|
+
# }
|
21
|
+
# clusters = ClusterCollection.new(config)
|
22
|
+
def initialize(configurations = {})
|
23
|
+
configurations.each do |name, config|
|
24
|
+
self[name] = Clustr::Cluster.new(name, config)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Clustr
|
2
|
+
module Config
|
3
|
+
# Handles determining the configuration for clusters that are
|
4
|
+
# present on the machine. Note that configurations should
|
5
|
+
# be defined in "~/.clustr".
|
6
|
+
class Clusters < Hash
|
7
|
+
|
8
|
+
# Determines if there are any configured clusters.
|
9
|
+
#
|
10
|
+
# @return [boolean] True if one or more configuration
|
11
|
+
# are present, false otherwise.
|
12
|
+
def self.configured?
|
13
|
+
!configuration.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the configuration for the clusters that have been
|
17
|
+
# defined inside the "~/.clustr/" folder. Note that the
|
18
|
+
# filename for each configuration, becomes the determined
|
19
|
+
# name for that cluster.
|
20
|
+
#
|
21
|
+
# @return [hash] A hash of the clusters and their accompanying
|
22
|
+
# configuration (including the adapter and key).
|
23
|
+
def self.configuration
|
24
|
+
clusters ||= self.new
|
25
|
+
|
26
|
+
config_files.each do |file|
|
27
|
+
cluster = Pathname.new(file).basename.to_s
|
28
|
+
clusters[cluster] = {}
|
29
|
+
File.open(file, 'r') do |fh|
|
30
|
+
fh.each_line do |line|
|
31
|
+
key, val = line.strip.split(%r(\s*=\s*))
|
32
|
+
clusters[cluster][key] = val if key && val
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
clusters
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
@@path = File.expand_path("~/.clustr")
|
43
|
+
|
44
|
+
def self.config_files
|
45
|
+
if Dir.exists?(@@path)
|
46
|
+
entries = Dir.entries(@@path)
|
47
|
+
entries = entries - [".", ".."]
|
48
|
+
entries.collect { |e| [@@path,e].join("/") }
|
49
|
+
else
|
50
|
+
[]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'clustr/config/clusters'
|
2
|
+
|
3
|
+
module Clustr
|
4
|
+
|
5
|
+
# Adds configuration values to the current static instance of Clustr.
|
6
|
+
#
|
7
|
+
# @overload self.Config(config={})
|
8
|
+
# @param [Hash] config of keys and values to add.
|
9
|
+
def self.Config(config={})
|
10
|
+
config.each do |key, value|
|
11
|
+
Clustr::Config[key] = value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Controls all configuration settings for the Clustr gem.
|
16
|
+
# @note This module should be accessed from within a static context.
|
17
|
+
module Config
|
18
|
+
|
19
|
+
# Retrieves values for the provided keys.
|
20
|
+
# @note Keys not present within the configuration will be omitted from the returned hash.
|
21
|
+
#
|
22
|
+
# @example Guided Usage
|
23
|
+
# Clustr::Config.select(:a, :b, :c, :d)
|
24
|
+
#
|
25
|
+
# @param [Splat] args of the key value pairs to retrieve.
|
26
|
+
# @return [Hash] a hash of the keys present within configuration.
|
27
|
+
def self.select(*args)
|
28
|
+
@@_.select { |key| args.include?(key) }
|
29
|
+
end
|
30
|
+
|
31
|
+
# Accesses {Clustr::Config} as an array of key-based values.
|
32
|
+
#
|
33
|
+
# @example Retrieve a singular key using a symbol
|
34
|
+
# Clustr::Config[:a]
|
35
|
+
# @example Retrieve a singular key using a string
|
36
|
+
# Clustr::Config["a"]
|
37
|
+
#
|
38
|
+
# @param [Symbol] key of the value to retrieve.
|
39
|
+
# @return [Mixed, Nil] Will return the value of the key or nil.
|
40
|
+
def self.[](key)
|
41
|
+
@@_[key.to_sym]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Sets or modifies a key and value within the current configuration state.
|
45
|
+
#
|
46
|
+
# @example Usage
|
47
|
+
# Clustr::Config[:a] = "hello world!"
|
48
|
+
#
|
49
|
+
# @param [Symbol] key of the value to set.
|
50
|
+
# @param [Mixed] value of the element.
|
51
|
+
# @return [Mixed] the new value of the element.
|
52
|
+
def self.[]=(key,value)
|
53
|
+
@@_[key.to_sym] = value
|
54
|
+
end
|
55
|
+
|
56
|
+
# Removes a key from the current configuration state.
|
57
|
+
#
|
58
|
+
# @example Usage
|
59
|
+
# Clustr::Config.delete!(:one, :or, :more, :keys)
|
60
|
+
#
|
61
|
+
# @param [Symbol] keys of the values to delete.
|
62
|
+
# @return [Hash] the values that have been deleted
|
63
|
+
def self.delete!(*keys)
|
64
|
+
keys.each { |k| @@_.delete(k.to_sym) }
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
@@_ = {}
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
data/lib/clustr/log.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require "logger"
|
2
|
+
|
3
|
+
module Clustr
|
4
|
+
# The Log class provides a static interface to output text to STDOUT.
|
5
|
+
# It provides helpful messages types such as debug, error and ok. On
|
6
|
+
# most command line interfaces, {Clustr::Log} will also output text
|
7
|
+
# from various messages types in an appropriate color.
|
8
|
+
#
|
9
|
+
# @example Sending a log message
|
10
|
+
# Clustr::Log.fatal "My fatal error is", exception.class.name
|
11
|
+
class Log
|
12
|
+
|
13
|
+
%w(debug error info warn fatal ok).each do |type|
|
14
|
+
define_singleton_method(type) do |*msgs|
|
15
|
+
@@log.send(type, *msgs.join(' '))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Print an OK log message using a concatenation of various strings
|
20
|
+
# that have been passed in.
|
21
|
+
#
|
22
|
+
# @example Sending an OK Message
|
23
|
+
# Clustr::Log.ok "Hello", username # => "Hello David"
|
24
|
+
def self.ok(*msgs)
|
25
|
+
print format_msg("OK", *msgs.join(' ')), :ok
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def self.format_msg(severity, msg, datetime=Time.now)
|
31
|
+
"[#{datetime}] [#{severity}]: #{msg}\n"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
@@log = Logger.new(STDOUT)
|
37
|
+
|
38
|
+
@@log.formatter = proc { |severity, datetime, progname, msg|
|
39
|
+
format_msg severity, msg, datetime
|
40
|
+
}
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|